kernel/
kernel.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//! Tock's main kernel loop, scheduler loop, and Scheduler trait.
6//!
7//! This module also includes utility functions that are commonly used by
8//! scheduler policy implementations. Scheduling policy (round robin, priority,
9//! etc.) is defined in the `scheduler` subcrate and selected by a board.
10
11use core::cell::Cell;
12use core::num::NonZeroU32;
13
14use crate::capabilities;
15use crate::config;
16use crate::debug;
17use crate::deferred_call::DeferredCall;
18use crate::errorcode::ErrorCode;
19use crate::grant::{AllowRoSize, AllowRwSize, Grant, UpcallSize};
20use crate::ipc;
21use crate::memop;
22use crate::platform::chip::Chip;
23use crate::platform::mpu::MPU;
24use crate::platform::platform::ContextSwitchCallback;
25use crate::platform::platform::KernelResources;
26use crate::platform::platform::{ProcessFault, SyscallDriverLookup, SyscallFilter};
27use crate::platform::scheduler_timer::SchedulerTimer;
28use crate::platform::watchdog::WatchDog;
29use crate::process::{self, ProcessId, Task};
30use crate::scheduler::{Scheduler, SchedulingDecision};
31use crate::syscall::SyscallDriver;
32use crate::syscall::{ContextSwitchReason, SyscallReturn};
33use crate::syscall::{Syscall, YieldCall};
34use crate::syscall_driver::CommandReturn;
35use crate::upcall::{Upcall, UpcallId};
36use crate::utilities::cells::NumericCellExt;
37
38/// Threshold in microseconds to consider a process's timeslice to be exhausted.
39/// That is, Tock will skip re-scheduling a process if its remaining timeslice
40/// is less than this threshold.
41pub(crate) const MIN_QUANTA_THRESHOLD_US: u32 = 500;
42
43/// Main object for the kernel. Each board will need to create one.
44pub struct Kernel {
45    /// This holds a pointer to the static array of Process pointers.
46    processes: &'static [Option<&'static dyn process::Process>],
47
48    /// A counter which keeps track of how many process identifiers have been
49    /// created. This is used to create new unique identifiers for processes.
50    process_identifier_max: Cell<usize>,
51
52    /// How many grant regions have been setup. This is incremented on every
53    /// call to `create_grant()`. We need to explicitly track this so that when
54    /// processes are created they can be allocated pointers for each grant.
55    grant_counter: Cell<usize>,
56
57    /// Flag to mark that grants have been finalized. This means that the kernel
58    /// cannot support creating new grants because processes have already been
59    /// created and the data structures for grants have already been
60    /// established.
61    grants_finalized: Cell<bool>,
62}
63
64/// Represents the different outcomes when trying to allocate a grant region
65enum AllocResult {
66    NoAllocation,
67    NewAllocation,
68    SameAllocation,
69}
70
71/// Tries to allocate the grant region for specified driver and process.
72/// Returns if a new grant was allocated or not
73fn try_allocate_grant(driver: &dyn SyscallDriver, process: &dyn process::Process) -> AllocResult {
74    let before_count = process.grant_allocated_count().unwrap_or(0);
75    match driver.allocate_grant(process.processid()).is_ok() {
76        true if before_count == process.grant_allocated_count().unwrap_or(0) => {
77            AllocResult::SameAllocation
78        }
79        true => AllocResult::NewAllocation,
80        false => AllocResult::NoAllocation,
81    }
82}
83
84impl Kernel {
85    /// Create the kernel object that knows about the list of processes.
86    ///
87    /// Crucially, the processes included in the `processes` array MUST be valid
88    /// to execute. Any credential checks or validation MUST happen before the
89    /// `Process` object is included in this array.
90    pub fn new(processes: &'static [Option<&'static dyn process::Process>]) -> Kernel {
91        Kernel {
92            processes,
93            process_identifier_max: Cell::new(0),
94            grant_counter: Cell::new(0),
95            grants_finalized: Cell::new(false),
96        }
97    }
98
99    /// Helper function that moves all non-generic portions of process_map_or
100    /// into a non-generic function to reduce code bloat from monomorphization.
101    pub(crate) fn get_process(&self, processid: ProcessId) -> Option<&dyn process::Process> {
102        // We use the index in the [`ProcessId`] so we can do a direct lookup.
103        // However, we are not guaranteed that the app still exists at that
104        // index in the processes array. To avoid additional overhead, we do the
105        // lookup and check here, rather than calling `.index()`.
106        match self.processes.get(processid.index) {
107            Some(Some(process)) => {
108                // Check that the process stored here matches the identifier
109                // in the `processid`.
110                if process.processid() == processid {
111                    Some(*process)
112                } else {
113                    None
114                }
115            }
116            _ => None,
117        }
118    }
119
120    /// Run a closure on a specific process if it exists. If the process with a
121    /// matching [`ProcessId`] does not exist at the index specified within the
122    /// [`ProcessId`], then `default` will be returned.
123    ///
124    /// A match will not be found if the process was removed (and there is a
125    /// `None` in the process array), if the process changed its identifier
126    /// (likely after being restarted), or if the process was moved to a
127    /// different index in the processes array. Note that a match _will_ be
128    /// found if the process still exists in the correct location in the array
129    /// but is in any "stopped" state.
130    pub(crate) fn process_map_or<F, R>(&self, default: R, processid: ProcessId, closure: F) -> R
131    where
132        F: FnOnce(&dyn process::Process) -> R,
133    {
134        match self.get_process(processid) {
135            Some(process) => closure(process),
136            None => default,
137        }
138    }
139
140    /// Run a closure on a specific process if it exists. If the process with a
141    /// matching `ProcessId` does not exist at the index specified within the
142    /// `ProcessId`, then `default` will be returned.
143    ///
144    /// A match will not be found if the process was removed (and there is a
145    /// `None` in the process array), if the process changed its identifier
146    /// (likely after being restarted), or if the process was moved to a
147    /// different index in the processes array. Note that a match _will_ be
148    /// found if the process still exists in the correct location in the array
149    /// but is in any "stopped" state.
150    ///
151    /// This is functionally the same as `process_map_or()`, but this method is
152    /// available outside the kernel crate and requires a
153    /// `ProcessManagementCapability` to use.
154    pub fn process_map_or_external<F, R>(
155        &self,
156        default: R,
157        processid: ProcessId,
158        closure: F,
159        _capability: &dyn capabilities::ProcessManagementCapability,
160    ) -> R
161    where
162        F: FnOnce(&dyn process::Process) -> R,
163    {
164        match self.get_process(processid) {
165            Some(process) => closure(process),
166            None => default,
167        }
168    }
169
170    /// Run a closure on every valid process. This will iterate the array of
171    /// processes and call the closure on every process that exists.
172    pub(crate) fn process_each<F>(&self, mut closure: F)
173    where
174        F: FnMut(&dyn process::Process),
175    {
176        for process in self.processes.iter() {
177            match process {
178                Some(p) => {
179                    closure(*p);
180                }
181                None => {}
182            }
183        }
184    }
185
186    /// Returns an iterator over all processes loaded by the kernel.
187    pub(crate) fn get_process_iter(
188        &self,
189    ) -> core::iter::FilterMap<
190        core::slice::Iter<Option<&dyn process::Process>>,
191        fn(&Option<&'static dyn process::Process>) -> Option<&'static dyn process::Process>,
192    > {
193        fn keep_some(
194            &x: &Option<&'static dyn process::Process>,
195        ) -> Option<&'static dyn process::Process> {
196            x
197        }
198        self.processes.iter().filter_map(keep_some)
199    }
200
201    /// Run a closure on every valid process. This will iterate the array of
202    /// processes and call the closure on every process that exists.
203    ///
204    /// This is functionally the same as `process_each()`, but this method is
205    /// available outside the kernel crate and requires a
206    /// `ProcessManagementCapability` to use.
207    pub fn process_each_capability<F>(
208        &'static self,
209        _capability: &dyn capabilities::ProcessManagementCapability,
210        mut closure: F,
211    ) where
212        F: FnMut(&dyn process::Process),
213    {
214        for process in self.processes.iter() {
215            match process {
216                Some(p) => {
217                    closure(*p);
218                }
219                None => {}
220            }
221        }
222    }
223
224    /// Run a closure on every process, but only continue if the closure returns
225    /// `None`. That is, if the closure returns any non-`None` value, iteration
226    /// stops and the value is returned from this function to the called.
227    pub(crate) fn process_until<T, F>(&self, closure: F) -> Option<T>
228    where
229        F: Fn(&dyn process::Process) -> Option<T>,
230    {
231        for process in self.processes.iter() {
232            match process {
233                Some(p) => {
234                    let ret = closure(*p);
235                    if ret.is_some() {
236                        return ret;
237                    }
238                }
239                None => {}
240            }
241        }
242        None
243    }
244
245    /// Checks if the provided [`ProcessId`] is still valid given the processes
246    /// stored in the processes array. Returns `true` if the ProcessId still
247    /// refers to a valid process, and `false` if not.
248    ///
249    /// This is needed for `ProcessId` itself to implement the `.index()`
250    /// command to verify that the referenced app is still at the correct index.
251    pub(crate) fn processid_is_valid(&self, processid: &ProcessId) -> bool {
252        self.processes
253            .get(processid.index)
254            .is_some_and(|p| p.is_some_and(|process| process.processid().id() == processid.id()))
255    }
256
257    /// Create a new grant. This is used in board initialization to setup grants
258    /// that capsules use to interact with processes.
259    ///
260    /// Grants **must** only be created _before_ processes are initialized.
261    /// Processes use the number of grants that have been allocated to correctly
262    /// initialize the process's memory with a pointer for each grant. If a
263    /// grant is created after processes are initialized this will panic.
264    ///
265    /// Calling this function is restricted to only certain users, and to
266    /// enforce this calling this function requires the
267    /// `MemoryAllocationCapability` capability.
268    pub fn create_grant<
269        T: Default,
270        Upcalls: UpcallSize,
271        AllowROs: AllowRoSize,
272        AllowRWs: AllowRwSize,
273    >(
274        &'static self,
275        driver_num: usize,
276        _capability: &dyn capabilities::MemoryAllocationCapability,
277    ) -> Grant<T, Upcalls, AllowROs, AllowRWs> {
278        if self.grants_finalized.get() {
279            panic!("Grants finalized. Cannot create a new grant.");
280        }
281
282        // Create and return a new grant.
283        let grant_index = self.grant_counter.get();
284        self.grant_counter.increment();
285        Grant::new(self, driver_num, grant_index)
286    }
287
288    /// Returns the number of grants that have been setup in the system and
289    /// marks the grants as "finalized". This means that no more grants can
290    /// be created because data structures have been setup based on the number
291    /// of grants when this function is called.
292    ///
293    /// In practice, this is called when processes are created, and the process
294    /// memory is setup based on the number of current grants.
295    pub(crate) fn get_grant_count_and_finalize(&self) -> usize {
296        self.grants_finalized.set(true);
297        self.grant_counter.get()
298    }
299
300    /// Returns the number of grants that have been setup in the system and
301    /// marks the grants as "finalized". This means that no more grants can
302    /// be created because data structures have been setup based on the number
303    /// of grants when this function is called.
304    ///
305    /// In practice, this is called when processes are created, and the process
306    /// memory is setup based on the number of current grants.
307    ///
308    /// This is exposed publicly, but restricted with a capability. The intent
309    /// is that external implementations of `Process` need to be able to
310    /// retrieve the final number of grants.
311    pub fn get_grant_count_and_finalize_external(
312        &self,
313        _capability: &dyn capabilities::ExternalProcessCapability,
314    ) -> usize {
315        self.get_grant_count_and_finalize()
316    }
317
318    /// Create a new unique identifier for a process and return the identifier.
319    ///
320    /// Typically we just choose a larger number than we have used for any
321    /// process before which ensures that the identifier is unique.
322    pub(crate) fn create_process_identifier(&self) -> usize {
323        self.process_identifier_max.get_and_increment()
324    }
325
326    /// Cause all apps to fault.
327    ///
328    /// This will call `set_fault_state()` on each app, causing the app to enter
329    /// the state as if it had crashed (for example with an MPU violation). If
330    /// the process is configured to be restarted it will be.
331    ///
332    /// Only callers with the `ProcessManagementCapability` can call this
333    /// function. This restricts general capsules from being able to call this
334    /// function, since capsules should not be able to arbitrarily restart all
335    /// apps.
336    pub fn hardfault_all_apps<C: capabilities::ProcessManagementCapability>(&self, _c: &C) {
337        for p in self.processes.iter() {
338            p.map(|process| {
339                process.set_fault_state();
340            });
341        }
342    }
343
344    /// Perform one iteration of the core Tock kernel loop.
345    ///
346    /// This function is responsible for three main operations:
347    ///
348    /// 1. Check if the kernel itself has any work to be done and if the
349    ///    scheduler wants to complete that work now. If so, it allows the
350    ///    kernel to run.
351    /// 2. Check if any processes have any work to be done, and if so if the
352    ///    scheduler wants to allow any processes to run now, and if so which
353    ///    one.
354    /// 3. After ensuring the scheduler does not want to complete any kernel or
355    ///    process work (or there is no work to be done), are there are no
356    ///    outstanding interrupts to handle, put the chip to sleep.
357    ///
358    /// This function has one configuration option: `no_sleep`. If that argument
359    /// is set to true, the kernel will never attempt to put the chip to sleep,
360    /// and this function can be called again immediately.
361    pub fn kernel_loop_operation<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
362        &self,
363        resources: &KR,
364        chip: &C,
365        ipc: Option<&ipc::IPC<NUM_PROCS>>,
366        no_sleep: bool,
367        _capability: &dyn capabilities::MainLoopCapability,
368    ) {
369        let scheduler = resources.scheduler();
370
371        resources.watchdog().tickle();
372        unsafe {
373            // Ask the scheduler if we should do tasks inside of the kernel,
374            // such as handle interrupts. A scheduler may want to prioritize
375            // processes instead, or there may be no kernel work to do.
376            match scheduler.do_kernel_work_now(chip) {
377                true => {
378                    // Execute kernel work. This includes handling
379                    // interrupts and is how code in the chips/ and capsules
380                    // crates is able to execute.
381                    scheduler.execute_kernel_work(chip);
382                }
383                false => {
384                    // No kernel work ready, so ask scheduler for a process.
385                    match scheduler.next() {
386                        SchedulingDecision::RunProcess((processid, timeslice_us)) => {
387                            self.process_map_or((), processid, |process| {
388                                let (reason, time_executed) =
389                                    self.do_process(resources, chip, process, ipc, timeslice_us);
390                                scheduler.result(reason, time_executed);
391                            });
392                        }
393                        SchedulingDecision::TrySleep => {
394                            // For testing, it may be helpful to
395                            // disable sleeping the chip in case
396                            // the running test does not generate
397                            // any interrupts.
398                            if !no_sleep {
399                                chip.atomic(|| {
400                                    // Cannot sleep if interrupts are pending,
401                                    // as on most platforms unhandled interrupts
402                                    // will wake the device. Also, if the only
403                                    // pending interrupt occurred after the
404                                    // scheduler decided to put the chip to
405                                    // sleep, but before this atomic section
406                                    // starts, the interrupt will not be
407                                    // serviced and the chip will never wake
408                                    // from sleep.
409                                    if !chip.has_pending_interrupts() && !DeferredCall::has_tasks()
410                                    {
411                                        resources.watchdog().suspend();
412                                        chip.sleep();
413                                        resources.watchdog().resume();
414                                    }
415                                });
416                            }
417                        }
418                    }
419                }
420            }
421        }
422    }
423
424    /// Main loop of the OS.
425    ///
426    /// Most of the behavior of this loop is controlled by the [`Scheduler`]
427    /// implementation in use.
428    pub fn kernel_loop<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
429        &self,
430        resources: &KR,
431        chip: &C,
432        ipc: Option<&ipc::IPC<NUM_PROCS>>,
433        capability: &dyn capabilities::MainLoopCapability,
434    ) -> ! {
435        resources.watchdog().setup();
436        // Before we begin, verify that deferred calls were soundly setup.
437        DeferredCall::verify_setup();
438        loop {
439            self.kernel_loop_operation(resources, chip, ipc, false, capability);
440        }
441    }
442
443    /// Transfer control from the kernel to a userspace process.
444    ///
445    /// This function is called by the main kernel loop to run userspace code.
446    /// Notably, system calls from processes are handled in the kernel, *by the
447    /// kernel thread* in this function, and the syscall return value is set for
448    /// the process immediately. Normally, a process is allowed to continue
449    /// running after calling a syscall. However, the scheduler is given an out,
450    /// as `do_process()` will check with the scheduler before re-executing the
451    /// process to allow it to return from the syscall. If a process yields with
452    /// no upcalls pending, exits, exceeds its timeslice, or is interrupted,
453    /// then `do_process()` will return.
454    ///
455    /// Depending on the particular scheduler in use, this function may act in a
456    /// few different ways. `scheduler.continue_process()` allows the scheduler
457    /// to tell the Kernel whether to continue executing the process, or to
458    /// return control to the scheduler as soon as a kernel task becomes ready
459    /// (either a bottom half interrupt handler or dynamic deferred call), or to
460    /// continue executing the userspace process until it reaches one of the
461    /// aforementioned stopping conditions. Some schedulers may not require a
462    /// scheduler timer; passing `None` for the timeslice will use a null
463    /// scheduler timer even if the chip provides a real scheduler timer.
464    /// Schedulers can pass a timeslice (in us) of their choice, though if the
465    /// passed timeslice is smaller than `MIN_QUANTA_THRESHOLD_US` the process
466    /// will not execute, and this function will return immediately.
467    ///
468    /// This function returns a tuple indicating the reason the reason this
469    /// function has returned to the scheduler, and the amount of time the
470    /// process spent executing (or `None` if the process was run
471    /// cooperatively). Notably, time spent in this function by the kernel,
472    /// executing system calls or merely setting up the switch to/from
473    /// userspace, is charged to the process.
474    fn do_process<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
475        &self,
476        resources: &KR,
477        chip: &C,
478        process: &dyn process::Process,
479        ipc: Option<&crate::ipc::IPC<NUM_PROCS>>,
480        timeslice_us: Option<NonZeroU32>,
481    ) -> (process::StoppedExecutingReason, Option<u32>) {
482        // We must use a dummy scheduler timer if the process should be executed
483        // without any timeslice restrictions. Note, a chip may not provide a
484        // real scheduler timer implementation even if a timeslice is requested.
485        let scheduler_timer: &dyn SchedulerTimer = if timeslice_us.is_none() {
486            &() // dummy timer, no preemption
487        } else {
488            resources.scheduler_timer()
489        };
490
491        // Clear the scheduler timer and then start the counter. This starts the
492        // process's timeslice. Since the kernel is still executing at this
493        // point, the scheduler timer need not have an interrupt enabled after
494        // `start()`.
495        scheduler_timer.reset();
496        if let Some(timeslice) = timeslice_us {
497            scheduler_timer.start(timeslice)
498        }
499
500        // Need to track why the process is no longer executing so that we can
501        // inform the scheduler.
502        let mut return_reason = process::StoppedExecutingReason::NoWorkLeft;
503
504        // Since the timeslice counts both the process's execution time and the
505        // time spent in the kernel on behalf of the process (setting it up and
506        // handling its syscalls), we intend to keep running the process until
507        // it has no more work to do. We break out of this loop if the scheduler
508        // no longer wants to execute this process or if it exceeds its
509        // timeslice.
510        loop {
511            let stop_running = match scheduler_timer.get_remaining_us() {
512                Some(us) => us.get() <= MIN_QUANTA_THRESHOLD_US,
513                None => true,
514            };
515            if stop_running {
516                // Process ran out of time while the kernel was executing.
517                process.debug_timeslice_expired();
518                return_reason = process::StoppedExecutingReason::TimesliceExpired;
519                break;
520            }
521
522            // Check if the scheduler wishes to continue running this process.
523            let continue_process = unsafe {
524                resources
525                    .scheduler()
526                    .continue_process(process.processid(), chip)
527            };
528            if !continue_process {
529                return_reason = process::StoppedExecutingReason::KernelPreemption;
530                break;
531            }
532
533            // Check if this process is actually ready to run. If not, we don't
534            // try to run it. This case can happen if a process faults and is
535            // stopped, for example.
536            if !process.ready() {
537                return_reason = process::StoppedExecutingReason::NoWorkLeft;
538                break;
539            }
540
541            match process.get_state() {
542                process::State::Running => {
543                    // Running means that this process expects to be running, so
544                    // go ahead and set things up and switch to executing the
545                    // process. Arming the scheduler timer instructs it to
546                    // generate an interrupt when the timeslice has expired. The
547                    // underlying timer is not affected.
548                    resources
549                        .context_switch_callback()
550                        .context_switch_hook(process);
551                    process.setup_mpu();
552                    chip.mpu().enable_app_mpu();
553                    scheduler_timer.arm();
554                    let context_switch_reason = process.switch_to();
555                    scheduler_timer.disarm();
556                    chip.mpu().disable_app_mpu();
557
558                    // Now the process has returned back to the kernel. Check
559                    // why and handle the process as appropriate.
560                    match context_switch_reason {
561                        Some(ContextSwitchReason::Fault) => {
562                            // The app faulted, check if the chip wants to
563                            // handle the fault.
564                            if resources
565                                .process_fault()
566                                .process_fault_hook(process)
567                                .is_err()
568                            {
569                                // Let process deal with it as appropriate.
570                                process.set_fault_state();
571                            }
572                        }
573                        Some(ContextSwitchReason::SyscallFired { syscall }) => {
574                            self.handle_syscall(resources, process, syscall);
575                        }
576                        Some(ContextSwitchReason::Interrupted) => {
577                            if scheduler_timer.get_remaining_us().is_none() {
578                                // This interrupt was a timeslice expiration.
579                                process.debug_timeslice_expired();
580                                return_reason = process::StoppedExecutingReason::TimesliceExpired;
581                                break;
582                            }
583                            // Go to the beginning of loop to determine whether
584                            // to break to handle the interrupt, continue
585                            // executing this process, or switch to another
586                            // process.
587                            continue;
588                        }
589                        None => {
590                            // Something went wrong when switching to this
591                            // process. Indicate this by putting it in a fault
592                            // state.
593                            process.set_fault_state();
594                        }
595                    }
596                }
597                process::State::Yielded => {
598                    // If the process is yielded or hasn't been started it is
599                    // waiting for a upcall. If there is a task scheduled for
600                    // this process go ahead and set the process to execute it.
601                    match process.dequeue_task() {
602                        None => break,
603                        Some(cb) => match cb {
604                            Task::ReturnValue(_) => {
605                                // Per TRD104, Yield-Wait does not wake the
606                                // process for events that generate Null
607                                // Upcalls.
608                                break;
609                            }
610                            Task::FunctionCall(ccb) => {
611                                if config::CONFIG.trace_syscalls {
612                                    debug!(
613                                        "[{:?}] function_call @{:#x}({:#x}, {:#x}, {:#x}, {:#x})",
614                                        process.processid(),
615                                        ccb.pc,
616                                        ccb.argument0,
617                                        ccb.argument1,
618                                        ccb.argument2,
619                                        ccb.argument3,
620                                    );
621                                }
622                                process.set_process_function(ccb);
623                            }
624                            Task::IPC((otherapp, ipc_type)) => {
625                                ipc.map_or_else(
626                                    || {
627                                        panic!("Kernel consistency error: IPC Task with no IPC");
628                                    },
629                                    |ipc| {
630                                        // TODO(alevy): this could error for a variety of reasons.
631                                        // Should we communicate the error somehow?
632                                        // https://github.com/tock/tock/issues/1993
633                                        unsafe {
634                                            let _ = ipc.schedule_upcall(
635                                                process.processid(),
636                                                otherapp,
637                                                ipc_type,
638                                            );
639                                        }
640                                    },
641                                );
642                            }
643                        },
644                    }
645                }
646                process::State::YieldedFor(upcall_id) => {
647                    // If this process is waiting for a specific upcall, see if
648                    // it is ready. If so, dequeue it and return its values to
649                    // the process without scheduling the callback.
650                    match process.remove_upcall(upcall_id) {
651                        None => break,
652                        Some(task) => {
653                            let (a0, a1, a2) = match task {
654                                // There is no callback function registered, we
655                                // just return the values provided by the driver
656                                Task::ReturnValue(rv) => {
657                                    if config::CONFIG.trace_syscalls {
658                                        debug!(
659                                            "[{:?}] Yield-WaitFor: [NU] ({:#x}, {:#x}, {:#x})",
660                                            process.processid(),
661                                            rv.argument0,
662                                            rv.argument1,
663                                            rv.argument2,
664                                        );
665                                    }
666                                    (rv.argument0, rv.argument1, rv.argument2)
667                                }
668                                // There is a registered callback function, but
669                                // since the process used `Yield-WaitFor`, we do
670                                // not execute it, we just return its arguments
671                                // values to the application.
672                                Task::FunctionCall(ccb) => {
673                                    if config::CONFIG.trace_syscalls {
674                                        debug!(
675                                            "[{:?}] Yield-WaitFor [Suppressed function_call @{:#x}] ({:#x}, {:#x}, {:#x}, {:#x})",
676                                            process.processid(),
677                                            ccb.pc,
678                                            ccb.argument0,
679                                            ccb.argument1,
680                                            ccb.argument2,
681                                            ccb.argument3,
682                                        );
683                                    }
684                                    (ccb.argument0, ccb.argument1, ccb.argument2)
685                                }
686                                Task::IPC(_) => todo!(),
687                            };
688                            process
689                                .set_syscall_return_value(SyscallReturn::YieldWaitFor(a0, a1, a2));
690                        }
691                    }
692                }
693                process::State::Faulted | process::State::Terminated => {
694                    // We should never be scheduling an unrunnable process.
695                    // This is a potential security flaw: panic.
696                    panic!("Attempted to schedule an unrunnable process");
697                }
698                process::State::Stopped(_) => {
699                    return_reason = process::StoppedExecutingReason::Stopped;
700                    break;
701                }
702            }
703        }
704
705        // Check how much time the process used while it was executing, and
706        // return the value so we can provide it to the scheduler.
707        let time_executed_us = timeslice_us.map(|timeslice| {
708            // Note, we cannot call `.get_remaining_us()` again if it has
709            // previously returned `None`, so we _must_ check the return reason
710            // first.
711            if return_reason == process::StoppedExecutingReason::TimesliceExpired {
712                // used the whole timeslice
713                timeslice.get()
714            } else {
715                match scheduler_timer.get_remaining_us() {
716                    Some(remaining) => timeslice.get() - remaining.get(),
717                    None => timeslice.get(), // used whole timeslice
718                }
719            }
720        });
721
722        // Reset the scheduler timer in case it unconditionally triggers
723        // interrupts upon expiration. We do not want it to expire while the
724        // chip is sleeping, for example.
725        scheduler_timer.reset();
726
727        (return_reason, time_executed_us)
728    }
729
730    /// Method to invoke a system call on a particular process. Applies the
731    /// kernel system call filtering policy (if any). Handles `Yield` and
732    /// `Exit`, dispatches `Memop` to `memop::memop`, and dispatches peripheral
733    /// driver system calls to peripheral driver capsules through the platforms
734    /// `with_driver` method.
735    #[inline]
736    fn handle_syscall<KR: KernelResources<C>, C: Chip>(
737        &self,
738        resources: &KR,
739        process: &dyn process::Process,
740        syscall: Syscall,
741    ) {
742        // Hook for process debugging.
743        process.debug_syscall_called(syscall);
744
745        // Enforce platform-specific syscall filtering here.
746        //
747        // Before continuing to handle non-yield syscalls the kernel first
748        // checks if the platform wants to block that syscall for the process,
749        // and if it does, sets a return value which is returned to the calling
750        // process.
751        //
752        // Filtering a syscall (i.e. blocking the syscall from running) does not
753        // cause the process to lose its timeslice. The error will be returned
754        // immediately (assuming the process has not already exhausted its
755        // timeslice) allowing the process to decide how to handle the error.
756        match syscall {
757            Syscall::Yield {
758                which: _,
759                param_a: _,
760                param_b: _,
761            } => {} // Yield is not filterable.
762            Syscall::Exit {
763                which: _,
764                completion_code: _,
765            } => {} // Exit is not filterable.
766            Syscall::Memop {
767                operand: _,
768                arg0: _,
769            } => {} // Memop is not filterable.
770            _ => {
771                // Check all other syscalls for filtering.
772                if let Err(response) = resources.syscall_filter().filter_syscall(process, &syscall)
773                {
774                    process.set_syscall_return_value(SyscallReturn::Failure(response));
775
776                    if config::CONFIG.trace_syscalls {
777                        debug!(
778                            "[{:?}] Filtered: {:?} was rejected with {:?}",
779                            process.processid(),
780                            syscall,
781                            response
782                        );
783                    }
784
785                    return;
786                }
787            }
788        }
789
790        // Handle each of the syscalls.
791        match syscall {
792            Syscall::Memop { operand, arg0 } => {
793                let rval = memop::memop(process, operand, arg0);
794                if config::CONFIG.trace_syscalls {
795                    debug!(
796                        "[{:?}] memop({}, {:#x}) = {:?}",
797                        process.processid(),
798                        operand,
799                        arg0,
800                        rval
801                    );
802                }
803                process.set_syscall_return_value(rval);
804            }
805            Syscall::Yield {
806                which,
807                param_a,
808                param_b,
809            } => {
810                if config::CONFIG.trace_syscalls {
811                    debug!("[{:?}] yield. which: {}", process.processid(), which);
812                }
813                match which.try_into() {
814                    Ok(YieldCall::NoWait) => {
815                        // If this is a `Yield-WaitFor` AND there are no pending
816                        // tasks, then return immediately. Otherwise, go into
817                        // the yielded state and execute tasks now or when they
818                        // arrive.
819                        let has_tasks = process.has_tasks();
820
821                        // Set the "did I trigger upcalls" flag.
822                        // If address is invalid does nothing.
823                        //
824                        // # Safety
825                        //
826                        // This is fine as long as no references to the
827                        // process's memory exist. We do not have a reference,
828                        // so we can safely call `set_byte()`.
829                        unsafe {
830                            let address = param_a as *mut u8;
831                            process.set_byte(address, has_tasks as u8);
832                        }
833
834                        if has_tasks {
835                            process.set_yielded_state();
836                        }
837                    }
838
839                    Ok(YieldCall::Wait) => {
840                        process.set_yielded_state();
841                    }
842
843                    Ok(YieldCall::WaitFor) => {
844                        let upcall_id = UpcallId {
845                            driver_num: param_a,
846                            subscribe_num: param_b,
847                        };
848                        process.set_yielded_for_state(upcall_id);
849                    }
850
851                    _ => {
852                        // Only 0, 1, and 2 are valid, so this is not a valid
853                        // yield system call, Yield does not have a return value
854                        // because it can push a function call onto the stack;
855                        // just return control to the process.
856                    }
857                }
858            }
859            Syscall::Subscribe { driver_number, .. }
860            | Syscall::Command { driver_number, .. }
861            | Syscall::ReadWriteAllow { driver_number, .. }
862            | Syscall::UserspaceReadableAllow { driver_number, .. }
863            | Syscall::ReadOnlyAllow { driver_number, .. } => {
864                resources
865                .syscall_driver_lookup()
866                .with_driver(driver_number, |driver| match syscall {
867                    Syscall::Subscribe {
868                        driver_number,
869                        subdriver_number,
870                        upcall_ptr,
871                        appdata,
872                    } => {
873                        // A upcall is identified as a tuple of the driver
874                        // number and the subdriver number.
875                        let upcall_id = UpcallId {
876                            driver_num: driver_number,
877                            subscribe_num: subdriver_number,
878                        };
879
880                        // TODO: when the compiler supports capability types
881                        // bring this back as a NonNull
882                        // type. https://github.com/tock/tock/issues/4134.
883                        //
884                        // Previously, we had a NonNull type (that had a niche)
885                        // here, and could wrap that in Option to fill the niche
886                        // and handle the Null case. CapabilityPtr is filling
887                        // the gap left by * const(), which does not have the
888                        // niche and allows NULL internally. Having a CHERI
889                        // capability type with a niche is (maybe?) predicated
890                        // on having better compiler support.
891                        // Option<NonNull<()>> is preferable here, and it should
892                        // go back to it just as soon as we can express "non
893                        // null capability". For now, checking for the null case
894                        // is handled internally in each `map_or` call.
895                        //
896                        //First check if `upcall_ptr` is null. A null
897                        //`upcall_ptr` will result in `None` here and
898                        //represents the special "unsubscribe" operation.
899                        //let ptr = NonNull::new(upcall_ptr);
900
901                        // For convenience create an `Upcall` type now. This is
902                        // just a data structure and doesn't do any checking or
903                        // conversion.
904                        let upcall = Upcall::new(process.processid(), upcall_id, appdata, upcall_ptr);
905
906                        // If `ptr` is not null, we must first verify that the
907                        // upcall function pointer is within process accessible
908                        // memory. Per TRD104:
909                        //
910                        // > If the passed upcall is not valid (is outside
911                        // > process executable memory...), the kernel...MUST
912                        // > immediately return a failure with a error code of
913                        // > `INVALID`.
914                        let rval1 = upcall_ptr.map_or(None, |upcall_ptr_nonnull| {
915                            if !process.is_valid_upcall_function_pointer(upcall_ptr_nonnull.as_ptr()) {
916                                Some(ErrorCode::INVAL)
917                            } else {
918                                None
919                            }
920                        });
921
922                        // If the upcall is either null or valid, then we
923                        // continue handling the upcall.
924                        let rval = match rval1 {
925                            Some(err) => upcall.into_subscribe_failure(err),
926                            None => {
927                                match driver {
928                                    Some(driver) => {
929                                        // At this point we must save the new
930                                        // upcall and return the old. The
931                                        // upcalls are stored by the core kernel
932                                        // in the grant region so we can
933                                        // guarantee a correct upcall swap.
934                                        // However, we do need help with
935                                        // initially allocating the grant if
936                                        // this driver has never been used
937                                        // before.
938                                        //
939                                        // To avoid the overhead with checking
940                                        // for process liveness and grant
941                                        // allocation, we assume the grant is
942                                        // initially allocated. If it turns out
943                                        // it isn't we ask the capsule to
944                                        // allocate the grant.
945                                        match crate::grant::subscribe(process, upcall) {
946                                            Ok(upcall) => upcall.into_subscribe_success(),
947                                            Err((upcall, err @ ErrorCode::NOMEM)) => {
948                                                // If we get a memory error, we
949                                                // always try to allocate the
950                                                // grant since this could be the
951                                                // first time the grant is
952                                                // getting accessed.
953                                                match try_allocate_grant(driver, process) {
954                                                    AllocResult::NewAllocation => {
955                                                        // Now we try again. It
956                                                        // is possible that the
957                                                        // capsule did not
958                                                        // actually allocate the
959                                                        // grant, at which point
960                                                        // this will fail again
961                                                        // and we return an
962                                                        // error to userspace.
963                                                        match crate::grant::subscribe(
964                                                            process, upcall,
965                                                        ) {
966                                                            // An Ok() returns
967                                                            // the previous
968                                                            // upcall, while
969                                                            // Err() returns the
970                                                            // one that was just
971                                                            // passed.
972                                                            Ok(upcall) => {
973                                                                upcall.into_subscribe_success()
974                                                            }
975                                                            Err((upcall, err)) => {
976                                                                upcall.into_subscribe_failure(err)
977                                                            }
978                                                        }
979                                                    }
980                                                    alloc_failure => {
981                                                        // We didn't actually
982                                                        // create a new alloc,
983                                                        // so just error.
984                                                        match (
985                                                            config::CONFIG.trace_syscalls,
986                                                            alloc_failure,
987                                                        ) {
988                                                            (true, AllocResult::NoAllocation) => {
989                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
990                                                                           process.processid(), driver_number);
991                                                            }
992                                                            (true, AllocResult::SameAllocation) => {
993                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
994                                                                           process.processid(), driver_number);
995                                                            }
996                                                            _ => {}
997                                                        }
998                                                        upcall.into_subscribe_failure(err)
999                                                    }
1000                                                }
1001                                            }
1002                                            Err((upcall, err)) => {
1003                                                upcall.into_subscribe_failure(err)
1004                                            }
1005                                        }
1006                                    }
1007                                    None => upcall.into_subscribe_failure(ErrorCode::NODEVICE),
1008                                }
1009                            }
1010                        };
1011
1012                        // Per TRD104, we only clear upcalls if the subscribe
1013                        // will return success. At this point we know the result
1014                        // and clear if necessary.
1015                        if rval.is_success() {
1016                            // Only one upcall should exist per tuple. To ensure
1017                            // that there are no pending upcalls with the same
1018                            // identifier but with the old function pointer, we
1019                            // clear them now.
1020                            let _ =process.remove_pending_upcalls(upcall_id);
1021                        }
1022
1023                        if config::CONFIG.trace_syscalls {
1024                            debug!(
1025                                "[{:?}] subscribe({:#x}, {}, @{:#x}, {:#x}) = {:?}",
1026                                process.processid(),
1027                                driver_number,
1028                                subdriver_number,
1029                                upcall_ptr,
1030                                appdata,
1031                                rval
1032                            );
1033                        }
1034
1035                        process.set_syscall_return_value(rval);
1036                    }
1037                    Syscall::Command {
1038                        driver_number,
1039                        subdriver_number,
1040                        arg0,
1041                        arg1,
1042                    } => {
1043                        let cres = match driver {
1044                            Some(d) => d.command(subdriver_number, arg0, arg1, process.processid()),
1045                            None => CommandReturn::failure(ErrorCode::NODEVICE),
1046                        };
1047
1048                        let res = SyscallReturn::from_command_return(cres);
1049
1050                        if config::CONFIG.trace_syscalls {
1051                            debug!(
1052                                "[{:?}] cmd({:#x}, {}, {:#x}, {:#x}) = {:?}",
1053                                process.processid(),
1054                                driver_number,
1055                                subdriver_number,
1056                                arg0,
1057                                arg1,
1058                                res,
1059                            );
1060                        }
1061                        process.set_syscall_return_value(res);
1062                    }
1063                    Syscall::ReadWriteAllow {
1064                        driver_number,
1065                        subdriver_number,
1066                        allow_address,
1067                        allow_size,
1068                    } => {
1069                        let res = match driver {
1070                            Some(driver) => {
1071                                // Try to create an appropriate
1072                                // [`ReadWriteProcessBuffer`]. This method will
1073                                // ensure that the memory in question is located
1074                                // in the process-accessible memory space.
1075                                match process
1076                                    .build_readwrite_process_buffer(allow_address, allow_size)
1077                                {
1078                                    Ok(rw_pbuf) => {
1079                                        // Creating the
1080                                        // [`ReadWriteProcessBuffer`] worked,
1081                                        // try to set in grant.
1082                                        match crate::grant::allow_rw(
1083                                            process,
1084                                            driver_number,
1085                                            subdriver_number,
1086                                            rw_pbuf,
1087                                        ) {
1088                                            Ok(rw_pbuf) => {
1089                                                let (ptr, len) = rw_pbuf.consume();
1090                                                SyscallReturn::AllowReadWriteSuccess(ptr, len)
1091                                            }
1092                                            Err((rw_pbuf, err @ ErrorCode::NOMEM)) => {
1093                                                // If we get a memory error, we
1094                                                // always try to allocate the
1095                                                // grant since this could be the
1096                                                // first time the grant is
1097                                                // getting accessed.
1098                                                match try_allocate_grant(driver, process) {
1099                                                    AllocResult::NewAllocation => {
1100                                                        // If we actually
1101                                                        // allocated a new
1102                                                        // grant, try again and
1103                                                        // honor the result.
1104                                                        match crate::grant::allow_rw(
1105                                                            process,
1106                                                            driver_number,
1107                                                            subdriver_number,
1108                                                            rw_pbuf,
1109                                                        ) {
1110                                                            Ok(rw_pbuf) => {
1111                                                                let (ptr, len) = rw_pbuf.consume();
1112                                                                SyscallReturn::AllowReadWriteSuccess(
1113                                                                    ptr, len,
1114                                                                )
1115                                                            }
1116                                                            Err((rw_pbuf, err)) => {
1117                                                                let (ptr, len) = rw_pbuf.consume();
1118                                                                SyscallReturn::AllowReadWriteFailure(
1119                                                                    err, ptr, len,
1120                                                                )
1121                                                            }
1122                                                        }
1123                                                    }
1124                                                    alloc_failure => {
1125                                                        // We didn't actually
1126                                                        // create a new alloc,
1127                                                        // so just error.
1128                                                        match (
1129                                                            config::CONFIG.trace_syscalls,
1130                                                            alloc_failure,
1131                                                        ) {
1132                                                            (true, AllocResult::NoAllocation) => {
1133                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1134                                                                           process.processid(), driver_number);
1135                                                            }
1136                                                            (true, AllocResult::SameAllocation) => {
1137                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1138                                                                           process.processid(), driver_number);
1139                                                            }
1140                                                            _ => {}
1141                                                        }
1142                                                        let (ptr, len) = rw_pbuf.consume();
1143                                                        SyscallReturn::AllowReadWriteFailure(
1144                                                            err, ptr, len,
1145                                                        )
1146                                                    }
1147                                                }
1148                                            }
1149                                            Err((rw_pbuf, err)) => {
1150                                                let (ptr, len) = rw_pbuf.consume();
1151                                                SyscallReturn::AllowReadWriteFailure(err, ptr, len)
1152                                            }
1153                                        }
1154                                    }
1155                                    Err(allow_error) => {
1156                                        // There was an error creating the
1157                                        // [`ReadWriteProcessBuffer`]. Report
1158                                        // back to the process with the original
1159                                        // parameters.
1160                                        SyscallReturn::AllowReadWriteFailure(
1161                                            allow_error,
1162                                            allow_address,
1163                                            allow_size,
1164                                        )
1165                                    }
1166                                }
1167                            }
1168                            None => SyscallReturn::AllowReadWriteFailure(
1169                                ErrorCode::NODEVICE,
1170                                allow_address,
1171                                allow_size,
1172                            ),
1173                        };
1174
1175                        if config::CONFIG.trace_syscalls {
1176                            debug!(
1177                                "[{:?}] read-write allow({:#x}, {}, @{:#x}, {}) = {:?}",
1178                                process.processid(),
1179                                driver_number,
1180                                subdriver_number,
1181                                allow_address as usize,
1182                                allow_size,
1183                                res
1184                            );
1185                        }
1186                        process.set_syscall_return_value(res);
1187                    }
1188                    Syscall::UserspaceReadableAllow {
1189                        driver_number,
1190                        subdriver_number,
1191                        allow_address,
1192                        allow_size,
1193                    } => {
1194                        let res = match driver {
1195                            Some(d) => {
1196                                // Try to create an appropriate
1197                                // [`UserspaceReadableProcessBuffer`]. This
1198                                // method will ensure that the memory in
1199                                // question is located in the process-accessible
1200                                // memory space.
1201                                match process
1202                                    .build_readwrite_process_buffer(allow_address, allow_size)
1203                                {
1204                                    Ok(rw_pbuf) => {
1205                                        // Creating the
1206                                        // [`UserspaceReadableProcessBuffer`]
1207                                        // worked, provide it to the capsule.
1208                                        match d.allow_userspace_readable(
1209                                            process.processid(),
1210                                            subdriver_number,
1211                                            rw_pbuf,
1212                                        ) {
1213                                            Ok(returned_pbuf) => {
1214                                                // The capsule has accepted the
1215                                                // allow operation. Pass the
1216                                                // previous buffer information
1217                                                // back to the process.
1218                                                let (ptr, len) = returned_pbuf.consume();
1219                                                SyscallReturn::UserspaceReadableAllowSuccess(
1220                                                    ptr, len,
1221                                                )
1222                                            }
1223                                            Err((rejected_pbuf, err)) => {
1224                                                // The capsule has rejected the
1225                                                // allow operation. Pass the new
1226                                                // buffer information back to
1227                                                // the process.
1228                                                let (ptr, len) = rejected_pbuf.consume();
1229                                                SyscallReturn::UserspaceReadableAllowFailure(
1230                                                    err, ptr, len,
1231                                                )
1232                                            }
1233                                        }
1234                                    }
1235                                    Err(allow_error) => {
1236                                        // There was an error creating the
1237                                        // [`UserspaceReadableProcessBuffer`].
1238                                        // Report back to the process.
1239                                        SyscallReturn::UserspaceReadableAllowFailure(
1240                                            allow_error,
1241                                            allow_address,
1242                                            allow_size,
1243                                        )
1244                                    }
1245                                }
1246                            }
1247
1248                            None => SyscallReturn::UserspaceReadableAllowFailure(
1249                                ErrorCode::NODEVICE,
1250                                allow_address,
1251                                allow_size,
1252                            ),
1253                        };
1254
1255                        if config::CONFIG.trace_syscalls {
1256                            debug!(
1257                                "[{:?}] userspace readable allow({:#x}, {}, @{:#x}, {}) = {:?}",
1258                                process.processid(),
1259                                driver_number,
1260                                subdriver_number,
1261                                allow_address as usize,
1262                                allow_size,
1263                                res
1264                            );
1265                        }
1266                        process.set_syscall_return_value(res);
1267                    }
1268                    Syscall::ReadOnlyAllow {
1269                        driver_number,
1270                        subdriver_number,
1271                        allow_address,
1272                        allow_size,
1273                    } => {
1274                        let res = match driver {
1275                            Some(driver) => {
1276                                // Try to create an appropriate
1277                                // [`ReadOnlyProcessBuffer`]. This method will
1278                                // ensure that the memory in question is located
1279                                // in the process-accessible memory space.
1280                                match process
1281                                    .build_readonly_process_buffer(allow_address, allow_size)
1282                                {
1283                                    Ok(ro_pbuf) => {
1284                                        // Creating the
1285                                        // [`ReadOnlyProcessBuffer`] worked, try
1286                                        // to set in grant.
1287                                        match crate::grant::allow_ro(
1288                                            process,
1289                                            driver_number,
1290                                            subdriver_number,
1291                                            ro_pbuf,
1292                                        ) {
1293                                            Ok(ro_pbuf) => {
1294                                                let (ptr, len) = ro_pbuf.consume();
1295                                                SyscallReturn::AllowReadOnlySuccess(ptr, len)
1296                                            }
1297                                            Err((ro_pbuf, err @ ErrorCode::NOMEM)) => {
1298                                                // If we get a memory error, we
1299                                                // always try to allocate the
1300                                                // grant since this could be the
1301                                                // first time the grant is
1302                                                // getting accessed.
1303                                                match try_allocate_grant(driver, process) {
1304                                                    AllocResult::NewAllocation => {
1305                                                        // If we actually
1306                                                        // allocated a new
1307                                                        // grant, try again and
1308                                                        // honor the result.
1309                                                        match crate::grant::allow_ro(
1310                                                            process,
1311                                                            driver_number,
1312                                                            subdriver_number,
1313                                                            ro_pbuf,
1314                                                        ) {
1315                                                            Ok(ro_pbuf) => {
1316                                                                let (ptr, len) = ro_pbuf.consume();
1317                                                                SyscallReturn::AllowReadOnlySuccess(
1318                                                                    ptr, len,
1319                                                                )
1320                                                            }
1321                                                            Err((ro_pbuf, err)) => {
1322                                                                let (ptr, len) = ro_pbuf.consume();
1323                                                                SyscallReturn::AllowReadOnlyFailure(
1324                                                                    err, ptr, len,
1325                                                                )
1326                                                            }
1327                                                        }
1328                                                    }
1329                                                    alloc_failure => {
1330                                                        // We didn't actually
1331                                                        // create a new alloc,
1332                                                        // so just error.
1333                                                        match (
1334                                                            config::CONFIG.trace_syscalls,
1335                                                            alloc_failure,
1336                                                        ) {
1337                                                            (true, AllocResult::NoAllocation) => {
1338                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1339                                                                           process.processid(), driver_number);
1340                                                            }
1341                                                            (true, AllocResult::SameAllocation) => {
1342                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1343                                                                           process.processid(), driver_number);
1344                                                            }
1345                                                            _ => {}
1346                                                        }
1347                                                        let (ptr, len) = ro_pbuf.consume();
1348                                                        SyscallReturn::AllowReadOnlyFailure(
1349                                                            err, ptr, len,
1350                                                        )
1351                                                    }
1352                                                }
1353                                            }
1354                                            Err((ro_pbuf, err)) => {
1355                                                let (ptr, len) = ro_pbuf.consume();
1356                                                SyscallReturn::AllowReadOnlyFailure(err, ptr, len)
1357                                            }
1358                                        }
1359                                    }
1360                                    Err(allow_error) => {
1361                                        // There was an error creating the
1362                                        // [`ReadOnlyProcessBuffer`]. Report
1363                                        // back to the process with the original
1364                                        // parameters.
1365                                        SyscallReturn::AllowReadOnlyFailure(
1366                                            allow_error,
1367                                            allow_address,
1368                                            allow_size,
1369                                        )
1370                                    }
1371                                }
1372                            }
1373                            None => SyscallReturn::AllowReadOnlyFailure(
1374                                ErrorCode::NODEVICE,
1375                                allow_address,
1376                                allow_size,
1377                            ),
1378                        };
1379
1380                        if config::CONFIG.trace_syscalls {
1381                            debug!(
1382                                "[{:?}] read-only allow({:#x}, {}, @{:#x}, {}) = {:?}",
1383                                process.processid(),
1384                                driver_number,
1385                                subdriver_number,
1386                                allow_address as usize,
1387                                allow_size,
1388                                res
1389                            );
1390                        }
1391
1392                        process.set_syscall_return_value(res);
1393                    }
1394                    Syscall::Yield { .. }
1395                    | Syscall::Exit { .. }
1396                    | Syscall::Memop { .. } => {
1397                        // These variants must not be reachable due to the outer
1398                        // match statement:
1399                        debug_assert!(false, "Kernel system call handling invariant violated!");
1400                    },
1401                })
1402            }
1403            Syscall::Exit {
1404                which,
1405                completion_code,
1406            } => {
1407                // exit try restart modifies the ID of the process.
1408                let old_process_id = process.processid();
1409                let optional_return_value = match which {
1410                    // The process called the `exit-terminate` system call.
1411                    0 => {
1412                        process.terminate(Some(completion_code as u32));
1413                        None
1414                    }
1415                    // The process called the `exit-restart` system call.
1416                    1 => {
1417                        process.try_restart(Some(completion_code as u32));
1418                        None
1419                    }
1420                    // The process called an invalid variant of the Exit
1421                    // system call class.
1422                    _ => {
1423                        let return_value = SyscallReturn::Failure(ErrorCode::NOSUPPORT);
1424                        process.set_syscall_return_value(return_value);
1425                        Some(return_value)
1426                    }
1427                };
1428                if config::CONFIG.trace_syscalls {
1429                    debug!(
1430                        "[{:?}] exit(which: {}, completion_code: {}) = {:?}",
1431                        old_process_id, which, completion_code, optional_return_value,
1432                    );
1433                }
1434            }
1435        }
1436    }
1437}