Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::arch;
9use crate::frame_alloc::FrameAllocator;
10use core::alloc::Layout;
11use core::mem::MaybeUninit;
12use core::range::Range;
13use core::slice;
14use loader_api::{BootInfo, MemoryRegion, MemoryRegionKind, MemoryRegions, TlsTemplate};
15
16#[expect(clippy::too_many_arguments, reason = "")]
17pub fn prepare_boot_info(
18 mut frame_alloc: FrameAllocator,
19 physical_address_offset: usize,
20 physical_memory_map: Range<usize>,
21 kernel_virt: Range<usize>,
22 maybe_tls_template: Option<TlsTemplate>,
23 loader_phys: Range<usize>,
24 kernel_phys: Range<usize>,
25 fdt_phys: Range<usize>,
26 hart_mask: usize,
27 rng_seed: [u8; 32],
28) -> crate::Result<*mut BootInfo> {
29 let frame = frame_alloc.allocate_contiguous_zeroed(
30 Layout::from_size_align(arch::PAGE_SIZE, arch::PAGE_SIZE).unwrap(),
31 arch::KERNEL_ASPACE_BASE,
32 )?;
33 let page = physical_address_offset.checked_add(frame).unwrap();
34
35 let memory_regions =
36 init_boot_info_memory_regions(page, frame_alloc, fdt_phys, loader_phys, kernel_phys);
37
38 let mut boot_info = BootInfo::new(memory_regions);
39 boot_info.physical_address_offset = physical_address_offset;
40 boot_info.physical_memory_map = physical_memory_map;
41 boot_info.tls_template = maybe_tls_template;
42 boot_info.kernel_virt = kernel_virt;
43 boot_info.kernel_phys = kernel_phys;
44 boot_info.cpu_mask = hart_mask;
45 boot_info.rng_seed = rng_seed;
46
47 let boot_info_ptr = page as *mut BootInfo;
48 // Safety: we just allocated the boot info frame
49 unsafe { boot_info_ptr.write(boot_info) }
50
51 Ok(boot_info_ptr)
52}
53
54fn init_boot_info_memory_regions(
55 page: usize,
56 frame_alloc: FrameAllocator,
57 fdt_phys: Range<usize>,
58 loader_phys: Range<usize>,
59 kernel_phys: Range<usize>,
60) -> MemoryRegions {
61 // Safety: we just allocated a whole frame for the boot info
62 let regions: &mut [MaybeUninit<MemoryRegion>] = unsafe {
63 let base = page.checked_add(size_of::<BootInfo>()).unwrap();
64 let len = (arch::PAGE_SIZE - size_of::<BootInfo>()) / size_of::<MemoryRegion>();
65
66 slice::from_raw_parts_mut(base as *mut MaybeUninit<MemoryRegion>, len)
67 };
68
69 let mut len = 0;
70 let mut push_region = |region: MemoryRegion| {
71 debug_assert!(!region.range.is_empty());
72 regions[len].write(region);
73 len += 1;
74 };
75
76 // Report the memory we consumed during startup as used.
77 for used_region in frame_alloc.used_regions() {
78 push_region(MemoryRegion {
79 range: used_region,
80 kind: MemoryRegionKind::Loader,
81 });
82 }
83
84 // Report the free regions as usable.
85 for free_region in frame_alloc.free_regions() {
86 push_region(MemoryRegion {
87 range: free_region,
88 kind: MemoryRegionKind::Usable,
89 });
90 }
91
92 // Most of the memory occupied by the loader is not needed once the kernel is running,
93 // but the kernel itself lies somewhere in the loader memory.
94 //
95 // We can still mark the range before and after the kernel as usable.
96 push_region(MemoryRegion {
97 range: Range::from(loader_phys.start..kernel_phys.start),
98 kind: MemoryRegionKind::Usable,
99 });
100 push_region(MemoryRegion {
101 range: Range::from(kernel_phys.end..loader_phys.end),
102 kind: MemoryRegionKind::Usable,
103 });
104
105 // Report the flattened device tree as a separate region.
106 push_region(MemoryRegion {
107 range: fdt_phys,
108 kind: MemoryRegionKind::FDT,
109 });
110
111 // Truncate the slice to include only initialized elements
112 // Safety: closure above ensures the slice up to len is valid
113 let regions = unsafe { regions[0..len].assume_init_mut() };
114
115 // Sort the memory regions by start address, we do this now in the loader
116 // because the BootInfo struct will be passed as a read-only static reference to the kernel.
117 regions.sort_unstable_by_key(|region| region.range.start);
118
119 MemoryRegions::from(regions)
120}