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}