From f46caa4cc835406d49218989cce166e3f2bbd249 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Fri, 15 May 2026 04:21:42 +0200 Subject: [PATCH 1/2] refactor(aarch64,riscv64): unify kernel image detection This unifies the kernel image detection for both aarch64 and riscv64. riscv64 was incorrectly using the length of the module reg as the size of the kernel image, which was always 0, which is fixed by using the approach taken by the aarch64 code to parse the ELF header. On the other hand, aarch64 now falls back to linux,initrd-start and linux,initrd-end for detecting the kernel image thanks to these changes, which is necessary for cloud-hypervisor at the moment. --- Cargo.toml | 1 + src/arch/aarch64/mod.rs | 45 ++----------------------------------- src/arch/riscv64/mod.rs | 41 +++------------------------------ src/fdt_ext.rs | 50 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ 5 files changed, 58 insertions(+), 81 deletions(-) create mode 100644 src/fdt_ext.rs diff --git a/Cargo.toml b/Cargo.toml index 536c0b34..912be584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ volatile = { version = "0.6", features = ["derive"] } [target.'cfg(target_arch = "riscv64")'.dependencies] fdt = "0.1" +goblin = { version = "0.10", default-features = false, features = ["elf64"] } naked-function = "0.1" sbi-rt = "0.0.3" diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 775cd609..4dcfe7a6 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -12,7 +12,6 @@ use aarch64_cpu::asm::barrier::{self, NSH, SY, dmb, dsb, isb}; use aarch64_cpu::registers::{ReadWriteable, SCTLR_EL1, TTBR0_EL1, TTBR1_EL1, Writeable}; use align_address::Align; use fdt::Fdt; -use goblin::elf::header::header64::{EI_DATA, ELFDATA2LSB, ELFMAG, Header, SELFMAG}; use hermit_entry::Entry; use hermit_entry::boot_info::{BootInfo, HardwareInfo, PlatformInfo, RawBootInfo, SerialPortBase}; use hermit_entry::elf::LoadedKernel; @@ -20,6 +19,7 @@ use log::info; use crate::BootInfoExt; use crate::arch::paging::*; +use crate::fdt_ext::FdtExt; use crate::os::CONSOLE; unsafe extern "C" { @@ -57,49 +57,8 @@ pub fn find_kernel() -> &'static [u8] { Fdt::from_ptr(ptr::with_exposed_provenance(DEVICE_TREE as usize)) .expect(".fdt file has invalid header") }; - let module_start = fdt - .find_node("/chosen") - .unwrap() - .children() - .find(|node| node.name.starts_with("module@")) - .map(|node| { - let value = node.name.strip_prefix("module@").unwrap(); - if let Some(value) = value.strip_prefix("0x") { - usize::from_str_radix(value, 16).unwrap() - } else if let Some(value) = value.strip_prefix("0X") { - usize::from_str_radix(value, 16).unwrap() - } else { - value.parse().unwrap() - } - }) - .unwrap(); - - let header = unsafe { - &*core::mem::transmute::<*const u8, *const Header>(ptr::with_exposed_provenance( - module_start, - )) - }; - - if header.e_ident[0..SELFMAG] != ELFMAG[..] { - panic!("Didn't find valid ELF file!"); - } - - let file_size = if header.e_ident[EI_DATA] == ELFDATA2LSB { - u64::from_le(header.e_shoff) - + (u16::from_le(header.e_shentsize) as u64 * u16::from_le(header.e_shnum) as u64) - } else { - u64::from_be(header.e_shoff) - + (u16::from_be(header.e_shentsize) as u64 * u16::from_be(header.e_shnum) as u64) - }; - info!("Found ELF file with size {file_size}"); - - unsafe { - core::slice::from_raw_parts( - ptr::with_exposed_provenance(module_start), - file_size.try_into().unwrap(), - ) - } + fdt.find_kernel().unwrap() } pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index a0beb860..9eb61b78 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -4,10 +4,9 @@ mod address_range; mod start; use core::arch::asm; -use core::{mem, ptr, slice}; +use core::ptr; use address_range::AddressRange; -use fdt::node::FdtNode; use hermit_entry::Entry; use hermit_entry::boot_info::{ BootInfo, DeviceTreeAddress, HardwareInfo, PlatformInfo, RawBootInfo, @@ -16,45 +15,11 @@ use hermit_entry::elf::LoadedKernel; use log::info; use crate::BootInfoExt; - -fn find_kernel_linux(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> { - let initrd_start = chosen.property("linux,initrd-start")?.as_usize()?; - let initrd_start = ptr::with_exposed_provenance_mut::(initrd_start); - let initrd_end = chosen.property("linux,initrd-end")?.as_usize()?; - let initrd_end = ptr::with_exposed_provenance_mut::(initrd_end); - // SAFETY: We trust the raw pointer from the firmware - let initrd_len = unsafe { initrd_end.offset_from(initrd_start).try_into().unwrap() }; - - // SAFETY: We trust the raw pointer from the firmware - Some(unsafe { slice::from_raw_parts(initrd_start, initrd_len) }) -} - -fn find_kernel_multiboot(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> { - let module = chosen - .children() - .filter(|child| child.name.starts_with("module@")) - .find(|child| { - child.compatible().is_some_and(|compatible| { - compatible - .all() - .any(|compatible| compatible == "multiboot,ramdisk") - }) - })?; - let reg = module.property("reg").unwrap(); - let addr = usize::from_be_bytes(reg.value[..mem::size_of::()].try_into().unwrap()); - let len = usize::from_be_bytes(reg.value[mem::size_of::()..].try_into().unwrap()); - - let initrd_start = ptr::with_exposed_provenance_mut::(addr); - // SAFETY: We trust the raw pointer from the firmware - Some(unsafe { slice::from_raw_parts(initrd_start, len) }) -} +use crate::fdt_ext::FdtExt; pub fn find_kernel() -> &'static [u8] { let fdt = start::get_fdt(); - let chosen = fdt.find_node("/chosen").unwrap(); - find_kernel_linux(&chosen) - .or_else(|| find_kernel_multiboot(&chosen)) - .expect("could not find kernel") + fdt.find_kernel().expect("could not find kernel") } pub unsafe fn get_memory(memory_size: u64) -> u64 { diff --git a/src/fdt_ext.rs b/src/fdt_ext.rs new file mode 100644 index 00000000..0f994a57 --- /dev/null +++ b/src/fdt_ext.rs @@ -0,0 +1,50 @@ +use goblin::elf64::header::{EI_DATA, ELFDATA2LSB, ELFMAG, Header, SELFMAG}; + +pub trait FdtExt { + fn find_module_start(&self) -> Option<&'static [u8]>; + fn find_linux_initrd(&self) -> Option<&'static [u8]>; + fn find_kernel(&self) -> Option<&'static [u8]>; +} + +impl FdtExt for fdt::Fdt<'_> { + fn find_module_start(&self) -> Option<&'static [u8]> { + let module = self + .find_node("/chosen")? + .children() + .find(|node| node.name.starts_with("module@"))?; + let start_ptr = module.reg().unwrap().next().unwrap().starting_address; + + // The reg size of the module nodes is always 0, so we cannot trust them and + // instead need to parse the ELF header + let header = unsafe { &*start_ptr.cast::
() }; + + if header.e_ident[0..SELFMAG] != ELFMAG[..] { + return None; + } + + let len = if header.e_ident[EI_DATA] == ELFDATA2LSB { + u64::from_le(header.e_shoff) + + (u16::from_le(header.e_shentsize) as u64 * u16::from_le(header.e_shnum) as u64) + } else { + u64::from_be(header.e_shoff) + + (u16::from_be(header.e_shentsize) as u64 * u16::from_be(header.e_shnum) as u64) + }; + + Some(unsafe { core::slice::from_raw_parts(start_ptr, len.try_into().unwrap()) }) + } + + fn find_linux_initrd(&self) -> Option<&'static [u8]> { + let chosen = self.find_node("/chosen")?; + let start = chosen.property("linux,initrd-start")?.as_usize()?; + let end = chosen.property("linux,initrd-end")?.as_usize()?; + let start_ptr = core::ptr::with_exposed_provenance::(start); + let end_ptr = core::ptr::with_exposed_provenance::(end); + let len = unsafe { end_ptr.offset_from(start_ptr).try_into().unwrap() }; + Some(unsafe { core::slice::from_raw_parts(start_ptr, len) }) + } + + fn find_kernel(&self) -> Option<&'static [u8]> { + self.find_module_start() + .or_else(|| self.find_linux_initrd()) + } +} diff --git a/src/main.rs b/src/main.rs index 6e745e59..620209af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,8 @@ mod arch; mod bump_allocator; #[cfg(any(target_os = "uefi", target_arch = "x86_64"))] mod fdt; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +mod fdt_ext; mod log; mod os; From 938ff80d4b6297d25d1b30ff0086c37bc795d586 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Sun, 24 May 2026 02:21:58 +0200 Subject: [PATCH 2/2] fix(xtask): Fix multibook ramdisk reg in U-Boot The generated devicetree contains #address-cells = <2> and #size-cells = <2>, which means that address and size are expected to be 64-bit values, but we were currently only constructing two 32-bit values, which meant that effectively, when parsing it, the size was missing. Fix it by making the values 64-bit wide. --- xtask/src/ci/u-boot/boot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/ci/u-boot/boot.txt b/xtask/src/ci/u-boot/boot.txt index 00e1f76b..f0198161 100644 --- a/xtask/src/ci/u-boot/boot.txt +++ b/xtask/src/ci/u-boot/boot.txt @@ -7,7 +7,7 @@ fdt get value hermit_load /images/ramdisk-1 load fdt addr ${fdt_addr} fdt mknode /chosen module@${hermit_load} fdt set /chosen/module@${hermit_load} compatible "multiboot,module\0multiboot,ramdisk" -fdt set /chosen/module@${hermit_load} reg <${hermit_load} 0> +fdt set /chosen/module@${hermit_load} reg <0x0 ${hermit_load} 0x0 0x0> fdt print bootm