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