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}