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