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