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.atomic(|| {
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 = unsafe {
513                resources
514                    .scheduler()
515                    .continue_process(process.processid(), chip)
516            };
517            if !continue_process {
518                return_reason = process::StoppedExecutingReason::KernelPreemption;
519                break;
520            }
521
522            // Check if this process is actually ready to run. If not, we don't
523            // try to run it. This case can happen if a process faults and is
524            // stopped, for example.
525            if !process.ready() {
526                return_reason = process::StoppedExecutingReason::NoWorkLeft;
527                break;
528            }
529
530            match process.get_state() {
531                process::State::Running => {
532                    // Running means that this process expects to be running, so
533                    // go ahead and set things up and switch to executing the
534                    // process. Arming the scheduler timer instructs it to
535                    // generate an interrupt when the timeslice has expired. The
536                    // underlying timer is not affected.
537                    resources
538                        .context_switch_callback()
539                        .context_switch_hook(process);
540                    process.setup_mpu();
541                    chip.mpu().enable_app_mpu();
542                    scheduler_timer.arm();
543                    let context_switch_reason = process.switch_to();
544                    scheduler_timer.disarm();
545                    chip.mpu().disable_app_mpu();
546
547                    // Now the process has returned back to the kernel. Check
548                    // why and handle the process as appropriate.
549                    match context_switch_reason {
550                        Some(ContextSwitchReason::Fault) => {
551                            // The app faulted, check if the chip wants to
552                            // handle the fault.
553                            if resources
554                                .process_fault()
555                                .process_fault_hook(process)
556                                .is_err()
557                            {
558                                // Let process deal with it as appropriate.
559                                process.set_fault_state();
560                            }
561                        }
562                        Some(ContextSwitchReason::SyscallFired { syscall }) => {
563                            self.handle_syscall(resources, process, syscall);
564                        }
565                        Some(ContextSwitchReason::Interrupted) => {
566                            if scheduler_timer.get_remaining_us().is_none() {
567                                // This interrupt was a timeslice expiration.
568                                process.debug_timeslice_expired();
569                                return_reason = process::StoppedExecutingReason::TimesliceExpired;
570                                break;
571                            }
572                            // Go to the beginning of loop to determine whether
573                            // to break to handle the interrupt, continue
574                            // executing this process, or switch to another
575                            // process.
576                            continue;
577                        }
578                        None => {
579                            // Something went wrong when switching to this
580                            // process. Indicate this by putting it in a fault
581                            // state.
582                            process.set_fault_state();
583                        }
584                    }
585                }
586                process::State::Yielded => {
587                    // If the process is yielded or hasn't been started it is
588                    // waiting for a upcall. If there is a task scheduled for
589                    // this process go ahead and set the process to execute it.
590                    match process.dequeue_task() {
591                        None => break,
592                        Some(cb) => match cb {
593                            Task::ReturnValue(_) => {
594                                // Per TRD104, Yield-Wait does not wake the
595                                // process for events that generate Null
596                                // Upcalls.
597                                break;
598                            }
599                            Task::FunctionCall(ccb) => {
600                                if config::CONFIG.trace_syscalls {
601                                    debug!(
602                                        "[{:?}] function_call @{:#x}({:#x}, {:#x}, {:#x}, {:#x})",
603                                        process.processid(),
604                                        ccb.pc,
605                                        ccb.argument0,
606                                        ccb.argument1,
607                                        ccb.argument2,
608                                        ccb.argument3,
609                                    );
610                                }
611                                process.set_process_function(ccb);
612                            }
613                            Task::IPC((otherapp, ipc_type)) => {
614                                ipc.map_or_else(
615                                    || {
616                                        panic!("Kernel consistency error: IPC Task with no IPC");
617                                    },
618                                    |ipc| {
619                                        // TODO(alevy): this could error for a variety of reasons.
620                                        // Should we communicate the error somehow?
621                                        // https://github.com/tock/tock/issues/1993
622                                        unsafe {
623                                            let _ = ipc.schedule_upcall(
624                                                process.processid(),
625                                                otherapp,
626                                                ipc_type,
627                                            );
628                                        }
629                                    },
630                                );
631                            }
632                        },
633                    }
634                }
635                process::State::YieldedFor(upcall_id) => {
636                    // If this process is waiting for a specific upcall, see if
637                    // it is ready. If so, dequeue it and return its values to
638                    // the process without scheduling the callback.
639                    match process.remove_upcall(upcall_id) {
640                        None => break,
641                        Some(task) => {
642                            let (a0, a1, a2) = match task {
643                                // There is no callback function registered, we
644                                // just return the values provided by the driver
645                                Task::ReturnValue(rv) => {
646                                    if config::CONFIG.trace_syscalls {
647                                        debug!(
648                                            "[{:?}] Yield-WaitFor: [NU] ({:#x}, {:#x}, {:#x})",
649                                            process.processid(),
650                                            rv.argument0,
651                                            rv.argument1,
652                                            rv.argument2,
653                                        );
654                                    }
655                                    (rv.argument0, rv.argument1, rv.argument2)
656                                }
657                                // There is a registered callback function, but
658                                // since the process used `Yield-WaitFor`, we do
659                                // not execute it, we just return its arguments
660                                // values to the application.
661                                Task::FunctionCall(ccb) => {
662                                    if config::CONFIG.trace_syscalls {
663                                        debug!(
664                                            "[{:?}] Yield-WaitFor [Suppressed function_call @{:#x}] ({:#x}, {:#x}, {:#x}, {:#x})",
665                                            process.processid(),
666                                            ccb.pc,
667                                            ccb.argument0,
668                                            ccb.argument1,
669                                            ccb.argument2,
670                                            ccb.argument3,
671                                        );
672                                    }
673                                    (ccb.argument0, ccb.argument1, ccb.argument2)
674                                }
675                                Task::IPC(_) => todo!(),
676                            };
677                            process
678                                .set_syscall_return_value(SyscallReturn::YieldWaitFor(a0, a1, a2));
679                        }
680                    }
681                }
682                process::State::Faulted | process::State::Terminated => {
683                    // We should never be scheduling an unrunnable process.
684                    // This is a potential security flaw: panic.
685                    panic!("Attempted to schedule an unrunnable process");
686                }
687                process::State::Stopped(_) => {
688                    return_reason = process::StoppedExecutingReason::Stopped;
689                    break;
690                }
691            }
692        }
693
694        // Check how much time the process used while it was executing, and
695        // return the value so we can provide it to the scheduler.
696        let time_executed_us = timeslice_us.map(|timeslice| {
697            // Note, we cannot call `.get_remaining_us()` again if it has
698            // previously returned `None`, so we _must_ check the return reason
699            // first.
700            if return_reason == process::StoppedExecutingReason::TimesliceExpired {
701                // used the whole timeslice
702                timeslice.get()
703            } else {
704                match scheduler_timer.get_remaining_us() {
705                    Some(remaining) => timeslice.get() - remaining.get(),
706                    None => timeslice.get(), // used whole timeslice
707                }
708            }
709        });
710
711        // Reset the scheduler timer in case it unconditionally triggers
712        // interrupts upon expiration. We do not want it to expire while the
713        // chip is sleeping, for example.
714        scheduler_timer.reset();
715
716        (return_reason, time_executed_us)
717    }
718
719    /// Method to invoke a system call on a particular process. Applies the
720    /// kernel system call filtering policy (if any). Handles `Yield` and
721    /// `Exit`, dispatches `Memop` to `memop::memop`, and dispatches peripheral
722    /// driver system calls to peripheral driver capsules through the platforms
723    /// `with_driver` method.
724    #[inline]
725    fn handle_syscall<KR: KernelResources<C>, C: Chip>(
726        &self,
727        resources: &KR,
728        process: &dyn process::Process,
729        syscall: Syscall,
730    ) {
731        // Hook for process debugging.
732        process.debug_syscall_called(syscall);
733
734        // Enforce platform-specific syscall filtering here.
735        //
736        // Before continuing to handle non-yield syscalls the kernel first
737        // checks if the platform wants to block that syscall for the process,
738        // and if it does, sets a return value which is returned to the calling
739        // process.
740        //
741        // Filtering a syscall (i.e. blocking the syscall from running) does not
742        // cause the process to lose its timeslice. The error will be returned
743        // immediately (assuming the process has not already exhausted its
744        // timeslice) allowing the process to decide how to handle the error.
745        match syscall {
746            Syscall::Yield {
747                which: _,
748                param_a: _,
749                param_b: _,
750            } => {} // Yield is not filterable.
751            Syscall::Exit {
752                which: _,
753                completion_code: _,
754            } => {} // Exit is not filterable.
755            Syscall::Memop {
756                operand: _,
757                arg0: _,
758            } => {} // Memop is not filterable.
759            _ => {
760                // Check all other syscalls for filtering.
761                if let Err(response) = resources.syscall_filter().filter_syscall(process, &syscall)
762                {
763                    process.set_syscall_return_value(SyscallReturn::Failure(response));
764
765                    if config::CONFIG.trace_syscalls {
766                        debug!(
767                            "[{:?}] Filtered: {:?} was rejected with {:?}",
768                            process.processid(),
769                            syscall,
770                            response
771                        );
772                    }
773
774                    return;
775                }
776            }
777        }
778
779        // Handle each of the syscalls.
780        match syscall {
781            Syscall::Memop { operand, arg0 } => {
782                let rval = memop::memop(process, operand, arg0);
783                if config::CONFIG.trace_syscalls {
784                    debug!(
785                        "[{:?}] memop({}, {:#x}) = {:?}",
786                        process.processid(),
787                        operand,
788                        arg0,
789                        rval
790                    );
791                }
792                process.set_syscall_return_value(rval);
793            }
794            Syscall::Yield {
795                which,
796                param_a,
797                param_b,
798            } => {
799                if config::CONFIG.trace_syscalls {
800                    debug!("[{:?}] yield. which: {}", process.processid(), which);
801                }
802                match which.try_into() {
803                    Ok(YieldCall::NoWait) => {
804                        // If this is a `Yield-WaitFor` AND there are no pending
805                        // tasks, then return immediately. Otherwise, go into
806                        // the yielded state and execute tasks now or when they
807                        // arrive.
808                        let has_tasks = process.has_tasks();
809
810                        // Set the "did I trigger upcalls" flag.
811                        // If address is invalid does nothing.
812                        //
813                        // # Safety
814                        //
815                        // This is fine as long as no references to the
816                        // process's memory exist. We do not have a reference,
817                        // so we can safely call `set_byte()`.
818                        unsafe {
819                            let address = param_a as *mut u8;
820                            process.set_byte(address, has_tasks as u8);
821                        }
822
823                        if has_tasks {
824                            process.set_yielded_state();
825                        }
826                    }
827
828                    Ok(YieldCall::Wait) => {
829                        process.set_yielded_state();
830                    }
831
832                    Ok(YieldCall::WaitFor) => {
833                        let upcall_id = UpcallId {
834                            driver_num: param_a,
835                            subscribe_num: param_b,
836                        };
837                        process.set_yielded_for_state(upcall_id);
838                    }
839
840                    _ => {
841                        // Only 0, 1, and 2 are valid, so this is not a valid
842                        // yield system call, Yield does not have a return value
843                        // because it can push a function call onto the stack;
844                        // just return control to the process.
845                    }
846                }
847            }
848            Syscall::Subscribe { driver_number, .. }
849            | Syscall::Command { driver_number, .. }
850            | Syscall::ReadWriteAllow { driver_number, .. }
851            | Syscall::UserspaceReadableAllow { driver_number, .. }
852            | Syscall::ReadOnlyAllow { driver_number, .. } => {
853                resources
854                .syscall_driver_lookup()
855                .with_driver(driver_number, |driver| match syscall {
856                    Syscall::Subscribe {
857                        driver_number,
858                        subdriver_number,
859                        upcall_ptr,
860                        appdata,
861                    } => {
862                        // A upcall is identified as a tuple of the driver
863                        // number and the subdriver number.
864                        let upcall_id = UpcallId {
865                            driver_num: driver_number,
866                            subscribe_num: subdriver_number,
867                        };
868
869                        // TODO: when the compiler supports capability types
870                        // bring this back as a NonNull
871                        // type. https://github.com/tock/tock/issues/4134.
872                        //
873                        // Previously, we had a NonNull type (that had a niche)
874                        // here, and could wrap that in Option to fill the niche
875                        // and handle the Null case. CapabilityPtr is filling
876                        // the gap left by * const(), which does not have the
877                        // niche and allows NULL internally. Having a CHERI
878                        // capability type with a niche is (maybe?) predicated
879                        // on having better compiler support.
880                        // Option<NonNull<()>> is preferable here, and it should
881                        // go back to it just as soon as we can express "non
882                        // null capability". For now, checking for the null case
883                        // is handled internally in each `map_or` call.
884                        //
885                        //First check if `upcall_ptr` is null. A null
886                        //`upcall_ptr` will result in `None` here and
887                        //represents the special "unsubscribe" operation.
888                        //let ptr = NonNull::new(upcall_ptr);
889
890                        // For convenience create an `Upcall` type now. This is
891                        // just a data structure and doesn't do any checking or
892                        // conversion.
893                        let upcall = Upcall::new(process.processid(), upcall_id, appdata, upcall_ptr);
894
895                        // If `ptr` is not null, we must first verify that the
896                        // upcall function pointer is within process accessible
897                        // memory. Per TRD104:
898                        //
899                        // > If the passed upcall is not valid (is outside
900                        // > process executable memory...), the kernel...MUST
901                        // > immediately return a failure with a error code of
902                        // > `INVALID`.
903                        let rval1 = upcall_ptr.map_or(None, |upcall_ptr_nonnull| {
904                            if !process.is_valid_upcall_function_pointer(upcall_ptr_nonnull.as_ptr()) {
905                                Some(ErrorCode::INVAL)
906                            } else {
907                                None
908                            }
909                        });
910
911                        // If the upcall is either null or valid, then we
912                        // continue handling the upcall.
913                        let rval = match rval1 {
914                            Some(err) => upcall.into_subscribe_failure(err),
915                            None => {
916                                match driver {
917                                    Some(driver) => {
918                                        // At this point we must save the new
919                                        // upcall and return the old. The
920                                        // upcalls are stored by the core kernel
921                                        // in the grant region so we can
922                                        // guarantee a correct upcall swap.
923                                        // However, we do need help with
924                                        // initially allocating the grant if
925                                        // this driver has never been used
926                                        // before.
927                                        //
928                                        // To avoid the overhead with checking
929                                        // for process liveness and grant
930                                        // allocation, we assume the grant is
931                                        // initially allocated. If it turns out
932                                        // it isn't we ask the capsule to
933                                        // allocate the grant.
934                                        match crate::grant::subscribe(process, upcall) {
935                                            Ok(upcall) => upcall.into_subscribe_success(),
936                                            Err((upcall, err @ ErrorCode::NOMEM)) => {
937                                                // If we get a memory error, we
938                                                // always try to allocate the
939                                                // grant since this could be the
940                                                // first time the grant is
941                                                // getting accessed.
942                                                match try_allocate_grant(driver, process) {
943                                                    AllocResult::NewAllocation => {
944                                                        // Now we try again. It
945                                                        // is possible that the
946                                                        // capsule did not
947                                                        // actually allocate the
948                                                        // grant, at which point
949                                                        // this will fail again
950                                                        // and we return an
951                                                        // error to userspace.
952                                                        match crate::grant::subscribe(
953                                                            process, upcall,
954                                                        ) {
955                                                            // An Ok() returns
956                                                            // the previous
957                                                            // upcall, while
958                                                            // Err() returns the
959                                                            // one that was just
960                                                            // passed.
961                                                            Ok(upcall) => {
962                                                                upcall.into_subscribe_success()
963                                                            }
964                                                            Err((upcall, err)) => {
965                                                                upcall.into_subscribe_failure(err)
966                                                            }
967                                                        }
968                                                    }
969                                                    alloc_failure => {
970                                                        // We didn't actually
971                                                        // create a new alloc,
972                                                        // so just error.
973                                                        match (
974                                                            config::CONFIG.trace_syscalls,
975                                                            alloc_failure,
976                                                        ) {
977                                                            (true, AllocResult::NoAllocation) => {
978                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
979                                                                           process.processid(), driver_number);
980                                                            }
981                                                            (true, AllocResult::SameAllocation) => {
982                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
983                                                                           process.processid(), driver_number);
984                                                            }
985                                                            _ => {}
986                                                        }
987                                                        upcall.into_subscribe_failure(err)
988                                                    }
989                                                }
990                                            }
991                                            Err((upcall, err)) => {
992                                                upcall.into_subscribe_failure(err)
993                                            }
994                                        }
995                                    }
996                                    None => upcall.into_subscribe_failure(ErrorCode::NODEVICE),
997                                }
998                            }
999                        };
1000
1001                        // Per TRD104, we only clear upcalls if the subscribe
1002                        // will return success. At this point we know the result
1003                        // and clear if necessary.
1004                        if rval.is_success() {
1005                            // Only one upcall should exist per tuple. To ensure
1006                            // that there are no pending upcalls with the same
1007                            // identifier but with the old function pointer, we
1008                            // clear them now.
1009                            let _ =process.remove_pending_upcalls(upcall_id);
1010                        }
1011
1012                        if config::CONFIG.trace_syscalls {
1013                            debug!(
1014                                "[{:?}] subscribe({:#x}, {}, @{:#x}, {:#x}) = {:?}",
1015                                process.processid(),
1016                                driver_number,
1017                                subdriver_number,
1018                                upcall_ptr,
1019                                appdata,
1020                                rval
1021                            );
1022                        }
1023
1024                        process.set_syscall_return_value(rval);
1025                    }
1026                    Syscall::Command {
1027                        driver_number,
1028                        subdriver_number,
1029                        arg0,
1030                        arg1,
1031                    } => {
1032                        let cres = match driver {
1033                            Some(d) => d.command(subdriver_number, arg0, arg1, process.processid()),
1034                            None => CommandReturn::failure(ErrorCode::NODEVICE),
1035                        };
1036
1037                        let res = SyscallReturn::from_command_return(cres);
1038
1039                        if config::CONFIG.trace_syscalls {
1040                            debug!(
1041                                "[{:?}] cmd({:#x}, {}, {:#x}, {:#x}) = {:?}",
1042                                process.processid(),
1043                                driver_number,
1044                                subdriver_number,
1045                                arg0,
1046                                arg1,
1047                                res,
1048                            );
1049                        }
1050                        process.set_syscall_return_value(res);
1051                    }
1052                    Syscall::ReadWriteAllow {
1053                        driver_number,
1054                        subdriver_number,
1055                        allow_address,
1056                        allow_size,
1057                    } => {
1058                        let res = match driver {
1059                            Some(driver) => {
1060                                // Try to create an appropriate
1061                                // [`ReadWriteProcessBuffer`]. This method will
1062                                // ensure that the memory in question is located
1063                                // in the process-accessible memory space.
1064                                match process
1065                                    .build_readwrite_process_buffer(allow_address, allow_size)
1066                                {
1067                                    Ok(rw_pbuf) => {
1068                                        // Creating the
1069                                        // [`ReadWriteProcessBuffer`] worked,
1070                                        // try to set in grant.
1071                                        match crate::grant::allow_rw(
1072                                            process,
1073                                            driver_number,
1074                                            subdriver_number,
1075                                            rw_pbuf,
1076                                        ) {
1077                                            Ok(rw_pbuf) => {
1078                                                let (ptr, len) = rw_pbuf.consume();
1079                                                SyscallReturn::AllowReadWriteSuccess(ptr, len)
1080                                            }
1081                                            Err((rw_pbuf, err @ ErrorCode::NOMEM)) => {
1082                                                // If we get a memory error, we
1083                                                // always try to allocate the
1084                                                // grant since this could be the
1085                                                // first time the grant is
1086                                                // getting accessed.
1087                                                match try_allocate_grant(driver, process) {
1088                                                    AllocResult::NewAllocation => {
1089                                                        // If we actually
1090                                                        // allocated a new
1091                                                        // grant, try again and
1092                                                        // honor the result.
1093                                                        match crate::grant::allow_rw(
1094                                                            process,
1095                                                            driver_number,
1096                                                            subdriver_number,
1097                                                            rw_pbuf,
1098                                                        ) {
1099                                                            Ok(rw_pbuf) => {
1100                                                                let (ptr, len) = rw_pbuf.consume();
1101                                                                SyscallReturn::AllowReadWriteSuccess(
1102                                                                    ptr, len,
1103                                                                )
1104                                                            }
1105                                                            Err((rw_pbuf, err)) => {
1106                                                                let (ptr, len) = rw_pbuf.consume();
1107                                                                SyscallReturn::AllowReadWriteFailure(
1108                                                                    err, ptr, len,
1109                                                                )
1110                                                            }
1111                                                        }
1112                                                    }
1113                                                    alloc_failure => {
1114                                                        // We didn't actually
1115                                                        // create a new alloc,
1116                                                        // so just error.
1117                                                        match (
1118                                                            config::CONFIG.trace_syscalls,
1119                                                            alloc_failure,
1120                                                        ) {
1121                                                            (true, AllocResult::NoAllocation) => {
1122                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1123                                                                           process.processid(), driver_number);
1124                                                            }
1125                                                            (true, AllocResult::SameAllocation) => {
1126                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1127                                                                           process.processid(), driver_number);
1128                                                            }
1129                                                            _ => {}
1130                                                        }
1131                                                        let (ptr, len) = rw_pbuf.consume();
1132                                                        SyscallReturn::AllowReadWriteFailure(
1133                                                            err, ptr, len,
1134                                                        )
1135                                                    }
1136                                                }
1137                                            }
1138                                            Err((rw_pbuf, err)) => {
1139                                                let (ptr, len) = rw_pbuf.consume();
1140                                                SyscallReturn::AllowReadWriteFailure(err, ptr, len)
1141                                            }
1142                                        }
1143                                    }
1144                                    Err(allow_error) => {
1145                                        // There was an error creating the
1146                                        // [`ReadWriteProcessBuffer`]. Report
1147                                        // back to the process with the original
1148                                        // parameters.
1149                                        SyscallReturn::AllowReadWriteFailure(
1150                                            allow_error,
1151                                            allow_address,
1152                                            allow_size,
1153                                        )
1154                                    }
1155                                }
1156                            }
1157                            None => SyscallReturn::AllowReadWriteFailure(
1158                                ErrorCode::NODEVICE,
1159                                allow_address,
1160                                allow_size,
1161                            ),
1162                        };
1163
1164                        if config::CONFIG.trace_syscalls {
1165                            debug!(
1166                                "[{:?}] read-write allow({:#x}, {}, @{:#x}, {}) = {:?}",
1167                                process.processid(),
1168                                driver_number,
1169                                subdriver_number,
1170                                allow_address as usize,
1171                                allow_size,
1172                                res
1173                            );
1174                        }
1175                        process.set_syscall_return_value(res);
1176                    }
1177                    Syscall::UserspaceReadableAllow {
1178                        driver_number,
1179                        subdriver_number,
1180                        allow_address,
1181                        allow_size,
1182                    } => {
1183                        let res = match driver {
1184                            Some(d) => {
1185                                // Try to create an appropriate
1186                                // [`UserspaceReadableProcessBuffer`]. This
1187                                // method will ensure that the memory in
1188                                // question is located in the process-accessible
1189                                // memory space.
1190                                match process
1191                                    .build_readwrite_process_buffer(allow_address, allow_size)
1192                                {
1193                                    Ok(rw_pbuf) => {
1194                                        // Creating the
1195                                        // [`UserspaceReadableProcessBuffer`]
1196                                        // worked, provide it to the capsule.
1197                                        match d.allow_userspace_readable(
1198                                            process.processid(),
1199                                            subdriver_number,
1200                                            rw_pbuf,
1201                                        ) {
1202                                            Ok(returned_pbuf) => {
1203                                                // The capsule has accepted the
1204                                                // allow operation. Pass the
1205                                                // previous buffer information
1206                                                // back to the process.
1207                                                let (ptr, len) = returned_pbuf.consume();
1208                                                SyscallReturn::UserspaceReadableAllowSuccess(
1209                                                    ptr, len,
1210                                                )
1211                                            }
1212                                            Err((rejected_pbuf, err)) => {
1213                                                // The capsule has rejected the
1214                                                // allow operation. Pass the new
1215                                                // buffer information back to
1216                                                // the process.
1217                                                let (ptr, len) = rejected_pbuf.consume();
1218                                                SyscallReturn::UserspaceReadableAllowFailure(
1219                                                    err, ptr, len,
1220                                                )
1221                                            }
1222                                        }
1223                                    }
1224                                    Err(allow_error) => {
1225                                        // There was an error creating the
1226                                        // [`UserspaceReadableProcessBuffer`].
1227                                        // Report back to the process.
1228                                        SyscallReturn::UserspaceReadableAllowFailure(
1229                                            allow_error,
1230                                            allow_address,
1231                                            allow_size,
1232                                        )
1233                                    }
1234                                }
1235                            }
1236
1237                            None => SyscallReturn::UserspaceReadableAllowFailure(
1238                                ErrorCode::NODEVICE,
1239                                allow_address,
1240                                allow_size,
1241                            ),
1242                        };
1243
1244                        if config::CONFIG.trace_syscalls {
1245                            debug!(
1246                                "[{:?}] userspace readable allow({:#x}, {}, @{:#x}, {}) = {:?}",
1247                                process.processid(),
1248                                driver_number,
1249                                subdriver_number,
1250                                allow_address as usize,
1251                                allow_size,
1252                                res
1253                            );
1254                        }
1255                        process.set_syscall_return_value(res);
1256                    }
1257                    Syscall::ReadOnlyAllow {
1258                        driver_number,
1259                        subdriver_number,
1260                        allow_address,
1261                        allow_size,
1262                    } => {
1263                        let res = match driver {
1264                            Some(driver) => {
1265                                // Try to create an appropriate
1266                                // [`ReadOnlyProcessBuffer`]. This method will
1267                                // ensure that the memory in question is located
1268                                // in the process-accessible memory space.
1269                                match process
1270                                    .build_readonly_process_buffer(allow_address, allow_size)
1271                                {
1272                                    Ok(ro_pbuf) => {
1273                                        // Creating the
1274                                        // [`ReadOnlyProcessBuffer`] worked, try
1275                                        // to set in grant.
1276                                        match crate::grant::allow_ro(
1277                                            process,
1278                                            driver_number,
1279                                            subdriver_number,
1280                                            ro_pbuf,
1281                                        ) {
1282                                            Ok(ro_pbuf) => {
1283                                                let (ptr, len) = ro_pbuf.consume();
1284                                                SyscallReturn::AllowReadOnlySuccess(ptr, len)
1285                                            }
1286                                            Err((ro_pbuf, err @ ErrorCode::NOMEM)) => {
1287                                                // If we get a memory error, we
1288                                                // always try to allocate the
1289                                                // grant since this could be the
1290                                                // first time the grant is
1291                                                // getting accessed.
1292                                                match try_allocate_grant(driver, process) {
1293                                                    AllocResult::NewAllocation => {
1294                                                        // If we actually
1295                                                        // allocated a new
1296                                                        // grant, try again and
1297                                                        // honor the result.
1298                                                        match crate::grant::allow_ro(
1299                                                            process,
1300                                                            driver_number,
1301                                                            subdriver_number,
1302                                                            ro_pbuf,
1303                                                        ) {
1304                                                            Ok(ro_pbuf) => {
1305                                                                let (ptr, len) = ro_pbuf.consume();
1306                                                                SyscallReturn::AllowReadOnlySuccess(
1307                                                                    ptr, len,
1308                                                                )
1309                                                            }
1310                                                            Err((ro_pbuf, err)) => {
1311                                                                let (ptr, len) = ro_pbuf.consume();
1312                                                                SyscallReturn::AllowReadOnlyFailure(
1313                                                                    err, ptr, len,
1314                                                                )
1315                                                            }
1316                                                        }
1317                                                    }
1318                                                    alloc_failure => {
1319                                                        // We didn't actually
1320                                                        // create a new alloc,
1321                                                        // so just error.
1322                                                        match (
1323                                                            config::CONFIG.trace_syscalls,
1324                                                            alloc_failure,
1325                                                        ) {
1326                                                            (true, AllocResult::NoAllocation) => {
1327                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1328                                                                           process.processid(), driver_number);
1329                                                            }
1330                                                            (true, AllocResult::SameAllocation) => {
1331                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1332                                                                           process.processid(), driver_number);
1333                                                            }
1334                                                            _ => {}
1335                                                        }
1336                                                        let (ptr, len) = ro_pbuf.consume();
1337                                                        SyscallReturn::AllowReadOnlyFailure(
1338                                                            err, ptr, len,
1339                                                        )
1340                                                    }
1341                                                }
1342                                            }
1343                                            Err((ro_pbuf, err)) => {
1344                                                let (ptr, len) = ro_pbuf.consume();
1345                                                SyscallReturn::AllowReadOnlyFailure(err, ptr, len)
1346                                            }
1347                                        }
1348                                    }
1349                                    Err(allow_error) => {
1350                                        // There was an error creating the
1351                                        // [`ReadOnlyProcessBuffer`]. Report
1352                                        // back to the process with the original
1353                                        // parameters.
1354                                        SyscallReturn::AllowReadOnlyFailure(
1355                                            allow_error,
1356                                            allow_address,
1357                                            allow_size,
1358                                        )
1359                                    }
1360                                }
1361                            }
1362                            None => SyscallReturn::AllowReadOnlyFailure(
1363                                ErrorCode::NODEVICE,
1364                                allow_address,
1365                                allow_size,
1366                            ),
1367                        };
1368
1369                        if config::CONFIG.trace_syscalls {
1370                            debug!(
1371                                "[{:?}] read-only allow({:#x}, {}, @{:#x}, {}) = {:?}",
1372                                process.processid(),
1373                                driver_number,
1374                                subdriver_number,
1375                                allow_address as usize,
1376                                allow_size,
1377                                res
1378                            );
1379                        }
1380
1381                        process.set_syscall_return_value(res);
1382                    }
1383                    Syscall::Yield { .. }
1384                    | Syscall::Exit { .. }
1385                    | Syscall::Memop { .. } => {
1386                        // These variants must not be reachable due to the outer
1387                        // match statement:
1388                        debug_assert!(false, "Kernel system call handling invariant violated!");
1389                    },
1390                })
1391            }
1392            Syscall::Exit {
1393                which,
1394                completion_code,
1395            } => {
1396                // exit try restart modifies the ID of the process.
1397                let old_process_id = process.processid();
1398                let optional_return_value = match which {
1399                    // The process called the `exit-terminate` system call.
1400                    0 => {
1401                        process.terminate(Some(completion_code as u32));
1402                        None
1403                    }
1404                    // The process called the `exit-restart` system call.
1405                    1 => {
1406                        process.try_restart(Some(completion_code as u32));
1407                        None
1408                    }
1409                    // The process called an invalid variant of the Exit
1410                    // system call class.
1411                    _ => {
1412                        let return_value = SyscallReturn::Failure(ErrorCode::NOSUPPORT);
1413                        process.set_syscall_return_value(return_value);
1414                        Some(return_value)
1415                    }
1416                };
1417                if config::CONFIG.trace_syscalls {
1418                    debug!(
1419                        "[{:?}] exit(which: {}, completion_code: {}) = {:?}",
1420                        old_process_id, which, completion_code, optional_return_value,
1421                    );
1422                }
1423            }
1424        }
1425    }
1426}