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