Skip to main content

kernel/
syscall.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Mechanisms for handling and defining system calls.
6//!
7//! # System Call Overview
8//!
9//! Tock supports six system calls. The `allow_readonly`, `allow_readwrite`,
10//! `subscribe`, `yield`, and `memop` system calls are handled by the core
11//! kernel, while `command` is implemented by drivers. The main system calls:
12//!
13//! - `subscribe` passes a upcall to the driver which it can invoke on the
14//!   process later, when an event has occurred or data of interest is
15//!   available.
16//! - `command` tells the driver to do something immediately.
17//! - `allow_readwrite` provides the driver read-write access to an application
18//!   buffer.
19//! - `allow_userspace_readable` provides the driver read-write access to an
20//!   application buffer that is still shared with the app.
21//! - `allow_readonly` provides the driver read-only access to an application
22//!   buffer.
23//!
24//! ## Mapping system-calls to drivers
25//!
26//! Each of these three system calls takes at least two parameters. The first is
27//! a _driver identifier_ and tells the scheduler which driver to forward the
28//! system call to. The second parameters is a __syscall number_ and is used by
29//! the driver to differentiate instances of the call with different
30//! driver-specific meanings (e.g. `subscribe` for "data received" vs
31//! `subscribe` for "send completed"). The mapping between _driver identifiers_
32//! and drivers is determined by a particular platform, while the _syscall
33//! number_ is driver-specific.
34//!
35//! One convention in Tock is that _driver minor number_ 0 for the `command`
36//! syscall can always be used to determine if the driver is supported by the
37//! running kernel by checking the return code. If the return value is greater
38//! than or equal to zero then the driver is present. Typically this is
39//! implemented by a null command that only returns 0, but in some cases the
40//! command can also return more information, like the number of supported
41//! devices (useful for things like the number of LEDs).
42//!
43//! # The `yield` system call class
44//!
45//! While drivers do not handle `yield` system calls, it is important to
46//! understand them and how they interact with `subscribe`, which registers
47//! upcall functions with the kernel. When a process calls a `yield` system
48//! call, the kernel checks if there are any pending upcalls for the process. If
49//! there are pending upcalls, it pushes one upcall onto the process stack. If
50//! there are no pending upcalls, `yield-wait` will cause the process to sleep
51//! until a upcall is triggered, while `yield-no-wait` returns immediately.
52//!
53//! # Method result types
54//!
55//! Each driver method has a limited set of valid return types. Every method has
56//! a single return type corresponding to success and a single return type
57//! corresponding to failure. For the `subscribe` and `allow` system calls,
58//! these return types are the same for every instance of those calls. Each
59//! instance of the `command` system call, however, has its own specified return
60//! types. A command that requests a timestamp, for example, might return a
61//! 32-bit number on success and an error code on failure, while a command that
62//! requests time of day in microsecond granularity might return a 64-bit number
63//! and a 32-bit timezone encoding on success, and an error code on failure.
64//!
65//! These result types are represented as safe Rust types. The core kernel (the
66//! scheduler and syscall dispatcher) is responsible for encoding these types
67//! into the Tock system call ABI specification.
68
69use core::fmt::Write;
70
71use crate::errorcode::ErrorCode;
72use crate::process;
73use crate::utilities::capability_ptr::CapabilityPtr;
74use crate::utilities::machine_register::MachineRegister;
75
76pub use crate::syscall_driver::{CommandReturn, SyscallDriver};
77
78// ---------- SYSTEMCALL ARGUMENT DECODING ----------
79
80/// Enumeration of the system call classes based on the identifiers specified in
81/// the Tock ABI.
82///
83/// These are encoded as 8 bit values as on some architectures the value can be
84/// encoded in the instruction itself.
85#[repr(u8)]
86#[derive(Copy, Clone, Debug)]
87pub enum SyscallClass {
88    Yield = 0,
89    Subscribe = 1,
90    Command = 2,
91    ReadWriteAllow = 3,
92    ReadOnlyAllow = 4,
93    Memop = 5,
94    Exit = 6,
95    UserspaceReadableAllow = 7,
96}
97
98// Required as long as no solution to
99// https://github.com/rust-lang/rfcs/issues/2783 is integrated into
100// the standard library.
101impl TryFrom<u8> for SyscallClass {
102    type Error = u8;
103
104    fn try_from(syscall_class_id: u8) -> Result<SyscallClass, u8> {
105        match syscall_class_id {
106            0 => Ok(SyscallClass::Yield),
107            1 => Ok(SyscallClass::Subscribe),
108            2 => Ok(SyscallClass::Command),
109            3 => Ok(SyscallClass::ReadWriteAllow),
110            4 => Ok(SyscallClass::ReadOnlyAllow),
111            5 => Ok(SyscallClass::Memop),
112            6 => Ok(SyscallClass::Exit),
113            7 => Ok(SyscallClass::UserspaceReadableAllow),
114            i => Err(i),
115        }
116    }
117}
118
119/// Enumeration of the yield system calls based on the Yield identifier
120/// values specified in the Tock ABI.
121#[derive(Copy, Clone, Debug, PartialEq)]
122pub enum YieldVariant {
123    NoWait {
124        ptr: *mut u8,
125    },
126    Wait,
127    WaitFor {
128        driver_number: usize,
129        subdriver_number: usize,
130    },
131}
132
133impl core::fmt::Display for YieldVariant {
134    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135        let name = match self {
136            YieldVariant::NoWait { ptr: _ } => "NoWait",
137            YieldVariant::Wait => "Wait",
138            YieldVariant::WaitFor {
139                driver_number: _,
140                subdriver_number: _,
141            } => "WaitFor",
142        };
143
144        write!(f, "{}", name)
145    }
146}
147
148/// Decoded system calls as defined in TRD104.
149#[derive(Copy, Clone, Debug, PartialEq)]
150pub enum Syscall {
151    /// Structure representing an invocation of the [`SyscallClass::Yield`]
152    /// system call class.
153    Yield {
154        /// The yield variant.
155        yield_type: YieldVariant,
156    },
157
158    /// Structure representing an invocation of the Subscribe system call class.
159    Subscribe {
160        /// The driver identifier.
161        driver_number: usize,
162        /// The subscribe identifier.
163        subdriver_number: usize,
164        /// Upcall pointer to the upcall function.
165        upcall_ptr: CapabilityPtr,
166        /// Userspace application data.
167        appdata: MachineRegister,
168    },
169
170    /// Structure representing an invocation of the Command system call class.
171    Command {
172        /// The driver identifier.
173        driver_number: usize,
174        /// The command identifier.
175        subdriver_number: usize,
176        /// Value passed to the `Command` implementation.
177        arg0: usize,
178        /// Value passed to the `Command` implementation.
179        arg1: usize,
180    },
181
182    /// Structure representing an invocation of the ReadWriteAllow system call
183    /// class.
184    ReadWriteAllow {
185        /// The driver identifier.
186        driver_number: usize,
187        /// The buffer identifier.
188        subdriver_number: usize,
189        /// The address where the buffer starts.
190        allow_address: *mut u8,
191        /// The size of the buffer in bytes.
192        allow_size: usize,
193    },
194
195    /// Structure representing an invocation of the UserspaceReadableAllow
196    /// system call class that allows shared kernel and app access.
197    UserspaceReadableAllow {
198        /// The driver identifier.
199        driver_number: usize,
200        /// The buffer identifier.
201        subdriver_number: usize,
202        /// The address where the buffer starts.
203        allow_address: *mut u8,
204        /// The size of the buffer in bytes.
205        allow_size: usize,
206    },
207
208    /// Structure representing an invocation of the ReadOnlyAllow system call
209    /// class.
210    ReadOnlyAllow {
211        /// The driver identifier.
212        driver_number: usize,
213        /// The buffer identifier.
214        subdriver_number: usize,
215        /// The address where the buffer starts.
216        allow_address: *const u8,
217        /// The size of the buffer in bytes.
218        allow_size: usize,
219    },
220
221    /// Structure representing an invocation of the Memop system call class.
222    Memop {
223        /// The operation.
224        operand: usize,
225        /// The operation argument.
226        arg0: usize,
227    },
228
229    /// Structure representing an invocation of the Exit system call class.
230    Exit {
231        /// The exit identifier.
232        which: usize,
233        /// The completion code passed into the kernel.
234        completion_code: usize,
235    },
236}
237
238impl Syscall {
239    /// Get the `driver_number` for the syscall classes that use driver numbers.
240    pub fn driver_number(&self) -> Option<usize> {
241        match *self {
242            Syscall::Subscribe {
243                driver_number,
244                subdriver_number: _,
245                upcall_ptr: _,
246                appdata: _,
247            } => Some(driver_number),
248            Syscall::Command {
249                driver_number,
250                subdriver_number: _,
251                arg0: _,
252                arg1: _,
253            } => Some(driver_number),
254            Syscall::ReadWriteAllow {
255                driver_number,
256                subdriver_number: _,
257                allow_address: _,
258                allow_size: _,
259            } => Some(driver_number),
260            Syscall::UserspaceReadableAllow {
261                driver_number,
262                subdriver_number: _,
263                allow_address: _,
264                allow_size: _,
265            } => Some(driver_number),
266            Syscall::ReadOnlyAllow {
267                driver_number,
268                subdriver_number: _,
269                allow_address: _,
270                allow_size: _,
271            } => Some(driver_number),
272            _ => None,
273        }
274    }
275
276    /// Get the `subdriver_number` for the syscall classes that use sub driver
277    /// numbers.
278    pub fn subdriver_number(&self) -> Option<usize> {
279        match *self {
280            Syscall::Subscribe {
281                driver_number: _,
282                subdriver_number,
283                upcall_ptr: _,
284                appdata: _,
285            } => Some(subdriver_number),
286            Syscall::Command {
287                driver_number: _,
288                subdriver_number,
289                arg0: _,
290                arg1: _,
291            } => Some(subdriver_number),
292            Syscall::ReadWriteAllow {
293                driver_number: _,
294                subdriver_number,
295                allow_address: _,
296                allow_size: _,
297            } => Some(subdriver_number),
298            Syscall::UserspaceReadableAllow {
299                driver_number: _,
300                subdriver_number,
301                allow_address: _,
302                allow_size: _,
303            } => Some(subdriver_number),
304            Syscall::ReadOnlyAllow {
305                driver_number: _,
306                subdriver_number,
307                allow_address: _,
308                allow_size: _,
309            } => Some(subdriver_number),
310            _ => None,
311        }
312    }
313}
314
315// ---------- SYSCALL RETURN VALUES ----------
316
317/// Enumeration of the possible system call return variants.
318///
319/// This struct operates over primitive types such as integers of fixed length
320/// and pointers. It is constructed by the scheduler and passed down to the
321/// architecture to be encoded into registers. Architectures may use the various
322/// helper functions defined in
323/// [`utilities::arch_helpers`](crate::utilities::arch_helpers).
324///
325/// Capsules do not use this struct. Capsules use higher level Rust types (e.g.
326/// [`ReadWriteProcessBuffer`](crate::processbuffer::ReadWriteProcessBuffer) and
327/// [`GrantKernelData`](crate::grant::GrantKernelData)) or wrappers around this
328/// struct ([`CommandReturn`]) which limit the available constructors to safely
329/// constructable variants.
330#[derive(Copy, Clone, Debug)]
331pub enum SyscallReturn {
332    /// Generic error case
333    Failure(ErrorCode),
334    /// Generic error case, with an additional 32-bit data field
335    FailureU32(ErrorCode, u32),
336    /// Generic error case, with two additional 32-bit data fields
337    FailureU32U32(ErrorCode, u32, u32),
338    /// Generic error case, with an additional 64-bit data field
339    FailureU64(ErrorCode, u64),
340    /// Generic success case
341    Success,
342    /// Generic success case, with an additional 32-bit data field
343    SuccessU32(u32),
344    /// Generic success case, with two additional 32-bit data fields
345    SuccessU32U32(u32, u32),
346    /// Generic success case, with three additional 32-bit data fields
347    SuccessU32U32U32(u32, u32, u32),
348    /// Generic success case, with an additional 64-bit data field
349    SuccessU64(u64),
350    /// Generic success case, with an additional 32-bit and 64-bit data field
351    SuccessU32U64(u32, u64),
352
353    /// Generic success case with an additional address-sized value
354    /// that does not impute access permissions to the process.
355    SuccessAddr(usize),
356
357    /// Generic success case, with an additional pointer.
358    /// This pointer is provenance bearing and implies access
359    /// permission to the process.
360    SuccessPtr(CapabilityPtr),
361
362    // These following types are used by the scheduler so that it can return
363    // values to userspace in an architecture (pointer-width) independent way.
364    // The kernel passes these types (rather than ProcessBuffer or Upcall) for
365    // two reasons. First, since the kernel/scheduler makes promises about the
366    // lifetime and safety of these types, it does not want to leak them to
367    // other code. Second, if subscribe or allow calls pass invalid values
368    // (pointers out of valid memory), the kernel cannot construct an
369    // ProcessBuffer or Upcall type but needs to be able to return a failure.
370    // -pal 11/24/20
371
372    // FIXME: We need to think about what these look like on CHERI
373    // Really, things that were capabilities should come back as capabilities.
374    // However, we discarded all capability information at the syscall boundary.
375    // We could always use our own DDC, with just the permissions and length implied by the
376    // specific syscall. This would certainly got give userspace _extra_ authority,
377    // but might rob them of some bounds / permissions. This is what is implemented currently.
378    // Preferable behavior is not to discard the capability so early (it should make it as far
379    // as grant is stored in grant allow slots)
380    /// Read/Write allow success case, returns the previous allowed buffer and
381    /// size to the process.
382    AllowReadWriteSuccess(*mut u8, usize),
383    /// Read/Write allow failure case, returns the passed allowed buffer and
384    /// size to the process.
385    AllowReadWriteFailure(ErrorCode, *mut u8, usize),
386
387    /// Shared Read/Write allow success case, returns the previous allowed
388    /// buffer and size to the process.
389    UserspaceReadableAllowSuccess(*mut u8, usize),
390    /// Shared Read/Write allow failure case, returns the passed allowed buffer
391    /// and size to the process.
392    UserspaceReadableAllowFailure(ErrorCode, *mut u8, usize),
393
394    /// Read only allow success case, returns the previous allowed buffer and
395    /// size to the process.
396    AllowReadOnlySuccess(*const u8, usize),
397    /// Read only allow failure case, returns the passed allowed buffer and size
398    /// to the process.
399    AllowReadOnlyFailure(ErrorCode, *const u8, usize),
400
401    /// Subscribe success case, returns the previous upcall function pointer and
402    /// application data.
403    SubscribeSuccess(*const (), usize),
404    /// Subscribe failure case, returns the passed upcall function pointer and
405    /// application data.
406    SubscribeFailure(ErrorCode, *const (), usize),
407
408    /// Yield-WaitFor return value. These arguments match the arguments to an
409    /// upcall, where the kernel does not define an error field. Therefore this
410    /// does not have success/failure versions because the kernel cannot know if
411    /// the upcall (i.e. Yield-WaitFor return value) represents success or
412    /// failure.
413    YieldWaitFor(usize, usize, usize),
414}
415
416impl SyscallReturn {
417    /// Transforms a [`CommandReturn`], which is wrapper around a subset of
418    /// [`SyscallReturn`], into a [`SyscallReturn`].
419    ///
420    /// This allows [`CommandReturn`] to include only the variants of
421    /// [`SyscallReturn`] that can be returned from a Command, while having an
422    /// inexpensive way to handle it as a [`SyscallReturn`] for more generic
423    /// code paths.
424    pub(crate) fn from_command_return(res: CommandReturn) -> Self {
425        res.into_inner()
426    }
427
428    /// Returns true if the [`SyscallReturn`] is any success type.
429    pub(crate) fn is_success(&self) -> bool {
430        match self {
431            SyscallReturn::Success => true,
432            SyscallReturn::SuccessU32(_) => true,
433            SyscallReturn::SuccessU32U32(_, _) => true,
434            SyscallReturn::SuccessU32U32U32(_, _, _) => true,
435            SyscallReturn::SuccessU64(_) => true,
436            SyscallReturn::SuccessU32U64(_, _) => true,
437            SyscallReturn::SuccessAddr(_) => true,
438            SyscallReturn::SuccessPtr(_) => true,
439            SyscallReturn::AllowReadWriteSuccess(_, _) => true,
440            SyscallReturn::UserspaceReadableAllowSuccess(_, _) => true,
441            SyscallReturn::AllowReadOnlySuccess(_, _) => true,
442            SyscallReturn::SubscribeSuccess(_, _) => true,
443            SyscallReturn::Failure(_) => false,
444            SyscallReturn::FailureU32(_, _) => false,
445            SyscallReturn::FailureU32U32(_, _, _) => false,
446            SyscallReturn::FailureU64(_, _) => false,
447            SyscallReturn::AllowReadWriteFailure(_, _, _) => false,
448            SyscallReturn::UserspaceReadableAllowFailure(_, _, _) => false,
449            SyscallReturn::AllowReadOnlyFailure(_, _, _) => false,
450            SyscallReturn::SubscribeFailure(_, _, _) => false,
451            SyscallReturn::YieldWaitFor(_, _, _) => true,
452        }
453    }
454}
455
456// ---------- USERSPACE KERNEL BOUNDARY ----------
457
458/// [`ContextSwitchReason`] specifies why the process stopped executing and
459/// execution returned to the kernel.
460#[derive(PartialEq, Copy, Clone)]
461pub enum ContextSwitchReason {
462    /// Process called a syscall. Also returns the syscall and relevant values.
463    SyscallFired { syscall: Syscall },
464    /// Process triggered the hardfault handler. The implementation should still
465    /// save registers in the event that the platform can handle the fault and
466    /// allow the app to continue running. For more details on this see
467    /// [`ProcessFault`](crate::platform::ProcessFault).
468    Fault,
469    /// Process was interrupted (e.g. by a hardware event).
470    Interrupted,
471}
472
473/// The [`UserspaceKernelBoundary`] trait is implemented by the
474/// architectural component of the chip implementation of Tock.
475///
476/// This trait allows the kernel to switch to and from processes in an
477/// architecture-independent manner.
478///
479/// Exactly how upcalls and return values are passed between kernelspace and
480/// userspace is architecture specific. The architecture may use process memory
481/// to store state when switching. Therefore, functions in this trait are passed
482/// the bounds of process-accessible memory so that the architecture
483/// implementation can verify it is reading and writing memory that the process
484/// has valid access to. These bounds are passed through
485/// `accessible_memory_start` and `app_brk` pointers.
486pub trait UserspaceKernelBoundary {
487    /// Some architecture-specific struct containing per-process state that must
488    /// be kept while the process is not running. For example, for keeping CPU
489    /// registers that aren't stored on the stack.
490    ///
491    /// Implementations should **not** rely on the [`Default`] constructor
492    /// (custom or derived) for any initialization of a process's stored state.
493    /// The initialization must happen in the
494    /// [`initialize_process()`](UserspaceKernelBoundary::initialize_process())
495    /// function.
496    type StoredState: Default;
497
498    /// Called by the kernel during process creation to inform the kernel of the
499    /// minimum amount of process-accessible RAM needed by a new process. This
500    /// allows for architecture-specific process layout decisions, such as stack
501    /// pointer initialization.
502    ///
503    /// This returns the minimum number of bytes of process-accessible memory
504    /// the kernel must allocate to a process so that a successful context
505    /// switch is possible.
506    ///
507    /// Some architectures may not need any allocated memory, and this should
508    /// return 0. In general, implementations should try to pre-allocate the
509    /// minimal amount of process-accessible memory (i.e. return as close to 0
510    /// as possible) to provide the most flexibility to the process. However,
511    /// the return value will be nonzero for architectures where values are
512    /// passed in memory between kernelspace and userspace during syscalls or a
513    /// stack needs to be setup.
514    fn initial_process_app_brk_size(&self) -> usize;
515
516    /// Called by the kernel after it has memory allocated to it but before it
517    /// is allowed to begin executing. Allows for architecture-specific process
518    /// setup, e.g. allocating a syscall stack frame.
519    ///
520    /// This function must also initialize the stored state (if needed).
521    ///
522    /// The kernel calls this function with the start of memory allocated to the
523    /// process by providing `accessible_memory_start`. It also provides the
524    /// `app_brk` pointer which marks the end of process-accessible memory. The
525    /// kernel guarantees that `accessible_memory_start` will be word-aligned.
526    ///
527    /// If successful, this function returns `Ok()`. If the process syscall
528    /// state cannot be initialized with the available amount of memory, or for
529    /// any other reason, it should return `Err()`.
530    ///
531    /// This function may be called multiple times on the same process. For
532    /// example, if a process crashes and is to be restarted, this must be
533    /// called. Or if the process is moved this may need to be called.
534    ///
535    /// # Safety
536    ///
537    /// This function guarantees that it if needs to change process memory, it
538    /// will only change memory starting at `accessible_memory_start` and before
539    /// `app_brk`. The caller is responsible for guaranteeing that those
540    /// pointers are valid for the process.
541    unsafe fn initialize_process(
542        &self,
543        accessible_memory_start: *const u8,
544        app_brk: *const u8,
545        state: &mut Self::StoredState,
546    ) -> Result<(), ()>;
547
548    /// Set the return value the process should see when it begins executing
549    /// again after the syscall. This will only be called after a process has
550    /// called a syscall.
551    ///
552    /// The process to set the return value for is specified by the `state`
553    /// value. The `return_value` is the value that should be passed to the
554    /// process so that when it resumes executing it knows the return value of
555    /// the syscall it called.
556    ///
557    /// # Safety
558    ///
559    /// This function guarantees that it if needs to change process memory, it
560    /// will only change memory starting at `accessible_memory_start` and before
561    /// `app_brk`. The caller is responsible for guaranteeing that those
562    /// pointers are valid for the process.
563    unsafe fn set_syscall_return_value(
564        &self,
565        accessible_memory_start: *const u8,
566        app_brk: *const u8,
567        state: &mut Self::StoredState,
568        return_value: SyscallReturn,
569    ) -> Result<(), ()>;
570
571    /// Set the function that the process should execute when it is resumed.
572    /// This has two major uses: 1) sets up the initial function call to
573    /// `_start` when the process is started for the very first time; 2) tells
574    /// the process to execute a upcall function after calling `yield()`.
575    ///
576    /// **Note:** This method cannot be called in conjunction with
577    /// `set_syscall_return_value`, as the injected function will clobber the
578    /// return value.
579    ///
580    /// ### Arguments
581    ///
582    /// - `accessible_memory_start` is the address of the start of the
583    ///   process-accessible memory region for this process.
584    /// - `app_brk` is the address of the current process break. This marks the
585    ///   end of the memory region the process has access to. Note, this is not
586    ///   the end of the entire memory region allocated to the process. Some
587    ///   memory above this address is still allocated for the process, but if
588    ///   the process tries to access it an MPU fault will occur.
589    /// - `state` is the stored state for this process.
590    /// - `upcall` is the function that should be executed when the process
591    ///   resumes.
592    ///
593    /// ### Return
594    ///
595    /// Returns `Ok(())` if the function was successfully enqueued for the
596    /// process. Returns `Err(())` if the function was not, likely because there
597    /// is insufficient memory available to do so.
598    ///
599    /// # Safety
600    ///
601    /// This function guarantees that it if needs to change process memory, it
602    /// will only change memory starting at `accessible_memory_start` and before
603    /// `app_brk`. The caller is responsible for guaranteeing that those
604    /// pointers are valid for the process.
605    unsafe fn set_process_function(
606        &self,
607        accessible_memory_start: *const u8,
608        app_brk: *const u8,
609        state: &mut Self::StoredState,
610        upcall: process::FunctionCall,
611    ) -> Result<(), ()>;
612
613    /// Context switch to a specific process.
614    ///
615    /// This returns two values in a tuple.
616    ///
617    /// 1. A [`ContextSwitchReason`] indicating why the process stopped
618    ///    executing and switched back to the kernel.
619    /// 2. Optionally, the current stack pointer used by the process. This is
620    ///    optional because it is only for debugging in process.rs. By sharing
621    ///    the process's stack pointer with process.rs users can inspect the
622    ///    state and see the stack depth, which might be useful for debugging.
623    ///
624    /// # Safety
625    ///
626    /// This function guarantees that it if needs to change process memory, it
627    /// will only change memory starting at `accessible_memory_start` and before
628    /// `app_brk`. The caller is responsible for guaranteeing that those
629    /// pointers are valid for the process.
630    unsafe fn switch_to_process(
631        &self,
632        accessible_memory_start: *const u8,
633        app_brk: *const u8,
634        state: &mut Self::StoredState,
635    ) -> (ContextSwitchReason, Option<*const u8>);
636
637    /// Display architecture specific (e.g. CPU registers or status flags) data
638    /// for a process identified by the stored state for that process.
639    ///
640    /// # Safety
641    ///
642    /// This function guarantees that it if needs to change process memory, it
643    /// will only change memory starting at `accessible_memory_start` and before
644    /// `app_brk`. The caller is responsible for guaranteeing that those
645    /// pointers are valid for the process.
646    unsafe fn print_context(
647        &self,
648        accessible_memory_start: *const u8,
649        app_brk: *const u8,
650        state: &Self::StoredState,
651        writer: &mut dyn Write,
652    );
653
654    /// Store architecture specific (e.g. CPU registers or status flags) data
655    /// for a process. On success returns the number of elements written to out.
656    fn store_context(&self, state: &Self::StoredState, out: &mut [u8]) -> Result<usize, ErrorCode>;
657}