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}