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 core::fmt::Formatter;
9use core::ops::Range;
10use core::{fmt, slice};
11
12use kmem::{AddressRangeExt, PhysicalAddress, VirtualAddress};
13use loader_api::LoaderConfig;
14use xmas_elf::program::{ProgramHeader, Type};
15
16use crate::error::Error;
17
18/// The inlined kernel
19static INLINED_KERNEL_BYTES: KernelBytes = KernelBytes(*include_bytes!(env!("KERNEL")));
20/// Wrapper type for the inlined bytes to ensure proper alignment
21#[repr(C, align(4096))]
22struct KernelBytes(pub [u8; include_bytes!(env!("KERNEL")).len()]);
23
24/// The decompressed and parsed kernel ELF plus the embedded loader configuration data
25pub struct Kernel<'a> {
26 pub elf_file: xmas_elf::ElfFile<'a>,
27 pub _loader_config: &'a LoaderConfig,
28}
29
30impl Kernel<'static> {
31 pub fn from_static(phys_off: VirtualAddress) -> crate::Result<Self> {
32 // Safety: The kernel elf file is inlined into the loader executable as part of the build setup
33 // which means we just need to parse it here.
34 let elf_file = xmas_elf::ElfFile::new(unsafe {
35 let base = phys_off.add(INLINED_KERNEL_BYTES.0.as_ptr().addr());
36
37 slice::from_raw_parts(base.as_mut_ptr(), INLINED_KERNEL_BYTES.0.len())
38 })
39 .map_err(Error::Elf)?;
40
41 let loader_config = {
42 let section = elf_file
43 .find_section_by_name(".loader_config")
44 .expect("missing .loader_config section");
45 let raw = section.raw_data(&elf_file);
46
47 let ptr: *const LoaderConfig = raw.as_ptr().cast();
48 // Safety: kernel is inlined into the loader, so ptr is always valid
49 let cfg = unsafe { &*ptr };
50
51 cfg.assert_valid();
52 cfg
53 };
54
55 Ok(Kernel {
56 elf_file,
57 _loader_config: loader_config,
58 })
59 }
60}
61
62impl Kernel<'_> {
63 pub fn phys_range(&self) -> Range<PhysicalAddress> {
64 Range::from_start_len(
65 PhysicalAddress::from_ptr(INLINED_KERNEL_BYTES.0.as_ptr()),
66 INLINED_KERNEL_BYTES.0.len(),
67 )
68 }
69
70 /// Returns the size of the kernel in memory.
71 pub fn mem_size(&self) -> u64 {
72 let max_addr = self
73 .loadable_program_headers()
74 .map(|ph| ph.virtual_addr() + ph.mem_size())
75 .max()
76 .unwrap_or(0);
77
78 let min_addr = self
79 .loadable_program_headers()
80 .map(|ph| ph.virtual_addr())
81 .min()
82 .unwrap_or(0);
83
84 max_addr - min_addr
85 }
86
87 /// Returns the largest alignment of any loadable segment in the kernel and by extension
88 /// the overall alignment for the kernel.
89 pub fn max_align(&self) -> u64 {
90 let load_program_headers = self.loadable_program_headers();
91
92 load_program_headers.map(|ph| ph.align()).max().unwrap_or(1)
93 }
94
95 fn loadable_program_headers(&self) -> impl Iterator<Item = ProgramHeader<'_>> + '_ {
96 self.elf_file
97 .program_iter()
98 .filter(|ph| ph.get_type().unwrap() == Type::Load)
99 }
100}
101
102impl fmt::Display for Kernel<'_> {
103 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
104 writeln!(
105 f,
106 "Idx Name Offset Vaddr Filesz Memsz"
107 )?;
108
109 for (idx, sec) in self.elf_file.section_iter().enumerate() {
110 writeln!(
111 f,
112 "{idx:>3} {name:<17} {offset:#08x} {vaddr:#016x} {filesz:#08x} {memsz:#08x}",
113 name = sec.get_name(&self.elf_file).unwrap_or(""),
114 offset = sec.offset(),
115 vaddr = sec.address(),
116 filesz = sec.entry_size(),
117 memsz = sec.size(),
118 )?;
119 }
120 Ok(())
121 }
122}