monitor/mon/compartment/
compthread.rs

1use dynlink::{compartment::MONITOR_COMPARTMENT_ID, context::Context};
2use miette::IntoDiagnostic;
3use twizzler_abi::{
4    object::MAX_SIZE,
5    upcall::{ResumeFlags, UpcallFrame},
6};
7use twizzler_rt_abi::object::ObjID;
8
9use crate::mon::{
10    space::MapHandle,
11    thread::{ManagedThread, ThreadMgr, DEFAULT_STACK_SIZE, STACK_SIZE_MIN_ALIGN},
12};
13
14#[allow(dead_code)]
15pub(super) struct CompThread {
16    pub(crate) stack_object: StackObject,
17    pub(crate) thread: ManagedThread,
18}
19
20impl CompThread {
21    /// Start a new thread using the given stack, in the provided security context instance, using
22    /// the start function.
23    #[allow(clippy::too_many_arguments)]
24    pub fn new(
25        tmgr: &mut ThreadMgr,
26        dynlink: &mut Context,
27        stack: StackObject,
28        instance: ObjID,
29        main_thread_comp: Option<ObjID>,
30        entry: usize,
31        arg: usize,
32        suspend_on_start: bool,
33    ) -> miette::Result<Self> {
34        let frame = stack.get_entry_frame(instance, entry, arg);
35        let start = move || {
36            twizzler_abi::syscall::sys_sctx_attach(instance).unwrap();
37            let flags = if suspend_on_start {
38                ResumeFlags::SUSPEND
39            } else {
40                ResumeFlags::empty()
41            };
42            unsafe { twizzler_abi::syscall::sys_thread_resume_from_upcall(&frame, flags) };
43        };
44        let mon = dynlink.get_compartment_mut(MONITOR_COMPARTMENT_ID).unwrap();
45        let mt = tmgr
46            .start_thread(mon, Box::new(start), main_thread_comp)
47            .into_diagnostic()?;
48        Ok(Self {
49            stack_object: stack,
50            thread: mt,
51        })
52    }
53}
54
55pub(crate) struct StackObject {
56    handle: MapHandle,
57    stack_size: usize,
58}
59
60impl StackObject {
61    /// Make a new stack object from a given handle and stack size.
62    pub fn new(handle: MapHandle, stack_size: usize) -> miette::Result<Self> {
63        // Find the stack size, with max and min values, and correct alignment.
64        let stack_size = stack_size
65            .clamp(DEFAULT_STACK_SIZE, MAX_SIZE / 2)
66            .next_multiple_of(STACK_SIZE_MIN_ALIGN);
67
68        Ok(Self { handle, stack_size })
69    }
70
71    /// Get the start start address for the compartment.
72    pub fn stack_comp_start(&self) -> usize {
73        self.handle.addrs().start
74    }
75
76    /// Get the stack size.
77    pub fn stack_size(&self) -> usize {
78        self.stack_size
79    }
80
81    // This works for architectures where the stack grows down. If your architecture does not use a
82    // downward-growing stack, implement this function differently.
83    #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
84    /// Get the initial stack pointer.
85    pub fn initial_stack_ptr(&self) -> usize {
86        self.stack_comp_start() + self.stack_size
87    }
88
89    /// Get the entry frame for this thread into a given compartment.
90    pub fn get_entry_frame(&self, ctx: ObjID, entry: usize, arg: usize) -> UpcallFrame {
91        UpcallFrame::new_entry_frame(
92            self.initial_stack_ptr(),
93            self.stack_size(),
94            0,
95            ctx,
96            entry,
97            arg,
98        )
99    }
100}