Skip to main content

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, YieldVariant};
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
305    /// [`&Process`](process::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    /// Terminate a process if it exists, and remove it from ProcessArray.
318    /// `extract_app_handle` is the caller-passed operation which is used
319    /// to determine the `app_handle` which is returned as a usize value.
320    pub(crate) fn remove_process_from_active_processes<F>(
321        &self,
322        shortid: process::ShortId,
323        extract_app_handle: F,
324    ) -> Result<usize, ()>
325    where
326        F: FnOnce(&'static dyn process::Process) -> usize,
327    {
328        for slot in self.processes.iter() {
329            if let Some(process) = slot.get() {
330                if process.short_app_id() == shortid {
331                    process.terminate(None);
332                    let result = extract_app_handle(process);
333                    slot.proc.set(None);
334                    return Ok(result);
335                }
336            }
337        }
338        Err(())
339    }
340
341    /// Cause all apps to fault.
342    ///
343    /// This will call `set_fault_state()` on each app, causing the app to enter
344    /// the state as if it had crashed (for example with an MPU violation). If
345    /// the process is configured to be restarted it will be.
346    ///
347    /// Only callers with the `ProcessManagementCapability` can call this
348    /// function. This restricts general capsules from being able to call this
349    /// function, since capsules should not be able to arbitrarily restart all
350    /// apps.
351    pub fn hardfault_all_apps<C: capabilities::ProcessManagementCapability>(&self, _c: &C) {
352        for process in self.get_process_iter() {
353            process.set_fault_state();
354        }
355    }
356
357    /// Perform one iteration of the core Tock kernel loop.
358    ///
359    /// This function is responsible for three main operations:
360    ///
361    /// 1. Check if the kernel itself has any work to be done and if the
362    ///    scheduler wants to complete that work now. If so, it allows the
363    ///    kernel to run.
364    /// 2. Check if any processes have any work to be done, and if so if the
365    ///    scheduler wants to allow any processes to run now, and if so which
366    ///    one.
367    /// 3. After ensuring the scheduler does not want to complete any kernel or
368    ///    process work (or there is no work to be done), are there are no
369    ///    outstanding interrupts to handle, put the chip to sleep.
370    ///
371    /// This function has one configuration option: `no_sleep`. If that argument
372    /// is set to true, the kernel will never attempt to put the chip to sleep,
373    /// and this function can be called again immediately.
374    pub fn kernel_loop_operation<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
375        &self,
376        resources: &KR,
377        chip: &C,
378        ipc: Option<&ipc::IPC<NUM_PROCS>>,
379        no_sleep: bool,
380        _capability: &dyn capabilities::MainLoopCapability,
381    ) {
382        let scheduler = resources.scheduler();
383
384        resources.watchdog().tickle();
385        // Ask the scheduler if we should do tasks inside of the kernel,
386        // such as handle interrupts. A scheduler may want to prioritize
387        // processes instead, or there may be no kernel work to do.
388        match scheduler.do_kernel_work_now(chip) {
389            true => {
390                // Execute kernel work. This includes handling
391                // interrupts and is how code in the chips/ and capsules
392                // crates is able to execute.
393                scheduler.execute_kernel_work(chip);
394            }
395            false => {
396                // No kernel work ready, so ask scheduler for a process.
397                match scheduler.next() {
398                    SchedulingDecision::RunProcess((processid, timeslice_us)) => {
399                        self.process_map_or((), processid, |process| {
400                            let (reason, time_executed) =
401                                self.do_process(resources, chip, process, ipc, timeslice_us);
402                            scheduler.result(reason, time_executed);
403                        });
404                    }
405                    SchedulingDecision::TrySleep => {
406                        // For testing, it may be helpful to
407                        // disable sleeping the chip in case
408                        // the running test does not generate
409                        // any interrupts.
410                        if !no_sleep {
411                            chip.with_interrupts_disabled(|| {
412                                // Cannot sleep if interrupts are pending,
413                                // as on most platforms unhandled interrupts
414                                // will wake the device. Also, if the only
415                                // pending interrupt occurred after the
416                                // scheduler decided to put the chip to
417                                // sleep, but before this atomic section
418                                // starts, the interrupt will not be
419                                // serviced and the chip will never wake
420                                // from sleep.
421                                if !chip.has_pending_interrupts() && !DeferredCall::has_tasks() {
422                                    resources.watchdog().suspend();
423                                    chip.sleep();
424                                    resources.watchdog().resume();
425                                }
426                            });
427                        }
428                    }
429                }
430            }
431        }
432    }
433
434    /// Main loop of the OS.
435    ///
436    /// Most of the behavior of this loop is controlled by the [`Scheduler`]
437    /// implementation in use.
438    pub fn kernel_loop<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
439        &self,
440        resources: &KR,
441        chip: &C,
442        ipc: Option<&ipc::IPC<NUM_PROCS>>,
443        capability: &dyn capabilities::MainLoopCapability,
444    ) -> ! {
445        resources.watchdog().setup();
446        // Before we begin, verify that deferred calls were soundly setup.
447        DeferredCall::verify_setup();
448        loop {
449            self.kernel_loop_operation(resources, chip, ipc, false, capability);
450        }
451    }
452
453    /// Transfer control from the kernel to a userspace process.
454    ///
455    /// This function is called by the main kernel loop to run userspace code.
456    /// Notably, system calls from processes are handled in the kernel, *by the
457    /// kernel thread* in this function, and the syscall return value is set for
458    /// the process immediately. Normally, a process is allowed to continue
459    /// running after calling a syscall. However, the scheduler is given an out,
460    /// as `do_process()` will check with the scheduler before re-executing the
461    /// process to allow it to return from the syscall. If a process yields with
462    /// no upcalls pending, exits, exceeds its timeslice, or is interrupted,
463    /// then `do_process()` will return.
464    ///
465    /// Depending on the particular scheduler in use, this function may act in a
466    /// few different ways. `scheduler.continue_process()` allows the scheduler
467    /// to tell the Kernel whether to continue executing the process, or to
468    /// return control to the scheduler as soon as a kernel task becomes ready
469    /// (either a bottom half interrupt handler or dynamic deferred call), or to
470    /// continue executing the userspace process until it reaches one of the
471    /// aforementioned stopping conditions. Some schedulers may not require a
472    /// scheduler timer; passing `None` for the timeslice will use a null
473    /// scheduler timer even if the chip provides a real scheduler timer.
474    /// Schedulers can pass a timeslice (in us) of their choice, though if the
475    /// passed timeslice is smaller than `MIN_QUANTA_THRESHOLD_US` the process
476    /// will not execute, and this function will return immediately.
477    ///
478    /// This function returns a tuple indicating the reason the reason this
479    /// function has returned to the scheduler, and the amount of time the
480    /// process spent executing (or `None` if the process was run
481    /// cooperatively). Notably, time spent in this function by the kernel,
482    /// executing system calls or merely setting up the switch to/from
483    /// userspace, is charged to the process.
484    fn do_process<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>(
485        &self,
486        resources: &KR,
487        chip: &C,
488        process: &dyn process::Process,
489        ipc: Option<&crate::ipc::IPC<NUM_PROCS>>,
490        timeslice_us: Option<NonZeroU32>,
491    ) -> (process::StoppedExecutingReason, Option<u32>) {
492        // We must use a dummy scheduler timer if the process should be executed
493        // without any timeslice restrictions. Note, a chip may not provide a
494        // real scheduler timer implementation even if a timeslice is requested.
495        let scheduler_timer: &dyn SchedulerTimer = if timeslice_us.is_none() {
496            &() // dummy timer, no preemption
497        } else {
498            resources.scheduler_timer()
499        };
500
501        // Clear the scheduler timer and then start the counter. This starts the
502        // process's timeslice. Since the kernel is still executing at this
503        // point, the scheduler timer need not have an interrupt enabled after
504        // `start()`.
505        scheduler_timer.reset();
506        if let Some(timeslice) = timeslice_us {
507            scheduler_timer.start(timeslice)
508        }
509
510        // Need to track why the process is no longer executing so that we can
511        // inform the scheduler.
512        let mut return_reason = process::StoppedExecutingReason::NoWorkLeft;
513
514        // Since the timeslice counts both the process's execution time and the
515        // time spent in the kernel on behalf of the process (setting it up and
516        // handling its syscalls), we intend to keep running the process until
517        // it has no more work to do. We break out of this loop if the scheduler
518        // no longer wants to execute this process or if it exceeds its
519        // timeslice.
520        loop {
521            let stop_running = match scheduler_timer.get_remaining_us() {
522                Some(us) => us.get() <= MIN_QUANTA_THRESHOLD_US,
523                None => true,
524            };
525            if stop_running {
526                // Process ran out of time while the kernel was executing.
527                process.debug_timeslice_expired();
528                return_reason = process::StoppedExecutingReason::TimesliceExpired;
529                break;
530            }
531
532            // Check if the scheduler wishes to continue running this process.
533            let continue_process = resources
534                .scheduler()
535                .continue_process(process.processid(), chip);
536            if !continue_process {
537                return_reason = process::StoppedExecutingReason::KernelPreemption;
538                break;
539            }
540
541            // Check if this process is actually ready to run. If not, we don't
542            // try to run it. This case can happen if a process faults and is
543            // stopped, for example.
544            if !process.ready() {
545                return_reason = process::StoppedExecutingReason::NoWorkLeft;
546                break;
547            }
548
549            match process.get_state() {
550                process::State::Running => {
551                    // Running means that this process expects to be running, so
552                    // go ahead and set things up and switch to executing the
553                    // process. Arming the scheduler timer instructs it to
554                    // generate an interrupt when the timeslice has expired. The
555                    // underlying timer is not affected.
556                    resources
557                        .context_switch_callback()
558                        .context_switch_hook(process);
559
560                    // Configure the MPU for the process to run, and activate
561                    // it. With certain memory protection mechanisms such as
562                    // RISC-V ePMP, this will make all userspace-accessible
563                    // memory inaccessible to kernel mode. When switching back
564                    // to the kernel, we must first run `disable_app_mpu` before
565                    // attempting to access any userspace memory.
566                    process.setup_mpu();
567                    chip.mpu().enable_app_mpu();
568
569                    scheduler_timer.arm();
570                    // SAFETY: We just setup and enabled the MPU for the process,
571                    // ensuring it will be correctly configured when the process
572                    // executes.
573                    let context_switch_reason = unsafe { process.switch_to() };
574                    scheduler_timer.disarm();
575
576                    // Disable the application MPU. This is necessary on systems
577                    // like RISC-V ePMP, which makes userspace memory
578                    // inaccessible to kernel mode if memory protection is
579                    // active.
580                    //
581                    // SAFETY: This function is unsafe, as calling it before switching
582                    // to a process, without first re-enabling memory
583                    // protection, could allow an application to access
584                    // kernel-private memory. Invoking this function is safe
585                    // here, as we always call `enable_app_mpu` above before
586                    // switching back to a process.
587                    unsafe {
588                        chip.mpu().disable_app_mpu();
589                    }
590
591                    // Now the process has returned back to the kernel. Check
592                    // why and handle the process as appropriate.
593                    match context_switch_reason {
594                        Some(ContextSwitchReason::Fault) => {
595                            // The app faulted, check if the chip wants to
596                            // handle the fault.
597                            if resources
598                                .process_fault()
599                                .process_fault_hook(process)
600                                .is_err()
601                            {
602                                // Let process deal with it as appropriate.
603                                process.set_fault_state();
604                            }
605                        }
606                        Some(ContextSwitchReason::SyscallFired { syscall }) => {
607                            self.handle_syscall(resources, process, syscall);
608                        }
609                        Some(ContextSwitchReason::Interrupted) => {
610                            if scheduler_timer.get_remaining_us().is_none() {
611                                // This interrupt was a timeslice expiration.
612                                process.debug_timeslice_expired();
613                                return_reason = process::StoppedExecutingReason::TimesliceExpired;
614                                break;
615                            }
616                            // Go to the beginning of loop to determine whether
617                            // to break to handle the interrupt, continue
618                            // executing this process, or switch to another
619                            // process.
620                            continue;
621                        }
622                        None => {
623                            // Something went wrong when switching to this
624                            // process. Indicate this by putting it in a fault
625                            // state.
626                            process.set_fault_state();
627                        }
628                    }
629                }
630                process::State::Yielded => {
631                    // If the process is yielded or hasn't been started it is
632                    // waiting for a upcall. If there is a task scheduled for
633                    // this process go ahead and set the process to execute it.
634                    match process.dequeue_task() {
635                        None => break,
636                        Some(cb) => match cb {
637                            Task::ReturnValue(_) => {
638                                // Per TRD104, Yield-Wait does not wake the
639                                // process for events that generate Null
640                                // Upcalls.
641                                break;
642                            }
643                            Task::FunctionCall(ccb) => {
644                                if config::CONFIG.trace_syscalls {
645                                    debug!(
646                                        "[{:?}] function_call @{:#x}({:#x}, {:#x}, {:#x}, {:#x})",
647                                        process.processid(),
648                                        ccb.pc,
649                                        ccb.argument0,
650                                        ccb.argument1,
651                                        ccb.argument2,
652                                        ccb.argument3,
653                                    );
654                                }
655                                process.set_process_function(ccb);
656                            }
657                            Task::IPC((otherapp, ipc_type)) => {
658                                ipc.map_or_else(
659                                    || {
660                                        panic!("Kernel consistency error: IPC Task with no IPC");
661                                    },
662                                    |ipc| {
663                                        // TODO(alevy): this could error for a variety of reasons.
664                                        // Should we communicate the error somehow?
665                                        // https://github.com/tock/tock/issues/1993
666                                        //
667                                        // SAFETY: This is NOT safe. This is a known issue with this
668                                        // implementation of IPC:
669                                        // <https://github.com/tock/tock/issues/1993>.
670                                        unsafe {
671                                            let _ = ipc.schedule_upcall(
672                                                process.processid(),
673                                                otherapp,
674                                                ipc_type,
675                                            );
676                                        }
677                                    },
678                                );
679                            }
680                        },
681                    }
682                }
683                process::State::YieldedFor(upcall_id) => {
684                    // If this process is waiting for a specific upcall, see if
685                    // it is ready. If so, dequeue it and return its values to
686                    // the process without scheduling the callback.
687                    match process.remove_upcall(upcall_id) {
688                        None => break,
689                        Some(task) => {
690                            let (a0, a1, a2) = match task {
691                                // There is no callback function registered, we
692                                // just return the values provided by the driver
693                                Task::ReturnValue(rv) => {
694                                    if config::CONFIG.trace_syscalls {
695                                        debug!(
696                                            "[{:?}] Yield-WaitFor: [NU] ({:#x}, {:#x}, {:#x})",
697                                            process.processid(),
698                                            rv.argument0,
699                                            rv.argument1,
700                                            rv.argument2,
701                                        );
702                                    }
703                                    (rv.argument0, rv.argument1, rv.argument2)
704                                }
705                                // There is a registered callback function, but
706                                // since the process used `Yield-WaitFor`, we do
707                                // not execute it, we just return its arguments
708                                // values to the application.
709                                Task::FunctionCall(ccb) => {
710                                    if config::CONFIG.trace_syscalls {
711                                        debug!(
712                                            "[{:?}] Yield-WaitFor [Suppressed function_call @{:#x}] ({:#x}, {:#x}, {:#x}, {:#x})",
713                                            process.processid(),
714                                            ccb.pc,
715                                            ccb.argument0,
716                                            ccb.argument1,
717                                            ccb.argument2,
718                                            ccb.argument3,
719                                        );
720                                    }
721                                    (ccb.argument0, ccb.argument1, ccb.argument2)
722                                }
723                                Task::IPC(_) => todo!(),
724                            };
725                            process
726                                .set_syscall_return_value(SyscallReturn::YieldWaitFor(a0, a1, a2));
727                        }
728                    }
729                }
730                process::State::Faulted | process::State::Terminated => {
731                    // We should never be scheduling an unrunnable process.
732                    // This is a potential security flaw: panic.
733                    panic!("Attempted to schedule an unrunnable process");
734                }
735                process::State::Stopped(_) => {
736                    return_reason = process::StoppedExecutingReason::Stopped;
737                    break;
738                }
739            }
740        }
741
742        // Check how much time the process used while it was executing, and
743        // return the value so we can provide it to the scheduler.
744        let time_executed_us = timeslice_us.map(|timeslice| {
745            // Note, we cannot call `.get_remaining_us()` again if it has
746            // previously returned `None`, so we _must_ check the return reason
747            // first.
748            if return_reason == process::StoppedExecutingReason::TimesliceExpired {
749                // used the whole timeslice
750                timeslice.get()
751            } else {
752                match scheduler_timer.get_remaining_us() {
753                    Some(remaining) => timeslice.get() - remaining.get(),
754                    None => timeslice.get(), // used whole timeslice
755                }
756            }
757        });
758
759        // Reset the scheduler timer in case it unconditionally triggers
760        // interrupts upon expiration. We do not want it to expire while the
761        // chip is sleeping, for example.
762        scheduler_timer.reset();
763
764        (return_reason, time_executed_us)
765    }
766
767    /// Method to invoke a system call on a particular process. Applies the
768    /// kernel system call filtering policy (if any). Handles `Yield` and
769    /// `Exit`, dispatches `Memop` to `memop::memop`, and dispatches peripheral
770    /// driver system calls to peripheral driver capsules through the platforms
771    /// `with_driver` method.
772    #[inline]
773    fn handle_syscall<KR: KernelResources<C>, C: Chip>(
774        &self,
775        resources: &KR,
776        process: &dyn process::Process,
777        syscall: Syscall,
778    ) {
779        // Hook for process debugging.
780        process.debug_syscall_called(syscall);
781
782        // Enforce platform-specific syscall filtering here.
783        //
784        // Before continuing to handle non-yield syscalls the kernel first
785        // checks if the platform wants to block that syscall for the process,
786        // and if it does, sets a return value which is returned to the calling
787        // process.
788        //
789        // Filtering a syscall (i.e. blocking the syscall from running) does not
790        // cause the process to lose its timeslice. The error will be returned
791        // immediately (assuming the process has not already exhausted its
792        // timeslice) allowing the process to decide how to handle the error.
793        match syscall {
794            Syscall::Yield { yield_type: _ } => {} // Yield is not filterable.
795            Syscall::Exit {
796                which: _,
797                completion_code: _,
798            } => {} // Exit is not filterable.
799            Syscall::Memop {
800                operand: _,
801                arg0: _,
802            } => {} // Memop is not filterable.
803            _ => {
804                // Check all other syscalls for filtering.
805                if let Err(response) = resources.syscall_filter().filter_syscall(process, &syscall)
806                {
807                    process.set_syscall_return_value(SyscallReturn::Failure(response));
808
809                    if config::CONFIG.trace_syscalls {
810                        debug!(
811                            "[{:?}] Filtered: {:?} was rejected with {:?}",
812                            process.processid(),
813                            syscall,
814                            response
815                        );
816                    }
817
818                    return;
819                }
820            }
821        }
822
823        // Handle each of the syscalls.
824        match syscall {
825            Syscall::Memop { operand, arg0 } => {
826                let rval = memop::memop(process, operand, arg0);
827                if config::CONFIG.trace_syscalls {
828                    debug!(
829                        "[{:?}] memop({}, {:#x}) = {:?}",
830                        process.processid(),
831                        operand,
832                        arg0,
833                        rval
834                    );
835                }
836                process.set_syscall_return_value(rval);
837            }
838            Syscall::Yield { yield_type } => {
839                if config::CONFIG.trace_syscalls {
840                    debug!("[{:?}] yield. which: {}", process.processid(), yield_type);
841                }
842                match yield_type {
843                    YieldVariant::NoWait { ptr } => {
844                        // If this is a `Yield-NoWait` AND there are no pending
845                        // tasks, then return immediately. Otherwise, go into
846                        // the yielded state and execute tasks now or when they
847                        // arrive.
848                        let has_tasks = process.has_tasks();
849
850                        // Set the "did I trigger upcalls" flag.
851                        // If address is invalid does nothing.
852                        //
853                        // SAFETY: This is fine as long as no references to the
854                        // process's memory exist. We do not have a reference,
855                        // so we can safely call `set_byte()`.
856                        unsafe {
857                            process.set_byte(ptr, has_tasks as u8);
858                        }
859
860                        if has_tasks {
861                            process.set_yielded_state();
862                        }
863                    }
864
865                    YieldVariant::Wait => {
866                        process.set_yielded_state();
867                    }
868
869                    YieldVariant::WaitFor {
870                        driver_number,
871                        subdriver_number,
872                    } => {
873                        let upcall_id = UpcallId {
874                            driver_num: driver_number,
875                            subscribe_num: subdriver_number,
876                        };
877                        process.set_yielded_for_state(upcall_id);
878                    }
879                }
880            }
881            Syscall::Subscribe { driver_number, .. }
882            | Syscall::Command { driver_number, .. }
883            | Syscall::ReadWriteAllow { driver_number, .. }
884            | Syscall::UserspaceReadableAllow { driver_number, .. }
885            | Syscall::ReadOnlyAllow { driver_number, .. } => {
886                resources
887                .syscall_driver_lookup()
888                .with_driver(driver_number, |driver| match syscall {
889                    Syscall::Subscribe {
890                        driver_number,
891                        subdriver_number,
892                        upcall_ptr,
893                        appdata,
894                    } => {
895                        // A upcall is identified as a tuple of the driver
896                        // number and the subdriver number.
897                        let upcall_id = UpcallId {
898                            driver_num: driver_number,
899                            subscribe_num: subdriver_number,
900                        };
901
902                        // TODO: when the compiler supports capability types
903                        // bring this back as a NonNull
904                        // type. https://github.com/tock/tock/issues/4134.
905                        //
906                        // Previously, we had a NonNull type (that had a niche)
907                        // here, and could wrap that in Option to fill the niche
908                        // and handle the Null case. CapabilityPtr is filling
909                        // the gap left by * const(), which does not have the
910                        // niche and allows NULL internally. Having a CHERI
911                        // capability type with a niche is (maybe?) predicated
912                        // on having better compiler support.
913                        // Option<NonNull<()>> is preferable here, and it should
914                        // go back to it just as soon as we can express "non
915                        // null capability". For now, checking for the null case
916                        // is handled internally in each `map_or` call.
917                        //
918                        //First check if `upcall_ptr` is null. A null
919                        //`upcall_ptr` will result in `None` here and
920                        //represents the special "unsubscribe" operation.
921                        //let ptr = NonNull::new(upcall_ptr);
922
923                        // For convenience create an `Upcall` type now. This is
924                        // just a data structure and doesn't do any checking or
925                        // conversion.
926                        let upcall = Upcall::new(process.processid(), upcall_id, appdata, upcall_ptr);
927
928                        let rval = match driver {
929                            Some(driver) => {
930                                // If `ptr` is not null, we must first verify that the
931                                // upcall function pointer is within process accessible
932                                // memory. Per TRD104:
933                                //
934                                // > If the passed upcall is not valid (is outside
935                                // > process executable memory...), the kernel...MUST
936                                // > immediately return a failure with a error code of
937                                // > `INVALID`.
938                                let rval1 = upcall_ptr.map_or(None, |upcall_ptr_nonnull| {
939                                    if !process.is_valid_upcall_function_pointer(upcall_ptr_nonnull.as_ptr()) {
940                                        Some(ErrorCode::INVAL)
941                                    } else {
942                                        None
943                                    }
944                                });
945
946                                // If the upcall is either null or valid, then we
947                                // continue handling the upcall.
948                                match rval1 {
949                                    Some(err) => upcall.into_subscribe_failure(err),
950                                    None => {
951                                        // At this point we must save the new
952                                        // upcall and return the old. The
953                                        // upcalls are stored by the core kernel
954                                        // in the grant region so we can
955                                        // guarantee a correct upcall swap.
956                                        // However, we do need help with
957                                        // initially allocating the grant if
958                                        // this driver has never been used
959                                        // before.
960                                        //
961                                        // To avoid the overhead with checking
962                                        // for process liveness and grant
963                                        // allocation, we assume the grant is
964                                        // initially allocated. If it turns out
965                                        // it isn't we ask the capsule to
966                                        // allocate the grant.
967                                        match crate::grant::subscribe(process, upcall) {
968                                            Ok(upcall) => upcall.into_subscribe_success(),
969                                            Err((upcall, err @ ErrorCode::NOMEM)) => {
970                                                // If we get a memory error, we
971                                                // always try to allocate the
972                                                // grant since this could be the
973                                                // first time the grant is
974                                                // getting accessed.
975                                                match try_allocate_grant(driver, process) {
976                                                    AllocResult::NewAllocation => {
977                                                        // Now we try again. It
978                                                        // is possible that the
979                                                        // capsule did not
980                                                        // actually allocate the
981                                                        // grant, at which point
982                                                        // this will fail again
983                                                        // and we return an
984                                                        // error to userspace.
985                                                        match crate::grant::subscribe(
986                                                            process, upcall,
987                                                        ) {
988                                                            // An Ok() returns
989                                                            // the previous
990                                                            // upcall, while
991                                                            // Err() returns the
992                                                            // one that was just
993                                                            // passed.
994                                                            Ok(upcall) => {
995                                                                upcall.into_subscribe_success()
996                                                            }
997                                                            Err((upcall, err)) => {
998                                                                upcall.into_subscribe_failure(err)
999                                                            }
1000                                                        }
1001                                                    }
1002                                                    alloc_failure => {
1003                                                        // We didn't actually
1004                                                        // create a new alloc,
1005                                                        // so just error.
1006                                                        match (
1007                                                            config::CONFIG.trace_syscalls,
1008                                                            alloc_failure,
1009                                                        ) {
1010                                                            (true, AllocResult::NoAllocation) => {
1011                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1012                                                                           process.processid(), driver_number);
1013                                                            }
1014                                                            (true, AllocResult::SameAllocation) => {
1015                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1016                                                                           process.processid(), driver_number);
1017                                                            }
1018                                                            _ => {}
1019                                                        }
1020                                                        upcall.into_subscribe_failure(err)
1021                                                    }
1022                                                }
1023                                            }
1024                                            Err((upcall, err)) => {
1025                                                upcall.into_subscribe_failure(err)
1026                                            }
1027                                        }
1028                                    }
1029                                }
1030                            },
1031                            None => upcall.into_subscribe_failure(ErrorCode::NODEVICE),
1032                        };
1033
1034                        // Per TRD104, we only clear upcalls if the subscribe
1035                        // will return success. At this point we know the result
1036                        // and clear if necessary.
1037                        if rval.is_success() {
1038                            // Only one upcall should exist per tuple. To ensure
1039                            // that there are no pending upcalls with the same
1040                            // identifier but with the old function pointer, we
1041                            // clear them now.
1042                            let _ =process.remove_pending_upcalls(upcall_id);
1043                        }
1044
1045                        if config::CONFIG.trace_syscalls {
1046                            debug!(
1047                                "[{:?}] subscribe({:#x}, {}, @{:#x}, {:#x}) = {:?}",
1048                                process.processid(),
1049                                driver_number,
1050                                subdriver_number,
1051                                upcall_ptr,
1052                                appdata,
1053                                rval
1054                            );
1055                        }
1056
1057                        process.set_syscall_return_value(rval);
1058                    }
1059                    Syscall::Command {
1060                        driver_number,
1061                        subdriver_number,
1062                        arg0,
1063                        arg1,
1064                    } => {
1065                        let cres = match driver {
1066                            Some(d) => d.command(subdriver_number, arg0, arg1, process.processid()),
1067                            None => CommandReturn::failure(ErrorCode::NODEVICE),
1068                        };
1069
1070                        let res = SyscallReturn::from_command_return(cres);
1071
1072                        if config::CONFIG.trace_syscalls {
1073                            debug!(
1074                                "[{:?}] cmd({:#x}, {}, {:#x}, {:#x}) = {:?}",
1075                                process.processid(),
1076                                driver_number,
1077                                subdriver_number,
1078                                arg0,
1079                                arg1,
1080                                res,
1081                            );
1082                        }
1083                        process.set_syscall_return_value(res);
1084                    }
1085                    Syscall::ReadWriteAllow {
1086                        driver_number,
1087                        subdriver_number,
1088                        allow_address,
1089                        allow_size,
1090                    } => {
1091                        let res = match driver {
1092                            Some(driver) => {
1093                                // Try to create an appropriate
1094                                // [`ReadWriteProcessBuffer`]. This method will
1095                                // ensure that the memory in question is located
1096                                // in the process-accessible memory space.
1097                                match process
1098                                    .build_readwrite_process_buffer(allow_address, allow_size)
1099                                {
1100                                    Ok(rw_pbuf) => {
1101                                        // Creating the
1102                                        // [`ReadWriteProcessBuffer`] worked,
1103                                        // try to set in grant.
1104                                        match crate::grant::allow_rw(
1105                                            process,
1106                                            driver_number,
1107                                            subdriver_number,
1108                                            rw_pbuf,
1109                                        ) {
1110                                            Ok(rw_pbuf) => {
1111                                                let (ptr, len) = rw_pbuf.consume();
1112                                                SyscallReturn::AllowReadWriteSuccess(ptr, len)
1113                                            }
1114                                            Err((rw_pbuf, err @ ErrorCode::NOMEM)) => {
1115                                                // If we get a memory error, we
1116                                                // always try to allocate the
1117                                                // grant since this could be the
1118                                                // first time the grant is
1119                                                // getting accessed.
1120                                                match try_allocate_grant(driver, process) {
1121                                                    AllocResult::NewAllocation => {
1122                                                        // If we actually
1123                                                        // allocated a new
1124                                                        // grant, try again and
1125                                                        // honor the result.
1126                                                        match crate::grant::allow_rw(
1127                                                            process,
1128                                                            driver_number,
1129                                                            subdriver_number,
1130                                                            rw_pbuf,
1131                                                        ) {
1132                                                            Ok(rw_pbuf) => {
1133                                                                let (ptr, len) = rw_pbuf.consume();
1134                                                                SyscallReturn::AllowReadWriteSuccess(
1135                                                                    ptr, len,
1136                                                                )
1137                                                            }
1138                                                            Err((rw_pbuf, err)) => {
1139                                                                let (ptr, len) = rw_pbuf.consume();
1140                                                                SyscallReturn::AllowReadWriteFailure(
1141                                                                    err, ptr, len,
1142                                                                )
1143                                                            }
1144                                                        }
1145                                                    }
1146                                                    alloc_failure => {
1147                                                        // We didn't actually
1148                                                        // create a new alloc,
1149                                                        // so just error.
1150                                                        match (
1151                                                            config::CONFIG.trace_syscalls,
1152                                                            alloc_failure,
1153                                                        ) {
1154                                                            (true, AllocResult::NoAllocation) => {
1155                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1156                                                                           process.processid(), driver_number);
1157                                                            }
1158                                                            (true, AllocResult::SameAllocation) => {
1159                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1160                                                                           process.processid(), driver_number);
1161                                                            }
1162                                                            _ => {}
1163                                                        }
1164                                                        let (ptr, len) = rw_pbuf.consume();
1165                                                        SyscallReturn::AllowReadWriteFailure(
1166                                                            err, ptr, len,
1167                                                        )
1168                                                    }
1169                                                }
1170                                            }
1171                                            Err((rw_pbuf, err)) => {
1172                                                let (ptr, len) = rw_pbuf.consume();
1173                                                SyscallReturn::AllowReadWriteFailure(err, ptr, len)
1174                                            }
1175                                        }
1176                                    }
1177                                    Err(allow_error) => {
1178                                        // There was an error creating the
1179                                        // [`ReadWriteProcessBuffer`]. Report
1180                                        // back to the process with the original
1181                                        // parameters.
1182                                        SyscallReturn::AllowReadWriteFailure(
1183                                            allow_error,
1184                                            allow_address,
1185                                            allow_size,
1186                                        )
1187                                    }
1188                                }
1189                            }
1190                            None => SyscallReturn::AllowReadWriteFailure(
1191                                ErrorCode::NODEVICE,
1192                                allow_address,
1193                                allow_size,
1194                            ),
1195                        };
1196
1197                        if config::CONFIG.trace_syscalls {
1198                            debug!(
1199                                "[{:?}] read-write allow({:#x}, {}, @{:#x}, {}) = {:?}",
1200                                process.processid(),
1201                                driver_number,
1202                                subdriver_number,
1203                                allow_address as usize,
1204                                allow_size,
1205                                res
1206                            );
1207                        }
1208                        process.set_syscall_return_value(res);
1209                    }
1210                    Syscall::UserspaceReadableAllow {
1211                        driver_number,
1212                        subdriver_number,
1213                        allow_address,
1214                        allow_size,
1215                    } => {
1216                        let res = match driver {
1217                            Some(d) => {
1218                                // Try to create an appropriate
1219                                // [`UserspaceReadableProcessBuffer`]. This
1220                                // method will ensure that the memory in
1221                                // question is located in the process-accessible
1222                                // memory space.
1223                                match process
1224                                    .build_readwrite_process_buffer(allow_address, allow_size)
1225                                {
1226                                    Ok(rw_pbuf) => {
1227                                        // Creating the
1228                                        // [`UserspaceReadableProcessBuffer`]
1229                                        // worked, provide it to the capsule.
1230                                        match d.allow_userspace_readable(
1231                                            process.processid(),
1232                                            subdriver_number,
1233                                            rw_pbuf,
1234                                        ) {
1235                                            Ok(returned_pbuf) => {
1236                                                // The capsule has accepted the
1237                                                // allow operation. Pass the
1238                                                // previous buffer information
1239                                                // back to the process.
1240                                                let (ptr, len) = returned_pbuf.consume();
1241                                                SyscallReturn::UserspaceReadableAllowSuccess(
1242                                                    ptr, len,
1243                                                )
1244                                            }
1245                                            Err((rejected_pbuf, err)) => {
1246                                                // The capsule has rejected the
1247                                                // allow operation. Pass the new
1248                                                // buffer information back to
1249                                                // the process.
1250                                                let (ptr, len) = rejected_pbuf.consume();
1251                                                SyscallReturn::UserspaceReadableAllowFailure(
1252                                                    err, ptr, len,
1253                                                )
1254                                            }
1255                                        }
1256                                    }
1257                                    Err(allow_error) => {
1258                                        // There was an error creating the
1259                                        // [`UserspaceReadableProcessBuffer`].
1260                                        // Report back to the process.
1261                                        SyscallReturn::UserspaceReadableAllowFailure(
1262                                            allow_error,
1263                                            allow_address,
1264                                            allow_size,
1265                                        )
1266                                    }
1267                                }
1268                            }
1269
1270                            None => SyscallReturn::UserspaceReadableAllowFailure(
1271                                ErrorCode::NODEVICE,
1272                                allow_address,
1273                                allow_size,
1274                            ),
1275                        };
1276
1277                        if config::CONFIG.trace_syscalls {
1278                            debug!(
1279                                "[{:?}] userspace readable allow({:#x}, {}, @{:#x}, {}) = {:?}",
1280                                process.processid(),
1281                                driver_number,
1282                                subdriver_number,
1283                                allow_address as usize,
1284                                allow_size,
1285                                res
1286                            );
1287                        }
1288                        process.set_syscall_return_value(res);
1289                    }
1290                    Syscall::ReadOnlyAllow {
1291                        driver_number,
1292                        subdriver_number,
1293                        allow_address,
1294                        allow_size,
1295                    } => {
1296                        let res = match driver {
1297                            Some(driver) => {
1298                                // Try to create an appropriate
1299                                // [`ReadOnlyProcessBuffer`]. This method will
1300                                // ensure that the memory in question is located
1301                                // in the process-accessible memory space.
1302                                match process
1303                                    .build_readonly_process_buffer(allow_address, allow_size)
1304                                {
1305                                    Ok(ro_pbuf) => {
1306                                        // Creating the
1307                                        // [`ReadOnlyProcessBuffer`] worked, try
1308                                        // to set in grant.
1309                                        match crate::grant::allow_ro(
1310                                            process,
1311                                            driver_number,
1312                                            subdriver_number,
1313                                            ro_pbuf,
1314                                        ) {
1315                                            Ok(ro_pbuf) => {
1316                                                let (ptr, len) = ro_pbuf.consume();
1317                                                SyscallReturn::AllowReadOnlySuccess(ptr, len)
1318                                            }
1319                                            Err((ro_pbuf, err @ ErrorCode::NOMEM)) => {
1320                                                // If we get a memory error, we
1321                                                // always try to allocate the
1322                                                // grant since this could be the
1323                                                // first time the grant is
1324                                                // getting accessed.
1325                                                match try_allocate_grant(driver, process) {
1326                                                    AllocResult::NewAllocation => {
1327                                                        // If we actually
1328                                                        // allocated a new
1329                                                        // grant, try again and
1330                                                        // honor the result.
1331                                                        match crate::grant::allow_ro(
1332                                                            process,
1333                                                            driver_number,
1334                                                            subdriver_number,
1335                                                            ro_pbuf,
1336                                                        ) {
1337                                                            Ok(ro_pbuf) => {
1338                                                                let (ptr, len) = ro_pbuf.consume();
1339                                                                SyscallReturn::AllowReadOnlySuccess(
1340                                                                    ptr, len,
1341                                                                )
1342                                                            }
1343                                                            Err((ro_pbuf, err)) => {
1344                                                                let (ptr, len) = ro_pbuf.consume();
1345                                                                SyscallReturn::AllowReadOnlyFailure(
1346                                                                    err, ptr, len,
1347                                                                )
1348                                                            }
1349                                                        }
1350                                                    }
1351                                                    alloc_failure => {
1352                                                        // We didn't actually
1353                                                        // create a new alloc,
1354                                                        // so just error.
1355                                                        match (
1356                                                            config::CONFIG.trace_syscalls,
1357                                                            alloc_failure,
1358                                                        ) {
1359                                                            (true, AllocResult::NoAllocation) => {
1360                                                                debug!("[{:?}] WARN driver #{:x} did not allocate grant",
1361                                                                           process.processid(), driver_number);
1362                                                            }
1363                                                            (true, AllocResult::SameAllocation) => {
1364                                                                debug!("[{:?}] ERROR driver #{:x} allocated wrong grant counts",
1365                                                                           process.processid(), driver_number);
1366                                                            }
1367                                                            _ => {}
1368                                                        }
1369                                                        let (ptr, len) = ro_pbuf.consume();
1370                                                        SyscallReturn::AllowReadOnlyFailure(
1371                                                            err, ptr, len,
1372                                                        )
1373                                                    }
1374                                                }
1375                                            }
1376                                            Err((ro_pbuf, err)) => {
1377                                                let (ptr, len) = ro_pbuf.consume();
1378                                                SyscallReturn::AllowReadOnlyFailure(err, ptr, len)
1379                                            }
1380                                        }
1381                                    }
1382                                    Err(allow_error) => {
1383                                        // There was an error creating the
1384                                        // [`ReadOnlyProcessBuffer`]. Report
1385                                        // back to the process with the original
1386                                        // parameters.
1387                                        SyscallReturn::AllowReadOnlyFailure(
1388                                            allow_error,
1389                                            allow_address,
1390                                            allow_size,
1391                                        )
1392                                    }
1393                                }
1394                            }
1395                            None => SyscallReturn::AllowReadOnlyFailure(
1396                                ErrorCode::NODEVICE,
1397                                allow_address,
1398                                allow_size,
1399                            ),
1400                        };
1401
1402                        if config::CONFIG.trace_syscalls {
1403                            debug!(
1404                                "[{:?}] read-only allow({:#x}, {}, @{:#x}, {}) = {:?}",
1405                                process.processid(),
1406                                driver_number,
1407                                subdriver_number,
1408                                allow_address as usize,
1409                                allow_size,
1410                                res
1411                            );
1412                        }
1413
1414                        process.set_syscall_return_value(res);
1415                    }
1416                    Syscall::Yield { .. }
1417                    | Syscall::Exit { .. }
1418                    | Syscall::Memop { .. } => {
1419                        // These variants must not be reachable due to the outer
1420                        // match statement:
1421                        debug_assert!(false, "Kernel system call handling invariant violated!");
1422                    },
1423                })
1424            }
1425            Syscall::Exit {
1426                which,
1427                completion_code,
1428            } => {
1429                // exit try restart modifies the ID of the process.
1430                let old_process_id = process.processid();
1431                let optional_return_value = match which {
1432                    // The process called the `exit-terminate` system call.
1433                    0 => {
1434                        process.terminate(Some(completion_code as u32));
1435                        None
1436                    }
1437                    // The process called the `exit-restart` system call.
1438                    1 => {
1439                        process.try_restart(Some(completion_code as u32));
1440                        None
1441                    }
1442                    // The process called an invalid variant of the Exit
1443                    // system call class.
1444                    _ => {
1445                        let return_value = SyscallReturn::Failure(ErrorCode::NOSUPPORT);
1446                        process.set_syscall_return_value(return_value);
1447                        Some(return_value)
1448                    }
1449                };
1450                if config::CONFIG.trace_syscalls {
1451                    debug!(
1452                        "[{:?}] exit(which: {}, completion_code: {}) = {:?}",
1453                        old_process_id, which, completion_code, optional_return_value,
1454                    );
1455                }
1456            }
1457        }
1458    }
1459}