From e853b40a3096d797e4ca70d66a7fb258113381da 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 | 46 ++----------------------------------- src/arch/riscv64/mod.rs | 42 +++------------------------------ src/main.rs | 51 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 83 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2f9d26e7..235364f6 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..8510149c 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -12,15 +12,14 @@ 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; use log::info; -use crate::BootInfoExt; use crate::arch::paging::*; use crate::os::CONSOLE; +use crate::{BootInfoExt, FdtExt}; unsafe extern "C" { static mut l0_pgtable: u64; @@ -57,49 +56,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..d2b70f6d 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, @@ -15,46 +14,11 @@ use hermit_entry::boot_info::{ 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::{BootInfoExt, 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/main.rs b/src/main.rs index 6e745e59..732715e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ #![allow(clippy::missing_safety_doc)] use ::log::info; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +use goblin::elf::header::header64::{EI_DATA, ELFDATA2LSB, ELFMAG, Header, SELFMAG}; use hermit_entry::boot_info::{BootInfo, RawBootInfo}; #[macro_use] @@ -39,6 +41,55 @@ impl BootInfoExt for BootInfo { } } +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +trait FdtExt { + fn find_kernel(&self) -> Option<&'static [u8]>; +} + +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +impl FdtExt for fdt::Fdt<'_> { + fn find_kernel(&self) -> Option<&'static [u8]> { + let chosen = self.find_node("/chosen").unwrap(); + + // The reg size of the module nodes is always 0, so we cannot trust them and + // instead need to parse the ELF header + let find_module_start = || { + let module = chosen + .children() + .find(|node| node.name.starts_with("module@"))?; + let start_ptr = module.reg().unwrap().next().unwrap().starting_address; + let header = unsafe { &*core::mem::transmute::<*const u8, *const Header>(start_ptr) }; + + 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()) }) + }; + + let find_linux_initrd = || { + 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) }) + }; + + find_module_start().or_else(find_linux_initrd) + } +} + #[doc(hidden)] fn _print(args: core::fmt::Arguments<'_>) { use core::fmt::Write; From 5ce1b37f25a7c93c552d4196c42fca98a77908e9 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