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/// Enumeration of the yield system calls based on the Yield identifier
99/// values specified in the Tock ABI.
100#[derive(Copy, Clone, Debug)]
101pub enum YieldCall {
102    NoWait = 0,
103    Wait = 1,
104    WaitFor = 2,
105}
106
107impl TryFrom<usize> for YieldCall {
108    type Error = usize;
109
110    fn try_from(yield_variant: usize) -> Result<YieldCall, usize> {
111        match yield_variant {
112            0 => Ok(YieldCall::NoWait),
113            1 => Ok(YieldCall::Wait),
114            2 => Ok(YieldCall::WaitFor),
115            i => Err(i),
116        }
117    }
118}
119
120// Required as long as no solution to
121// https://github.com/rust-lang/rfcs/issues/2783 is integrated into
122// the standard library.
123impl TryFrom<u8> for SyscallClass {
124    type Error = u8;
125
126    fn try_from(syscall_class_id: u8) -> Result<SyscallClass, u8> {
127        match syscall_class_id {
128            0 => Ok(SyscallClass::Yield),
129            1 => Ok(SyscallClass::Subscribe),
130            2 => Ok(SyscallClass::Command),
131            3 => Ok(SyscallClass::ReadWriteAllow),
132            4 => Ok(SyscallClass::ReadOnlyAllow),
133            5 => Ok(SyscallClass::Memop),
134            6 => Ok(SyscallClass::Exit),
135            7 => Ok(SyscallClass::UserspaceReadableAllow),
136            i => Err(i),
137        }
138    }
139}
140
141/// Decoded system calls as defined in TRD104.
142#[derive(Copy, Clone, Debug, PartialEq)]
143pub enum Syscall {
144    /// Structure representing an invocation of the [`SyscallClass::Yield`]
145    /// system call class. `which` is the Yield identifier value and `address`
146    /// is the no wait field.
147    Yield {
148        which: usize,
149        param_a: usize,
150        param_b: usize,
151    },
152
153    /// Structure representing an invocation of the Subscribe system call class.
154    Subscribe {
155        /// The driver identifier.
156        driver_number: usize,
157        /// The subscribe identifier.
158        subdriver_number: usize,
159        /// Upcall pointer to the upcall function.
160        upcall_ptr: CapabilityPtr,
161        /// Userspace application data.
162        appdata: MachineRegister,
163    },
164
165    /// Structure representing an invocation of the Command system call class.
166    Command {
167        /// The driver identifier.
168        driver_number: usize,
169        /// The command identifier.
170        subdriver_number: usize,
171        /// Value passed to the `Command` implementation.
172        arg0: usize,
173        /// Value passed to the `Command` implementation.
174        arg1: usize,
175    },
176
177    /// Structure representing an invocation of the ReadWriteAllow system call
178    /// class.
179    ReadWriteAllow {
180        /// The driver identifier.
181        driver_number: usize,
182        /// The buffer identifier.
183        subdriver_number: usize,
184        /// The address where the buffer starts.
185        allow_address: *mut u8,
186        /// The size of the buffer in bytes.
187        allow_size: usize,
188    },
189
190    /// Structure representing an invocation of the UserspaceReadableAllow
191    /// system call class that allows shared kernel and app access.
192    UserspaceReadableAllow {
193        /// The driver identifier.
194        driver_number: usize,
195        /// The buffer identifier.
196        subdriver_number: usize,
197        /// The address where the buffer starts.
198        allow_address: *mut u8,
199        /// The size of the buffer in bytes.
200        allow_size: usize,
201    },
202
203    /// Structure representing an invocation of the ReadOnlyAllow system call
204    /// class.
205    ReadOnlyAllow {
206        /// The driver identifier.
207        driver_number: usize,
208        /// The buffer identifier.
209        subdriver_number: usize,
210        /// The address where the buffer starts.
211        allow_address: *const u8,
212        /// The size of the buffer in bytes.
213        allow_size: usize,
214    },
215
216    /// Structure representing an invocation of the Memop system call class.
217    Memop {
218        /// The operation.
219        operand: usize,
220        /// The operation argument.
221        arg0: usize,
222    },
223
224    /// Structure representing an invocation of the Exit system call class.
225    Exit {
226        /// The exit identifier.
227        which: usize,
228        /// The completion code passed into the kernel.
229        completion_code: usize,
230    },
231}
232
233impl Syscall {
234    /// Helper function for converting raw values passed back from an
235    /// application into a `Syscall` type in Tock, representing an typed version
236    /// of a system call invocation. The method returns None if the values do
237    /// not specify a valid system call.
238    ///
239    /// Different architectures have different ABIs for a process and the kernel
240    /// to exchange data. The 32-bit ABI for CortexM and RISCV microcontrollers
241    /// is specified in TRD104.
242    pub fn from_register_arguments(
243        syscall_number: u8,
244        r0: usize,
245        r1: MachineRegister,
246        r2: MachineRegister,
247        r3: MachineRegister,
248    ) -> Option<Syscall> {
249        match SyscallClass::try_from(syscall_number) {
250            Ok(SyscallClass::Yield) => Some(Syscall::Yield {
251                which: r0,
252                param_a: r1.as_usize(),
253                param_b: r2.as_usize(),
254            }),
255            Ok(SyscallClass::Subscribe) => Some(Syscall::Subscribe {
256                driver_number: r0,
257                subdriver_number: r1.as_usize(),
258                upcall_ptr: r2.as_capability_ptr(),
259                appdata: r3,
260            }),
261            Ok(SyscallClass::Command) => Some(Syscall::Command {
262                driver_number: r0,
263                subdriver_number: r1.as_usize(),
264                arg0: r2.as_usize(),
265                arg1: r3.as_usize(),
266            }),
267            Ok(SyscallClass::ReadWriteAllow) => Some(Syscall::ReadWriteAllow {
268                driver_number: r0,
269                subdriver_number: r1.as_usize(),
270                allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
271                allow_size: r3.as_usize(),
272            }),
273            Ok(SyscallClass::UserspaceReadableAllow) => Some(Syscall::UserspaceReadableAllow {
274                driver_number: r0,
275                subdriver_number: r1.as_usize(),
276                allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
277                allow_size: r3.as_usize(),
278            }),
279            Ok(SyscallClass::ReadOnlyAllow) => Some(Syscall::ReadOnlyAllow {
280                driver_number: r0,
281                subdriver_number: r1.as_usize(),
282                allow_address: r2.as_capability_ptr().as_ptr(),
283                allow_size: r3.as_usize(),
284            }),
285            Ok(SyscallClass::Memop) => Some(Syscall::Memop {
286                operand: r0,
287                arg0: r1.as_usize(),
288            }),
289            Ok(SyscallClass::Exit) => Some(Syscall::Exit {
290                which: r0,
291                completion_code: r1.as_usize(),
292            }),
293            Err(_) => None,
294        }
295    }
296
297    /// Get the `driver_number` for the syscall classes that use driver numbers.
298    pub fn driver_number(&self) -> Option<usize> {
299        match *self {
300            Syscall::Subscribe {
301                driver_number,
302                subdriver_number: _,
303                upcall_ptr: _,
304                appdata: _,
305            } => Some(driver_number),
306            Syscall::Command {
307                driver_number,
308                subdriver_number: _,
309                arg0: _,
310                arg1: _,
311            } => Some(driver_number),
312            Syscall::ReadWriteAllow {
313                driver_number,
314                subdriver_number: _,
315                allow_address: _,
316                allow_size: _,
317            } => Some(driver_number),
318            Syscall::UserspaceReadableAllow {
319                driver_number,
320                subdriver_number: _,
321                allow_address: _,
322                allow_size: _,
323            } => Some(driver_number),
324            Syscall::ReadOnlyAllow {
325                driver_number,
326                subdriver_number: _,
327                allow_address: _,
328                allow_size: _,
329            } => Some(driver_number),
330            _ => None,
331        }
332    }
333
334    /// Get the `subdriver_number` for the syscall classes that use sub driver
335    /// numbers.
336    pub fn subdriver_number(&self) -> Option<usize> {
337        match *self {
338            Syscall::Subscribe {
339                driver_number: _,
340                subdriver_number,
341                upcall_ptr: _,
342                appdata: _,
343            } => Some(subdriver_number),
344            Syscall::Command {
345                driver_number: _,
346                subdriver_number,
347                arg0: _,
348                arg1: _,
349            } => Some(subdriver_number),
350            Syscall::ReadWriteAllow {
351                driver_number: _,
352                subdriver_number,
353                allow_address: _,
354                allow_size: _,
355            } => Some(subdriver_number),
356            Syscall::UserspaceReadableAllow {
357                driver_number: _,
358                subdriver_number,
359                allow_address: _,
360                allow_size: _,
361            } => Some(subdriver_number),
362            Syscall::ReadOnlyAllow {
363                driver_number: _,
364                subdriver_number,
365                allow_address: _,
366                allow_size: _,
367            } => Some(subdriver_number),
368            _ => None,
369        }
370    }
371}
372
373// ---------- SYSCALL RETURN VALUES ----------
374
375/// Enumeration of the possible system call return variants.
376///
377/// This struct operates over primitive types such as integers of fixed length
378/// and pointers. It is constructed by the scheduler and passed down to the
379/// architecture to be encoded into registers. Architectures may use the various
380/// helper functions defined in
381/// [`utilities::arch_helpers`](crate::utilities::arch_helpers).
382///
383/// Capsules do not use this struct. Capsules use higher level Rust types (e.g.
384/// [`ReadWriteProcessBuffer`](crate::processbuffer::ReadWriteProcessBuffer) and
385/// [`GrantKernelData`](crate::grant::GrantKernelData)) or wrappers around this
386/// struct ([`CommandReturn`]) which limit the available constructors to safely
387/// constructable variants.
388#[derive(Copy, Clone, Debug)]
389pub enum SyscallReturn {
390    /// Generic error case
391    Failure(ErrorCode),
392    /// Generic error case, with an additional 32-bit data field
393    FailureU32(ErrorCode, u32),
394    /// Generic error case, with two additional 32-bit data fields
395    FailureU32U32(ErrorCode, u32, u32),
396    /// Generic error case, with an additional 64-bit data field
397    FailureU64(ErrorCode, u64),
398    /// Generic success case
399    Success,
400    /// Generic success case, with an additional 32-bit data field
401    SuccessU32(u32),
402    /// Generic success case, with two additional 32-bit data fields
403    SuccessU32U32(u32, u32),
404    /// Generic success case, with three additional 32-bit data fields
405    SuccessU32U32U32(u32, u32, u32),
406    /// Generic success case, with an additional 64-bit data field
407    SuccessU64(u64),
408    /// Generic success case, with an additional 32-bit and 64-bit data field
409    SuccessU32U64(u32, u64),
410
411    /// Generic success case with an additional address-sized value
412    /// that does not impute access permissions to the process.
413    SuccessAddr(usize),
414
415    /// Generic success case, with an additional pointer.
416    /// This pointer is provenance bearing and implies access
417    /// permission to the process.
418    SuccessPtr(CapabilityPtr),
419
420    // These following types are used by the scheduler so that it can return
421    // values to userspace in an architecture (pointer-width) independent way.
422    // The kernel passes these types (rather than ProcessBuffer or Upcall) for
423    // two reasons. First, since the kernel/scheduler makes promises about the
424    // lifetime and safety of these types, it does not want to leak them to
425    // other code. Second, if subscribe or allow calls pass invalid values
426    // (pointers out of valid memory), the kernel cannot construct an
427    // ProcessBuffer or Upcall type but needs to be able to return a failure.
428    // -pal 11/24/20
429
430    // FIXME: We need to think about what these look like on CHERI
431    // Really, things that were capabilities should come back as capabilities.
432    // However, we discarded all capability information at the syscall boundary.
433    // We could always use our own DDC, with just the permissions and length implied by the
434    // specific syscall. This would certainly got give userspace _extra_ authority,
435    // but might rob them of some bounds / permissions. This is what is implemented currently.
436    // Preferable behavior is not to discard the capability so early (it should make it as far
437    // as grant is stored in grant allow slots)
438    /// Read/Write allow success case, returns the previous allowed buffer and
439    /// size to the process.
440    AllowReadWriteSuccess(*mut u8, usize),
441    /// Read/Write allow failure case, returns the passed allowed buffer and
442    /// size to the process.
443    AllowReadWriteFailure(ErrorCode, *mut u8, usize),
444
445    /// Shared Read/Write allow success case, returns the previous allowed
446    /// buffer and size to the process.
447    UserspaceReadableAllowSuccess(*mut u8, usize),
448    /// Shared Read/Write allow failure case, returns the passed allowed buffer
449    /// and size to the process.
450    UserspaceReadableAllowFailure(ErrorCode, *mut u8, usize),
451
452    /// Read only allow success case, returns the previous allowed buffer and
453    /// size to the process.
454    AllowReadOnlySuccess(*const u8, usize),
455    /// Read only allow failure case, returns the passed allowed buffer and size
456    /// to the process.
457    AllowReadOnlyFailure(ErrorCode, *const u8, usize),
458
459    /// Subscribe success case, returns the previous upcall function pointer and
460    /// application data.
461    SubscribeSuccess(*const (), usize),
462    /// Subscribe failure case, returns the passed upcall function pointer and
463    /// application data.
464    SubscribeFailure(ErrorCode, *const (), usize),
465
466    /// Yield-WaitFor return value. These arguments match the arguments to an
467    /// upcall, where the kernel does not define an error field. Therefore this
468    /// does not have success/failure versions because the kernel cannot know if
469    /// the upcall (i.e. Yield-WaitFor return value) represents success or
470    /// failure.
471    YieldWaitFor(usize, usize, usize),
472}
473
474impl SyscallReturn {
475    /// Transforms a [`CommandReturn`], which is wrapper around a subset of
476    /// [`SyscallReturn`], into a [`SyscallReturn`].
477    ///
478    /// This allows [`CommandReturn`] to include only the variants of
479    /// [`SyscallReturn`] that can be returned from a Command, while having an
480    /// inexpensive way to handle it as a [`SyscallReturn`] for more generic
481    /// code paths.
482    pub(crate) fn from_command_return(res: CommandReturn) -> Self {
483        res.into_inner()
484    }
485
486    /// Returns true if the [`SyscallReturn`] is any success type.
487    pub(crate) fn is_success(&self) -> bool {
488        match self {
489            SyscallReturn::Success => true,
490            SyscallReturn::SuccessU32(_) => true,
491            SyscallReturn::SuccessU32U32(_, _) => true,
492            SyscallReturn::SuccessU32U32U32(_, _, _) => true,
493            SyscallReturn::SuccessU64(_) => true,
494            SyscallReturn::SuccessU32U64(_, _) => true,
495            SyscallReturn::SuccessAddr(_) => true,
496            SyscallReturn::SuccessPtr(_) => true,
497            SyscallReturn::AllowReadWriteSuccess(_, _) => true,
498            SyscallReturn::UserspaceReadableAllowSuccess(_, _) => true,
499            SyscallReturn::AllowReadOnlySuccess(_, _) => true,
500            SyscallReturn::SubscribeSuccess(_, _) => true,
501            SyscallReturn::Failure(_) => false,
502            SyscallReturn::FailureU32(_, _) => false,
503            SyscallReturn::FailureU32U32(_, _, _) => false,
504            SyscallReturn::FailureU64(_, _) => false,
505            SyscallReturn::AllowReadWriteFailure(_, _, _) => false,
506            SyscallReturn::UserspaceReadableAllowFailure(_, _, _) => false,
507            SyscallReturn::AllowReadOnlyFailure(_, _, _) => false,
508            SyscallReturn::SubscribeFailure(_, _, _) => false,
509            SyscallReturn::YieldWaitFor(_, _, _) => true,
510        }
511    }
512}
513
514// ---------- USERSPACE KERNEL BOUNDARY ----------
515
516/// [`ContextSwitchReason`] specifies why the process stopped executing and
517/// execution returned to the kernel.
518#[derive(PartialEq, Copy, Clone)]
519pub enum ContextSwitchReason {
520    /// Process called a syscall. Also returns the syscall and relevant values.
521    SyscallFired { syscall: Syscall },
522    /// Process triggered the hardfault handler. The implementation should still
523    /// save registers in the event that the platform can handle the fault and
524    /// allow the app to continue running. For more details on this see
525    /// [`ProcessFault`](crate::platform::ProcessFault).
526    Fault,
527    /// Process was interrupted (e.g. by a hardware event).
528    Interrupted,
529}
530
531/// The [`UserspaceKernelBoundary`] trait is implemented by the
532/// architectural component of the chip implementation of Tock.
533///
534/// This trait allows the kernel to switch to and from processes in an
535/// architecture-independent manner.
536///
537/// Exactly how upcalls and return values are passed between kernelspace and
538/// userspace is architecture specific. The architecture may use process memory
539/// to store state when switching. Therefore, functions in this trait are passed
540/// the bounds of process-accessible memory so that the architecture
541/// implementation can verify it is reading and writing memory that the process
542/// has valid access to. These bounds are passed through
543/// `accessible_memory_start` and `app_brk` pointers.
544pub trait UserspaceKernelBoundary {
545    /// Some architecture-specific struct containing per-process state that must
546    /// be kept while the process is not running. For example, for keeping CPU
547    /// registers that aren't stored on the stack.
548    ///
549    /// Implementations should **not** rely on the [`Default`] constructor
550    /// (custom or derived) for any initialization of a process's stored state.
551    /// The initialization must happen in the
552    /// [`initialize_process()`](UserspaceKernelBoundary::initialize_process())
553    /// function.
554    type StoredState: Default;
555
556    /// Called by the kernel during process creation to inform the kernel of the
557    /// minimum amount of process-accessible RAM needed by a new process. This
558    /// allows for architecture-specific process layout decisions, such as stack
559    /// pointer initialization.
560    ///
561    /// This returns the minimum number of bytes of process-accessible memory
562    /// the kernel must allocate to a process so that a successful context
563    /// switch is possible.
564    ///
565    /// Some architectures may not need any allocated memory, and this should
566    /// return 0. In general, implementations should try to pre-allocate the
567    /// minimal amount of process-accessible memory (i.e. return as close to 0
568    /// as possible) to provide the most flexibility to the process. However,
569    /// the return value will be nonzero for architectures where values are
570    /// passed in memory between kernelspace and userspace during syscalls or a
571    /// stack needs to be setup.
572    fn initial_process_app_brk_size(&self) -> usize;
573
574    /// Called by the kernel after it has memory allocated to it but before it
575    /// is allowed to begin executing. Allows for architecture-specific process
576    /// setup, e.g. allocating a syscall stack frame.
577    ///
578    /// This function must also initialize the stored state (if needed).
579    ///
580    /// The kernel calls this function with the start of memory allocated to the
581    /// process by providing `accessible_memory_start`. It also provides the
582    /// `app_brk` pointer which marks the end of process-accessible memory. The
583    /// kernel guarantees that `accessible_memory_start` will be word-aligned.
584    ///
585    /// If successful, this function returns `Ok()`. If the process syscall
586    /// state cannot be initialized with the available amount of memory, or for
587    /// any other reason, it should return `Err()`.
588    ///
589    /// This function may be called multiple times on the same process. For
590    /// example, if a process crashes and is to be restarted, this must be
591    /// called. Or if the process is moved this may need to be called.
592    ///
593    /// ### Safety
594    ///
595    /// This function guarantees that it if needs to change process memory, it
596    /// will only change memory starting at `accessible_memory_start` and before
597    /// `app_brk`. The caller is responsible for guaranteeing that those
598    /// pointers are valid for the process.
599    unsafe fn initialize_process(
600        &self,
601        accessible_memory_start: *const u8,
602        app_brk: *const u8,
603        state: &mut Self::StoredState,
604    ) -> Result<(), ()>;
605
606    /// Set the return value the process should see when it begins executing
607    /// again after the syscall. This will only be called after a process has
608    /// called a syscall.
609    ///
610    /// The process to set the return value for is specified by the `state`
611    /// value. The `return_value` is the value that should be passed to the
612    /// process so that when it resumes executing it knows the return value of
613    /// the syscall it called.
614    ///
615    /// ### Safety
616    ///
617    /// This function guarantees that it if needs to change process memory, it
618    /// will only change memory starting at `accessible_memory_start` and before
619    /// `app_brk`. The caller is responsible for guaranteeing that those
620    /// pointers are valid for the process.
621    unsafe fn set_syscall_return_value(
622        &self,
623        accessible_memory_start: *const u8,
624        app_brk: *const u8,
625        state: &mut Self::StoredState,
626        return_value: SyscallReturn,
627    ) -> Result<(), ()>;
628
629    /// Set the function that the process should execute when it is resumed.
630    /// This has two major uses: 1) sets up the initial function call to
631    /// `_start` when the process is started for the very first time; 2) tells
632    /// the process to execute a upcall function after calling `yield()`.
633    ///
634    /// **Note:** This method cannot be called in conjunction with
635    /// `set_syscall_return_value`, as the injected function will clobber the
636    /// return value.
637    ///
638    /// ### Arguments
639    ///
640    /// - `accessible_memory_start` is the address of the start of the
641    ///   process-accessible memory region for this process.
642    /// - `app_brk` is the address of the current process break. This marks the
643    ///   end of the memory region the process has access to. Note, this is not
644    ///   the end of the entire memory region allocated to the process. Some
645    ///   memory above this address is still allocated for the process, but if
646    ///   the process tries to access it an MPU fault will occur.
647    /// - `state` is the stored state for this process.
648    /// - `upcall` is the function that should be executed when the process
649    ///   resumes.
650    ///
651    /// ### Return
652    ///
653    /// Returns `Ok(())` if the function was successfully enqueued for the
654    /// process. Returns `Err(())` if the function was not, likely because there
655    /// is insufficient memory available to do so.
656    ///
657    /// ### Safety
658    ///
659    /// This function guarantees that it if needs to change process memory, it
660    /// will only change memory starting at `accessible_memory_start` and before
661    /// `app_brk`. The caller is responsible for guaranteeing that those
662    /// pointers are valid for the process.
663    unsafe fn set_process_function(
664        &self,
665        accessible_memory_start: *const u8,
666        app_brk: *const u8,
667        state: &mut Self::StoredState,
668        upcall: process::FunctionCall,
669    ) -> Result<(), ()>;
670
671    /// Context switch to a specific process.
672    ///
673    /// This returns two values in a tuple.
674    ///
675    /// 1. A [`ContextSwitchReason`] indicating why the process stopped
676    ///    executing and switched back to the kernel.
677    /// 2. Optionally, the current stack pointer used by the process. This is
678    ///    optional because it is only for debugging in process.rs. By sharing
679    ///    the process's stack pointer with process.rs users can inspect the
680    ///    state and see the stack depth, which might be useful for debugging.
681    ///
682    /// ### Safety
683    ///
684    /// This function guarantees that it if needs to change process memory, it
685    /// will only change memory starting at `accessible_memory_start` and before
686    /// `app_brk`. The caller is responsible for guaranteeing that those
687    /// pointers are valid for the process.
688    unsafe fn switch_to_process(
689        &self,
690        accessible_memory_start: *const u8,
691        app_brk: *const u8,
692        state: &mut Self::StoredState,
693    ) -> (ContextSwitchReason, Option<*const u8>);
694
695    /// Display architecture specific (e.g. CPU registers or status flags) data
696    /// for a process identified by the stored state for that process.
697    ///
698    /// ### Safety
699    ///
700    /// This function guarantees that it if needs to change process memory, it
701    /// will only change memory starting at `accessible_memory_start` and before
702    /// `app_brk`. The caller is responsible for guaranteeing that those
703    /// pointers are valid for the process.
704    unsafe fn print_context(
705        &self,
706        accessible_memory_start: *const u8,
707        app_brk: *const u8,
708        state: &Self::StoredState,
709        writer: &mut dyn Write,
710    );
711
712    /// Store architecture specific (e.g. CPU registers or status flags) data
713    /// for a process. On success returns the number of elements written to out.
714    fn store_context(&self, state: &Self::StoredState, out: &mut [u8]) -> Result<usize, ErrorCode>;
715}