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 core::ptr;
10use gimli::{Pointer, Register, RegisterRule, UnwindTableRow};
11
12pub struct StoreOnStack;
13
14// gimli's MSRV doesn't allow const generics, so we need to pick a supported array size.
15const fn next_value(x: usize) -> usize {
16 let supported = [0, 1, 2, 3, 4, 8, 16, 32, 64, 128];
17 let mut i = 0;
18 while i < supported.len() {
19 if supported[i] >= x {
20 return supported[i];
21 }
22 i += 1;
23 }
24 192
25}
26
27impl<R: gimli::ReaderOffset> gimli::UnwindContextStorage<R> for StoreOnStack {
28 type Rules = [(Register, RegisterRule<R>); next_value(arch::MAX_REG_RULES)];
29 type Stack = [UnwindTableRow<R, Self>; 2];
30}
31
32pub unsafe fn get_unlimited_slice<'a>(start: *const u8) -> &'a [u8] {
33 // Create the largest possible slice for this address.
34 let start = start as usize;
35 let end = start.saturating_add(isize::MAX as usize);
36 let len = end - start;
37 // Safety: caller has to ensure start is valid
38 unsafe { core::slice::from_raw_parts(start as *const u8, len) }
39}
40
41pub unsafe fn deref_pointer(ptr: Pointer) -> u64 {
42 match ptr {
43 Pointer::Direct(x) => x,
44 // Safety: caller has to ensure `ptr` is valid
45 Pointer::Indirect(x) => unsafe { *(x as *const u64) },
46 }
47}
48
49// Helper function to turn `save_context` which takes function pointer to a closure-taking function.
50pub fn with_context<T, F: FnOnce(&mut arch::Registers, usize) -> T>(f: F) -> T {
51 use core::mem::ManuallyDrop;
52
53 union Data<T, F> {
54 f: ManuallyDrop<F>,
55 t: ManuallyDrop<T>,
56 }
57
58 extern "C" fn delegate<T, F: FnOnce(&mut arch::Registers, usize) -> T>(
59 regs: &mut arch::Registers,
60 ptr: *mut (),
61 ) {
62 // SAFETY: This function is called exactly once; it extracts the function, call it and
63 // store the return value. This function is `extern "C"` so we don't need to worry about
64 // unwinding past it.
65 unsafe {
66 let data = &mut *ptr.cast::<Data<T, F>>();
67
68 // Due to the way we capture the register context the effective program counter for unwinding
69 // is the return address. TODO explain why
70 let ip = regs[arch::RA];
71
72 let t = ManuallyDrop::take(&mut data.f)(regs, ip);
73 data.t = ManuallyDrop::new(t);
74 }
75 }
76
77 let mut data = Data {
78 f: ManuallyDrop::new(f),
79 };
80 arch::save_context(delegate::<T, F>, ptr::addr_of_mut!(data).cast());
81
82 // Safety: `delegate` places the closure return value into `data.t`
83 unsafe { ManuallyDrop::into_inner(data.t) }
84}