From dfc9fd3c60c070feea739d41ab30dd5c0ff264be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=88=CE=BB=CE=BB=CE=B5=CE=BD=20=CE=95=CE=BC=CE=AF=CE=BB?= =?UTF-8?q?=CE=B9=CE=B1=20=CE=86=CE=BD=CE=BD=CE=B1=20Zscheile?= Date: Wed, 10 Dec 2025 20:16:53 +0100 Subject: [PATCH] feat: implement decompression of Hermit images --- Cargo.lock | 29 ++++++++++++++++ Cargo.toml | 4 ++- src/arch/aarch64/mod.rs | 6 ++-- src/arch/mod.rs | 2 +- src/arch/riscv64/mod.rs | 5 ++- src/arch/x86_64/platform/linux/mod.rs | 10 ++++-- src/arch/x86_64/platform/multiboot/mod.rs | 14 +++++--- src/fdt.rs | 30 ++++++++++++++--- src/main.rs | 41 ++++++++++++++++++++++- src/os/mod.rs | 6 ++++ src/os/none/mod.rs | 15 +++++++-- src/os/uefi/mod.rs | 35 ++++++++++++++----- 12 files changed, 169 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71d61959..7defc4ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "compression" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a82b366ae14633c67a1cbb4aa3738210a23f77d2868a0fd50faa23a956f9ec4" +dependencies = [ + "cfg-if", + "lazy_static", + "log", + "num-traits", +] + [[package]] name = "const_fn" version = "0.4.11" @@ -514,10 +526,12 @@ dependencies = [ "anstyle", "built", "cfg-if", + "compression", "enum_dispatch", "fdt", "goblin", "hermit-entry", + "lazy_static", "linux-boot-params", "log", "multiboot", @@ -707,6 +721,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.175" @@ -1194,6 +1217,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 4aadddd6..d7ec5829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ align-address = "0.4" allocator-api2 = { version = "0.4", default-features = false } anstyle = { version = "1", default-features = false } cfg-if = "1" -hermit-entry = { version = "0.10", features = ["loader"] } +compression = { version = "0.1", default-features = false, features = ["gzip"] } +hermit-entry = { version = "0.10.8", features = ["loader"] } +lazy_static = { version = "1.5.0", features = ["spin_no_std"] } log = "0.4" one-shot-mutex = "0.2" take-static = "0.1" diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index be1834ef..dac7f170 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -19,7 +19,7 @@ use log::info; use crate::BootInfoExt; use crate::arch::paging::*; -use crate::os::CONSOLE; +use crate::os::{CONSOLE, ExtraBootInfo}; unsafe extern "C" { static mut loader_end: u8; @@ -106,12 +106,14 @@ pub fn find_kernel() -> &'static [u8] { } } -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { +pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! { let LoadedKernel { load_info, entry_point, } = kernel_info; + assert!(extra_info.image.is_none()); + let fdt = unsafe { Fdt::from_ptr(ptr::with_exposed_provenance(DEVICE_TREE as usize)) .expect(".fdt file has invalid header") diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 7bf01322..87096952 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -5,7 +5,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "riscv64")] { mod riscv64; pub use self::riscv64::*; - } else if #[cfg(all(target_arch = "x86_64"))] { + } else if #[cfg(target_arch = "x86_64")] { mod x86_64; pub use self::x86_64::*; } diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index a0beb860..233aeaee 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -16,6 +16,7 @@ use hermit_entry::elf::LoadedKernel; use log::info; use crate::BootInfoExt; +use crate::os::ExtraBootInfo; fn find_kernel_linux(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> { let initrd_start = chosen.property("linux,initrd-start")?.as_usize()?; @@ -89,7 +90,7 @@ pub unsafe fn get_memory(memory_size: u64) -> u64 { u64::try_from(start_address).unwrap() } -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { +pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! { let LoadedKernel { load_info, entry_point, @@ -117,6 +118,8 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { DeviceTreeAddress::new(fdt_addr.try_into().unwrap()) }; + assert!(extra_info.image.is_none()); + let boot_info = BootInfo { hardware_info: HardwareInfo { phys_addr_range, diff --git a/src/arch/x86_64/platform/linux/mod.rs b/src/arch/x86_64/platform/linux/mod.rs index e3fc5c5c..4dbc20de 100644 --- a/src/arch/x86_64/platform/linux/mod.rs +++ b/src/arch/x86_64/platform/linux/mod.rs @@ -16,6 +16,7 @@ use crate::BootInfoExt; use crate::arch::x86_64::physicalmem::PhysAlloc; use crate::arch::x86_64::{KERNEL_STACK_SIZE, SERIAL_IO_PORT, paging}; use crate::fdt::Fdt; +use crate::os::ExtraBootInfo; unsafe extern "C" { static mut loader_end: u8; @@ -51,7 +52,7 @@ pub fn find_kernel() -> &'static [u8] { boot_params_ref.map_ramdisk().unwrap() } -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { +pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! { let LoadedKernel { load_info, entry_point, @@ -74,7 +75,12 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { write_bytes(stack, 0, KERNEL_STACK_SIZE.try_into().unwrap()); } - let mut fdt = Fdt::new("linux").unwrap(); + let maybe_image = match extra_info.image { + None => &[], + Some(tar_image) => tar_image, + }; + + let mut fdt = Fdt::new("linux", maybe_image).unwrap(); let e820_entries = boot_params_ref.e820_entries(); assert!(!e820_entries.is_empty()); diff --git a/src/arch/x86_64/platform/multiboot/mod.rs b/src/arch/x86_64/platform/multiboot/mod.rs index 7f798230..e20a0833 100644 --- a/src/arch/x86_64/platform/multiboot/mod.rs +++ b/src/arch/x86_64/platform/multiboot/mod.rs @@ -16,6 +16,7 @@ use crate::BootInfoExt; use crate::arch::x86_64::physicalmem::PhysAlloc; use crate::arch::x86_64::{KERNEL_STACK_SIZE, SERIAL_IO_PORT, paging}; use crate::fdt::Fdt; +use crate::os::ExtraBootInfo; unsafe extern "C" { static mut loader_end: u8; @@ -53,7 +54,7 @@ impl MemoryManagement for Mem { pub struct DeviceTree; impl DeviceTree { - pub fn create() -> FdtWriterResult<&'static [u8]> { + pub fn create(extra_info: &ExtraBootInfo) -> FdtWriterResult<&'static [u8]> { let mut mem = Mem; let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; @@ -61,7 +62,12 @@ impl DeviceTree { .memory_regions() .expect("Could not find a memory map in the Multiboot information"); - let mut fdt = Fdt::new("multiboot")?.memory_regions(memory_regions)?; + let maybe_image = match extra_info.image { + None => &[], + Some(tar_image) => tar_image, + }; + + let mut fdt = Fdt::new("multiboot", maybe_image)?.memory_regions(memory_regions)?; if let Some(cmdline) = multiboot.command_line() { fdt = fdt.bootargs(cmdline.to_owned())?; @@ -140,7 +146,7 @@ pub fn find_kernel() -> &'static [u8] { unsafe { slice::from_raw_parts(ptr::with_exposed_provenance(elf_start), elf_len) } } -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { +pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! { let LoadedKernel { load_info, entry_point, @@ -183,7 +189,7 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { write_bytes(stack, 0, KERNEL_STACK_SIZE.try_into().unwrap()); } - let device_tree = DeviceTree::create().expect("Unable to create devicetree!"); + let device_tree = DeviceTree::create(&extra_info).expect("Unable to create devicetree!"); let device_tree = DeviceTreeAddress::new(u64::try_from(device_tree.as_ptr().expose_provenance()).unwrap()); diff --git a/src/fdt.rs b/src/fdt.rs index 0f83c4d9..215ab697 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -9,31 +9,51 @@ pub struct Fdt { writer: FdtWriter, root_node: FdtWriterNode, bootargs: Option, + image_range: Option<(u64, u64)>, } impl Fdt { - pub fn new(platform: &str) -> FdtWriterResult { - let mut writer = FdtWriter::new()?; + pub fn new(platform: &str, maybe_image: &'static [u8]) -> FdtWriterResult { + let mut mem_reserved = Vec::new(); + + let image_range = if maybe_image.is_empty() { + None + } else { + let image_start = (&maybe_image[0]) as *const u8 as u64; + mem_reserved.push(vm_fdt::FdtReserveEntry::new( + image_start, + maybe_image.len() as u64, + )?); + Some((image_start, maybe_image.len() as u64)) + }; + + let mut writer = FdtWriter::new_with_mem_reserv(&mem_reserved[..])?; let root_node = writer.begin_node("")?; writer.property_string("compatible", &format!("hermit,{platform}"))?; writer.property_u32("#address-cells", 0x2)?; writer.property_u32("#size-cells", 0x2)?; - let bootargs = None; - Ok(Self { writer, root_node, - bootargs, + bootargs: None, + image_range, }) } pub fn finish(mut self) -> FdtWriterResult> { let chosen_node = self.writer.begin_node("chosen")?; + if let Some(bootargs) = &self.bootargs { self.writer.property_string("bootargs", bootargs)?; } + + if let Some((image_start, image_len)) = self.image_range { + self.writer + .property_array_u64("image_reg", &[image_start, image_len])?; + } + self.writer.end_node(chosen_node)?; self.writer.end_node(self.root_node)?; diff --git a/src/main.rs b/src/main.rs index 40ed5b42..b3e30cf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,13 +12,14 @@ use hermit_entry::boot_info::{BootInfo, RawBootInfo}; mod macros; mod arch; + mod bump_allocator; + #[cfg(any(target_os = "uefi", target_arch = "x86_64"))] mod fdt; mod log; mod os; -#[cfg(any(target_os = "uefi", all(target_arch = "x86_64", target_os = "none")))] extern crate alloc; mod built_info { @@ -75,3 +76,41 @@ fn _print(args: core::fmt::Arguments<'_>) { self::os::CONSOLE.lock().write_fmt(args).unwrap(); } + +/// Detects the input format are resolves the kernel +fn resolve_kernel<'a>( + input_blob: &'a [u8], + buf: &'a mut Option>, +) -> (&'a [u8], Option>) { + use hermit_entry::{Format, detect_format}; + match detect_format(input_blob) { + Some(Format::Elf) => (input_blob, None), + + Some(Format::Gzip) => { + use compression::prelude::{DecodeExt as _, GZipDecoder}; + *buf = Some( + input_blob + .iter() + .copied() + .decode(&mut GZipDecoder::new()) + .collect::, _>>() + .expect("Unable to decompress Hermit gzip image"), + ); + match *buf { + Some(ref mut tmp) => { + let handle = hermit_entry::config::parse_tar(tmp) + .expect("Unable to find Hermit image configuration + kernel"); + + // TODO: do we just let the kernel handle the config + + (handle.raw_kernel, Some(handle.config)) + } + None => unreachable!(), + } + } + + None => { + panic!("Input BLOB has unknown magic bytes (neither Gzip nor ELF)") + } + } +} diff --git a/src/os/mod.rs b/src/os/mod.rs index 00b8faf2..79a1d181 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -7,3 +7,9 @@ cfg_if::cfg_if! { pub use self::uefi::*; } } + +#[cfg_attr(not(target_os = "none"), allow(dead_code))] +#[derive(Clone, Default)] +pub struct ExtraBootInfo { + pub(crate) image: Option<&'static [u8]>, +} diff --git a/src/os/none/mod.rs b/src/os/none/mod.rs index 41fa2623..e21f71ff 100644 --- a/src/os/none/mod.rs +++ b/src/os/none/mod.rs @@ -1,4 +1,4 @@ -mod allocator; +pub(crate) mod allocator; mod console; use core::fmt::Write; @@ -10,6 +10,7 @@ use log::info; pub use self::console::CONSOLE; use crate::arch; +use crate::os::ExtraBootInfo; unsafe extern "C" { static loader_end: u8; @@ -27,6 +28,10 @@ pub(crate) unsafe extern "C" fn loader_main() -> ! { } let kernel = arch::find_kernel(); + let mut buf = None; + // TODO: handle config + let (kernel, _) = crate::resolve_kernel(kernel, &mut buf); + let kernel = KernelObject::parse(kernel).unwrap(); let mem_size = kernel.mem_size(); @@ -41,7 +46,13 @@ pub(crate) unsafe extern "C" fn loader_main() -> ! { let kernel_info = kernel.load_kernel(memory, memory.as_ptr() as u64); - unsafe { arch::boot_kernel(kernel_info) } + let mut extra_info = ExtraBootInfo::default(); + if let Some(tar_image) = buf { + let tar_image = alloc::boxed::Box::leak(tar_image); + extra_info.image = Some(&*tar_image); + } + + unsafe { arch::boot_kernel(kernel_info, extra_info) } } #[panic_handler] diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index 83fb7b22..3c5a3b59 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -29,21 +29,38 @@ fn main() -> Status { crate::log::init(); crate::log_built_info(); - let kernel_image = read_app(); - let kernel = KernelObject::parse(&kernel_image).unwrap(); + let kernel; + let kernel_memory; + let kernel_info; + let rsdp_: *const _; + let maybe_image: &[u8]; - let kernel_memory = alloc_page_slice(kernel.mem_size()).unwrap(); - let kernel_memory = &mut kernel_memory[..kernel.mem_size()]; + { + let mut buf = None; + let kernel_image = read_app(); - let kernel_info = kernel.load_kernel(kernel_memory, kernel_memory.as_ptr() as u64); + let (kernel_image2, _) = crate::resolve_kernel(&kernel_image, &mut buf); - let rsdp = rsdp(); + kernel = KernelObject::parse(kernel_image2).unwrap(); - drop(kernel_image); + let kernel_memory2 = alloc_page_slice(kernel.mem_size()).unwrap(); + kernel_memory = &mut kernel_memory2[..kernel.mem_size()]; - let mut fdt = Fdt::new("uefi") + kernel_info = kernel.load_kernel(kernel_memory, kernel_memory.as_ptr() as u64); + + rsdp_ = rsdp(); + + // TODO: honor bootargs from image + + maybe_image = match buf { + None => &[], + Some(tar_image) => &*alloc::boxed::Box::leak(tar_image), + } + } + + let mut fdt = Fdt::new("uefi", maybe_image) .unwrap() - .rsdp(u64::try_from(rsdp.expose_provenance()).unwrap()) + .rsdp(u64::try_from(rsdp_.expose_provenance()).unwrap()) .unwrap(); if let Some(bootargs) = read_bootargs() {