twizzler_abi/
upcall.rs

1//! Functions for handling upcalls from the kernel.
2
3use core::fmt::Debug;
4
5use bitflags::bitflags;
6use twizzler_rt_abi::error::{RawTwzError, TwzError};
7
8pub use crate::arch::upcall::UpcallFrame;
9use crate::{
10    object::ObjID,
11    syscall::{sys_thread_get_upcall, sys_thread_set_upcall},
12};
13
14/// Information about an exception.
15#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
16#[repr(C)]
17pub struct ExceptionInfo {
18    /// CPU-reported exception code.
19    pub code: u64,
20    /// Arch-specific additional info.
21    pub info: u64,
22}
23
24impl ExceptionInfo {
25    /// Construct new exception info.
26    pub fn new(code: u64, info: u64) -> Self {
27        Self { code, info }
28    }
29}
30
31/// Information about a memory access error to an object.
32#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
33#[repr(C)]
34pub struct ObjectMemoryFaultInfo {
35    /// Object ID of attempted access.
36    pub object_id: ObjID,
37    /// The kind of error.
38    pub error: ObjectMemoryError,
39    /// The kind of memory access that caused the error.
40    pub access: MemoryAccessKind,
41    /// The virtual address at which the error occurred.
42    pub addr: usize,
43}
44
45impl ObjectMemoryFaultInfo {
46    /// Construct a new upcall info for memory fault.
47    pub fn new(
48        object_id: ObjID,
49        error: ObjectMemoryError,
50        access: MemoryAccessKind,
51        addr: usize,
52    ) -> Self {
53        Self {
54            object_id,
55            error,
56            access,
57            addr,
58        }
59    }
60}
61
62/// Kinds of object memory errors.
63#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
64#[repr(u8)]
65pub enum ObjectMemoryError {
66    /// Tried to access an object's null page
67    NullPageAccess,
68    /// Tried to access outside of an object
69    OutOfBounds(usize),
70    /// Failed to satisfy fault due to backing storage failure
71    BackingFailed(RawTwzError),
72}
73
74/// Information about a non-object-related memory access violation.
75#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
76#[repr(C)]
77pub struct MemoryContextViolationInfo {
78    /// The virtual address that caused the exception.
79    pub address: u64,
80    /// The kind of memory access.
81    pub kind: MemoryAccessKind,
82}
83
84impl Debug for MemoryContextViolationInfo {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        write!(
87            f,
88            "MemoryContextViolationInfo {{ {:?}: {:x} }}",
89            self.kind, self.address
90        )
91    }
92}
93
94impl MemoryContextViolationInfo {
95    pub fn new(address: u64, kind: MemoryAccessKind) -> Self {
96        Self { address, kind }
97    }
98}
99
100/// Kinds of memory access.
101#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
102#[repr(u8)]
103pub enum MemoryAccessKind {
104    Read,
105    Write,
106    InstructionFetch,
107}
108
109/// Information about a non-object-related memory access violation.
110#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
111#[repr(C)]
112pub struct SecurityViolationInfo {
113    /// The virtual address that caused the violation.
114    pub address: u64,
115    /// The kind of memory access.
116    pub access_kind: MemoryAccessKind,
117}
118
119/// Possible upcall reasons and info.
120#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
121#[repr(C)]
122pub enum UpcallInfo {
123    Exception(ExceptionInfo),
124    ObjectMemoryFault(ObjectMemoryFaultInfo),
125    MemoryContextViolation(MemoryContextViolationInfo),
126    SecurityViolation(SecurityViolationInfo),
127    Mailbox(u64),
128}
129
130impl UpcallInfo {
131    /// The number of upcall info variants
132    pub const NR_UPCALLS: usize = 5;
133    /// Get the number associated with this variant
134    pub fn number(&self) -> usize {
135        match self {
136            UpcallInfo::Exception(_) => 0,
137            UpcallInfo::ObjectMemoryFault(_) => 1,
138            UpcallInfo::MemoryContextViolation(_) => 2,
139            UpcallInfo::SecurityViolation(_) => 3,
140            UpcallInfo::Mailbox(_) => 4,
141        }
142    }
143}
144
145/// A collection of data about this upcall, and the [UpcallInfo] for this
146/// particular upcall.
147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
148#[repr(C)]
149pub struct UpcallData {
150    /// Info for this upcall, including reason and elaboration data.
151    pub info: UpcallInfo,
152    /// Upcall flags
153    pub flags: UpcallHandlerFlags,
154    /// Source context
155    pub source_ctx: ObjID,
156    /// The thread ID for this thread.
157    pub thread_id: ObjID,
158}
159
160/// Information for handling an upcall, per-thread. By default, a thread starts with
161/// all these fields initialized to zero, and the mode set to [UpcallMode::Abort].
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163#[repr(C)]
164pub struct UpcallTarget {
165    /// Address to jump to when handling via a call to the same context.
166    pub self_address: usize,
167    /// Address to jump to when handling via a call to supervisor context.
168    pub super_address: usize,
169    /// Address of supervisor stack to use, when switching to supervisor context.
170    pub super_stack: usize,
171    /// Size of the super stack.
172    pub super_stack_size: usize,
173    /// Value to use for stack pointer, when switching to supervisor context.
174    pub super_thread_ptr: usize,
175    /// Supervisor context to use, when switching to supervisor context.
176    pub super_ctx: ObjID,
177    /// Self context to use, when handling upcalls to self.
178    pub self_ctx: ObjID,
179    /// Per-upcall options.
180    pub options: [UpcallOptions; UpcallInfo::NR_UPCALLS],
181}
182
183impl UpcallTarget {
184    /// Construct a new upcall target.
185    pub fn new(
186        self_address: Option<
187            unsafe extern "C-unwind" fn(*mut core::ffi::c_void, *const core::ffi::c_void) -> !,
188        >,
189        super_address: Option<
190            unsafe extern "C-unwind" fn(*mut core::ffi::c_void, *const core::ffi::c_void) -> !,
191        >,
192        super_stack: usize,
193        super_stack_size: usize,
194        super_thread_ptr: usize,
195        super_ctx: ObjID,
196        self_ctx: ObjID,
197        options: [UpcallOptions; UpcallInfo::NR_UPCALLS],
198    ) -> Self {
199        Self {
200            self_address: self_address.map(|addr| addr as usize).unwrap_or_default(),
201            super_address: super_address.map(|addr| addr as usize).unwrap_or_default(),
202            super_stack,
203            super_thread_ptr,
204            super_ctx,
205            self_ctx,
206            options,
207            super_stack_size,
208        }
209    }
210}
211
212/// The exit code the kernel will use when killing a thread that cannot handle
213/// an upcall (e.g. the kernel fails to push the upcall stack frame, or the mode is set
214/// to [UpcallMode::Abort]).
215pub const UPCALL_EXIT_CODE: u64 = 127;
216
217bitflags::bitflags! {
218    /// Flags controlling upcall handling.
219#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
220pub struct UpcallFlags : u8 {
221    /// Whether or not to suspend the thread before handling (or aborting from) the upcall.
222    const SUSPEND = 1;
223}
224}
225
226bitflags::bitflags! {
227    /// Flags passed to the upcall handler in [UpcallData].
228#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
229pub struct UpcallHandlerFlags : u8 {
230    /// Whether or not to suspend the thread before handling (or aborting from) the upcall.
231    const SWITCHED_CONTEXT = 1;
232}
233}
234
235/// Possible modes for upcall handling.
236#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
237pub enum UpcallMode {
238    /// Handle this upcall by immediate abort. If the SUSPEND flag is set, the thread will
239    /// still abort when unsuspended.
240    Abort,
241    /// Handle this upcall by calling, without trying to transfer to supervisor context. Upcall
242    /// data, including frame data, will be placed on the current stack, and the thread pointer
243    /// is unchanged.
244    CallSelf,
245    /// Handle this upcall by calling into supervisor context. If the thread is already in
246    /// supervisor context, this acts like [UpcallMode::CallSelf]. Otherwise, the thread's stack
247    /// and thread pointer are updated to the super_stack and super_thread_pointer values in the
248    /// upcall target respectively, and the active security context is switched to the supervisor
249    /// context (super_ctx).
250    CallSuper,
251}
252
253/// Options for a single upcall.
254#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
255pub struct UpcallOptions {
256    /// Flags for the upcall.
257    pub flags: UpcallFlags,
258    /// The mode for the upcall.
259    pub mode: UpcallMode,
260}
261
262bitflags! {
263    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
264    pub struct ResumeFlags: u64 {
265        /// Suspend the thread during resume.
266        const SUSPEND = 1;
267    }
268}
269
270pub fn set_self_upcall_ptr(
271    target: unsafe extern "C-unwind" fn(*mut UpcallFrame, *const UpcallData) -> !,
272) -> Result<(), TwzError> {
273    let mut upcall = sys_thread_get_upcall()?;
274    upcall.self_address = target as usize;
275    sys_thread_set_upcall(upcall)?;
276    Ok(())
277}