Skip to content

Commit 0d9d117

Browse files
committed
feat: implement decompression of Hermit images
1 parent 8577575 commit 0d9d117

File tree

13 files changed

+550
-157
lines changed

13 files changed

+550
-157
lines changed

Cargo.lock

Lines changed: 344 additions & 138 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ align-address = "0.3"
1111
allocator-api2 = { version = "0.3", default-features = false }
1212
anstyle = { version = "1", default-features = false }
1313
cfg-if = "1"
14-
hermit-entry = { version = "0.10", features = ["loader"] }
14+
hermit-entry = { version = "0.10", git = "https://github.com/fogti/hermit-entry.git", branch = "image-reader", features = ["allocator-api2", "loader"] }
1515
log = "0.4"
1616
one-shot-mutex = "0.2"
1717
sptr = "0.3"

src/arch/aarch64/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use sptr::Strict;
2020

2121
use crate::BootInfoExt;
2222
use crate::arch::paging::*;
23-
use crate::os::CONSOLE;
23+
use crate::os::{CONSOLE, ExtraBootInfo};
2424

2525
unsafe extern "C" {
2626
static mut loader_end: u8;
@@ -110,12 +110,14 @@ pub fn find_kernel() -> &'static [u8] {
110110
}
111111
}
112112

113-
pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
113+
pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! {
114114
let LoadedKernel {
115115
load_info,
116116
entry_point,
117117
} = kernel_info;
118118

119+
assert!(extra_info.image.is_none());
120+
119121
let dtb = unsafe {
120122
Fdt::from_ptr(sptr::from_exposed_addr(get_dtb_addr() as usize))
121123
.expect(".dtb file has invalid header")

src/arch/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cfg_if::cfg_if! {
55
} else if #[cfg(target_arch = "riscv64")] {
66
mod riscv64;
77
pub use self::riscv64::*;
8-
} else if #[cfg(all(target_arch = "x86_64"))] {
8+
} else if #[cfg(target_arch = "x86_64")] {
99
mod x86_64;
1010
pub use self::x86_64::*;
1111
}

src/arch/riscv64/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use log::info;
1717
use sptr::Strict;
1818

1919
use crate::BootInfoExt;
20+
use crate::os::ExtraBootInfo;
2021

2122
fn find_kernel_linux(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> {
2223
let initrd_start = chosen.property("linux,initrd-start")?.as_usize()?;
@@ -90,7 +91,7 @@ pub unsafe fn get_memory(memory_size: u64) -> u64 {
9091
u64::try_from(start_address).unwrap()
9192
}
9293

93-
pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
94+
pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! {
9495
let LoadedKernel {
9596
load_info,
9697
entry_point,
@@ -118,6 +119,8 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
118119
DeviceTreeAddress::new(fdt_addr.try_into().unwrap())
119120
};
120121

122+
assert!(extra_info.image.is_none());
123+
121124
let boot_info = BootInfo {
122125
hardware_info: HardwareInfo {
123126
phys_addr_range,

src/arch/x86_64/firecracker.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use super::physicalmem::PhysAlloc;
2020
use super::{KERNEL_STACK_SIZE, SERIAL_IO_PORT, paging};
2121
use crate::BootInfoExt;
2222
use crate::fdt::Fdt;
23+
use crate::os::ExtraBootInfo;
2324

2425
unsafe extern "C" {
2526
static mut loader_end: u8;
@@ -118,7 +119,7 @@ pub fn find_kernel() -> &'static [u8] {
118119
unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) }
119120
}
120121

121-
pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
122+
pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! {
122123
let LoadedKernel {
123124
load_info,
124125
entry_point,
@@ -172,7 +173,12 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
172173
write_bytes(stack, 0, KERNEL_STACK_SIZE.try_into().unwrap());
173174
}
174175

175-
let mut fdt = Fdt::new("firecracker").unwrap();
176+
let maybe_image = match extra_info.image {
177+
None => &[],
178+
Some(tar_image) => tar_image,
179+
};
180+
181+
let mut fdt = Fdt::new("firecracker", maybe_image).unwrap();
176182

177183
// Load the boot_param memory-map information
178184
let linux_e820_entries =

src/arch/x86_64/multiboot.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::physicalmem::PhysAlloc;
1818
use crate::BootInfoExt;
1919
use crate::arch::x86_64::{KERNEL_STACK_SIZE, SERIAL_IO_PORT};
2020
use crate::fdt::Fdt;
21+
use crate::os::ExtraBootInfo;
2122

2223
unsafe extern "C" {
2324
static mut loader_end: u8;
@@ -55,15 +56,20 @@ impl MemoryManagement for Mem {
5556
pub struct DeviceTree;
5657

5758
impl DeviceTree {
58-
pub fn create() -> FdtWriterResult<&'static [u8]> {
59+
pub fn create(extra_info: &ExtraBootInfo) -> FdtWriterResult<&'static [u8]> {
5960
let mut mem = Mem;
6061
let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() };
6162

6263
let memory_regions = multiboot
6364
.memory_regions()
6465
.expect("Could not find a memory map in the Multiboot information");
6566

66-
let mut fdt = Fdt::new("multiboot")?.memory_regions(memory_regions)?;
67+
let maybe_image = match extra_info.image {
68+
None => &[],
69+
Some(tar_image) => tar_image,
70+
};
71+
72+
let mut fdt = Fdt::new("multiboot", maybe_image)?.memory_regions(memory_regions)?;
6773

6874
if let Some(cmdline) = multiboot.command_line() {
6975
fdt = fdt.bootargs(cmdline.to_owned())?;
@@ -142,7 +148,7 @@ pub fn find_kernel() -> &'static [u8] {
142148
unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) }
143149
}
144150

145-
pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
151+
pub unsafe fn boot_kernel(kernel_info: LoadedKernel, extra_info: ExtraBootInfo) -> ! {
146152
let LoadedKernel {
147153
load_info,
148154
entry_point,
@@ -185,7 +191,7 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! {
185191
write_bytes(stack, 0, KERNEL_STACK_SIZE.try_into().unwrap());
186192
}
187193

188-
let device_tree = DeviceTree::create().expect("Unable to create devicetree!");
194+
let device_tree = DeviceTree::create(extra_info).expect("Unable to create devicetree!");
189195
let device_tree =
190196
DeviceTreeAddress::new(u64::try_from(device_tree.as_ptr().expose_addr()).unwrap());
191197

src/fdt.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,47 @@ pub struct Fdt {
99
writer: FdtWriter,
1010
root_node: FdtWriterNode,
1111
bootargs: Option<String>,
12+
image_range: Option<(u64, u64)>,
1213
}
1314

1415
impl Fdt {
15-
pub fn new(platform: &str) -> FdtWriterResult<Self> {
16-
let mut writer = FdtWriter::new()?;
16+
pub fn new(platform: &str, maybe_image: &'static [u8]) -> FdtWriterResult<Self> {
17+
let mut mem_reserved = Vec::new();
18+
19+
let image_range = if maybe_image.is_empty() {
20+
None
21+
} else {
22+
let image_start = (&maybe_image[0]) as *const u8 as u64;
23+
mem_reserved.push(FdtReserveEntry::new(image_start, maybe_image.len() as u64)?);
24+
Some((image_start, maybe_image.len() as u64))
25+
};
26+
27+
let mut writer = FdtWriter::new_with_mem_reserv(&mem_reserved[..])?;
1728

1829
let root_node = writer.begin_node("")?;
1930
writer.property_string("compatible", &format!("hermit,{platform}"))?;
2031
writer.property_u32("#address-cells", 0x2)?;
2132
writer.property_u32("#size-cells", 0x2)?;
2233

23-
let bootargs = None;
24-
2534
Ok(Self {
2635
writer,
2736
root_node,
28-
bootargs,
37+
bootargs: None,
38+
image_range,
2939
})
3040
}
3141

3242
pub fn finish(mut self) -> FdtWriterResult<Vec<u8>> {
3343
let chosen_node = self.writer.begin_node("chosen")?;
44+
3445
if let Some(bootargs) = &self.bootargs {
3546
self.writer.property_string("bootargs", bootargs)?;
3647
}
48+
49+
if let Some((image_start, image_len)) = self.image_range {
50+
self.writer_property_array_u64("image_reg", &[image_start, image_len])?;
51+
}
52+
3753
self.writer.end_node(chosen_node)?;
3854

3955
self.writer.end_node(self.root_node)?;

src/main.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use hermit_entry::boot_info::{BootInfo, RawBootInfo};
1212
mod macros;
1313

1414
mod arch;
15+
1516
mod bump_allocator;
17+
mod oneshot_allocator;
18+
1619
#[cfg(any(target_os = "uefi", target_arch = "x86_64"))]
1720
mod fdt;
1821
mod log;
@@ -45,3 +48,38 @@ fn _print(args: core::fmt::Arguments<'_>) {
4548

4649
self::os::CONSOLE.lock().write_fmt(args).unwrap();
4750
}
51+
52+
/// Detects the input format are resolves the kernel
53+
fn resolve_kernel<'a, A: allocator_api2::alloc::Allocator>(
54+
input_blob: &[u8],
55+
alloc: A,
56+
buf: &'a mut Option<allocator_api2::boxes::Box<hermit_entry::tar_parser::Bytes, A>>,
57+
) -> (&'a [u8], Option<hermit_entry::config::Config>) {
58+
use hermit_entry::{Format, ThinTree, decompress_image_with_allocator, detect_format};
59+
match detect_format(input_blob) {
60+
Some(Format::Elf) => (input_blob, None),
61+
62+
Some(Format::Gzip) => {
63+
*buf = Some(
64+
decompress_image_with_allocator(input_blob, alloc)
65+
.expect("Unable to decompress Hermit gzip image"),
66+
);
67+
let tmp = buf.as_mut().unwrap();
68+
69+
let image_tree =
70+
ThinTree::try_from_image(&tmp).expect("Unable to parse Hermit image tarball");
71+
72+
let (config, kernel) = image_tree
73+
.handle_config()
74+
.expect("Unable to find Hermit image configuration + kernel");
75+
76+
// TODO: do we just let the kernel handle the config
77+
78+
(kernel, Some(config))
79+
}
80+
81+
None => {
82+
panic!("Input BLOB has unknown magic bytes (neither Gzip nor ELF)")
83+
}
84+
}
85+
}

src/oneshot_allocator.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! A one-shot allocator.
2+
//!
3+
//! This is a simple allocator design which can only allocate once.
4+
5+
use core::cell::Cell;
6+
use core::mem::MaybeUninit;
7+
use core::ptr::NonNull;
8+
9+
use allocator_api2::alloc::{AllocError, Allocator, Layout};
10+
11+
/// A simple, `!Sync` implementation of a one-shot allocator.
12+
///
13+
/// This allocator manages the provided memory.
14+
pub struct OneshotAllocator {
15+
mem: Cell<*mut u8>,
16+
}
17+
18+
unsafe impl Allocator for OneshotAllocator {
19+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
20+
assert!(layout.align() <= 8);
21+
// `mem` is already aligned.
22+
23+
match NonNull::new(self.mem.take()) {
24+
None => return Err(AllocError),
25+
Some(mem) => {
26+
let mid = layout.size();
27+
if mid >= (isize::MAX / 2) {
28+
self.mem.set(mem);
29+
Err(AllocError)
30+
} else {
31+
Ok(mem)
32+
}
33+
}
34+
}
35+
}
36+
37+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
38+
if self.mem.get().is_null() {
39+
self.mem.set(ptr);
40+
} else {
41+
#[cfg(debug_assertions)]
42+
panic!("Tried to deallocate pointer that was allocated from a different allocator");
43+
}
44+
}
45+
46+
unsafe fn grow(
47+
&self,
48+
ptr: NonNull<u8>,
49+
old_layout: Layout,
50+
new_layout: Layout,
51+
) -> Result<NonNull<[u8]>, AllocError> {
52+
assert!(new_layout.align() <= 8);
53+
Ok(ptr)
54+
}
55+
56+
unsafe fn grow_zeroed(
57+
&self,
58+
ptr: NonNull<u8>,
59+
old_layout: Layout,
60+
new_layout: Layout,
61+
) -> Result<NonNull<[u8]>, AllocError> {
62+
assert!(
63+
new_layout.size() >= old_layout.size(),
64+
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
65+
);
66+
assert!(new_layout.align() <= 8);
67+
68+
unsafe {
69+
ptr.add(old_layout.size())
70+
.as_ptr()
71+
.write_bytes(0, new_layout.size() - old_layout.size())
72+
};
73+
74+
Ok(ptr)
75+
}
76+
}
77+
78+
impl BumpAllocator {
79+
pub fn new(ptr: NonNull<u8>) -> Self {
80+
let align_offset = ptr.align_offset(8);
81+
Self {
82+
mem: Cell::new(ptr.add(align_offset)),
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)