Next Generation WASM Microkernel Operating System
at main 136 lines 4.8 kB view raw
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 core::alloc::Layout; 9use core::mem::MaybeUninit; 10use core::ops::Range; 11use core::slice; 12 13use kmem::{PhysicalAddress, VirtualAddress}; 14use loader_api::{BootInfo, MemoryRegion, MemoryRegionKind, MemoryRegions, TlsTemplate}; 15 16use crate::arch; 17use crate::frame_alloc::FrameAllocator; 18 19#[expect(clippy::too_many_arguments, reason = "")] 20pub fn prepare_boot_info( 21 mut frame_alloc: FrameAllocator, 22 physical_address_offset: VirtualAddress, 23 physical_memory_map: Range<VirtualAddress>, 24 kernel_virt: Range<VirtualAddress>, 25 maybe_tls_template: Option<TlsTemplate>, 26 loader_phys: Range<PhysicalAddress>, 27 kernel_phys: Range<PhysicalAddress>, 28 fdt_phys: Range<PhysicalAddress>, 29 hart_mask: usize, 30 rng_seed: [u8; 32], 31) -> crate::Result<*mut BootInfo> { 32 let frame = frame_alloc.allocate_contiguous_zeroed( 33 Layout::from_size_align(arch::PAGE_SIZE, arch::PAGE_SIZE).unwrap(), 34 arch::KERNEL_ASPACE_BASE, 35 )?; 36 let page = physical_address_offset.add(frame.get()); 37 38 let memory_regions = init_boot_info_memory_regions( 39 page, 40 frame_alloc, 41 fdt_phys, 42 loader_phys, 43 kernel_phys.clone(), 44 ); 45 46 let mut boot_info = BootInfo::new(memory_regions); 47 boot_info.physical_address_offset = physical_address_offset; 48 boot_info.physical_memory_map = physical_memory_map; 49 boot_info.tls_template = maybe_tls_template; 50 boot_info.kernel_virt = kernel_virt; 51 boot_info.kernel_phys = kernel_phys; 52 boot_info.cpu_mask = hart_mask; 53 boot_info.rng_seed = rng_seed; 54 55 #[expect( 56 clippy::cast_ptr_alignment, 57 reason = "`page` is actually page aligned, so this is perfectly fine" 58 )] 59 let boot_info_ptr = page.as_mut_ptr().cast::<BootInfo>(); 60 // Safety: we just allocated the boot info frame 61 unsafe { boot_info_ptr.write(boot_info) } 62 63 Ok(boot_info_ptr) 64} 65 66fn init_boot_info_memory_regions( 67 page: VirtualAddress, 68 frame_alloc: FrameAllocator, 69 fdt_phys: Range<PhysicalAddress>, 70 loader_phys: Range<PhysicalAddress>, 71 kernel_phys: Range<PhysicalAddress>, 72) -> MemoryRegions { 73 // Safety: we just allocated a whole frame for the boot info 74 let regions: &mut [MaybeUninit<MemoryRegion>] = unsafe { 75 let base = page.add(size_of::<BootInfo>()); 76 let len = (arch::PAGE_SIZE - size_of::<BootInfo>()) / size_of::<MemoryRegion>(); 77 78 #[expect( 79 clippy::cast_ptr_alignment, 80 reason = "`page` is actually page aligned, so this is perfectly fine" 81 )] 82 slice::from_raw_parts_mut(base.as_mut_ptr().cast::<MaybeUninit<MemoryRegion>>(), len) 83 }; 84 85 let mut len = 0; 86 let mut push_region = |region: MemoryRegion| { 87 debug_assert!(!region.range.is_empty()); 88 regions[len].write(region); 89 len += 1; 90 }; 91 92 // Report the memory we consumed during startup as used. 93 for used_region in frame_alloc.used_regions() { 94 push_region(MemoryRegion { 95 range: used_region, 96 kind: MemoryRegionKind::Loader, 97 }); 98 } 99 100 // Report the free regions as usable. 101 for free_region in frame_alloc.free_regions() { 102 push_region(MemoryRegion { 103 range: free_region, 104 kind: MemoryRegionKind::Usable, 105 }); 106 } 107 108 // Most of the memory occupied by the loader is not needed once the kernel is running, 109 // but the kernel itself lies somewhere in the loader memory. 110 // 111 // We can still mark the range before and after the kernel as usable. 112 push_region(MemoryRegion { 113 range: loader_phys.start..kernel_phys.start, 114 kind: MemoryRegionKind::Usable, 115 }); 116 push_region(MemoryRegion { 117 range: kernel_phys.end..loader_phys.end, 118 kind: MemoryRegionKind::Usable, 119 }); 120 121 // Report the flattened device tree as a separate region. 122 push_region(MemoryRegion { 123 range: fdt_phys, 124 kind: MemoryRegionKind::FDT, 125 }); 126 127 // Truncate the slice to include only initialized elements 128 // Safety: closure above ensures the slice up to len is valid 129 let regions = unsafe { regions[0..len].assume_init_mut() }; 130 131 // Sort the memory regions by start address, we do this now in the loader 132 // because the BootInfo struct will be passed as a read-only static reference to the kernel. 133 regions.sort_unstable_by_key(|region| region.range.start); 134 135 MemoryRegions::from(regions) 136}