kernel/platform/platform.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//! Interfaces for implementing boards in Tock.
6
7use crate::errorcode;
8use crate::platform::chip::Chip;
9use crate::platform::scheduler_timer;
10use crate::platform::watchdog;
11use crate::process;
12use crate::scheduler::Scheduler;
13use crate::syscall;
14use crate::syscall_driver::SyscallDriver;
15
16/// Combination trait that boards provide to the kernel that includes all of
17/// the extensible operations the kernel supports.
18///
19/// This is the primary method for configuring the kernel for a specific board.
20pub trait KernelResources<C: Chip> {
21    /// The implementation of the system call dispatch mechanism the kernel
22    /// will use.
23    type SyscallDriverLookup: SyscallDriverLookup;
24
25    /// The implementation of the system call filtering mechanism the kernel
26    /// will use.
27    type SyscallFilter: SyscallFilter;
28
29    /// The implementation of the process fault handling mechanism the kernel
30    /// will use.
31    type ProcessFault: ProcessFault;
32
33    /// The implementation of the context switch callback handler
34    /// the kernel will use.
35    type ContextSwitchCallback: ContextSwitchCallback;
36
37    /// The implementation of the scheduling algorithm the kernel will use.
38    type Scheduler: Scheduler<C>;
39
40    /// The implementation of the timer used to create the timeslices provided
41    /// to applications.
42    type SchedulerTimer: scheduler_timer::SchedulerTimer;
43
44    /// The implementation of the WatchDog timer used to monitor the running
45    /// of the kernel.
46    type WatchDog: watchdog::WatchDog;
47
48    /// Returns a reference to the implementation of the SyscallDriverLookup this
49    /// platform will use to route syscalls.
50    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup;
51
52    /// Returns a reference to the implementation of the SyscallFilter this
53    /// platform wants the kernel to use.
54    fn syscall_filter(&self) -> &Self::SyscallFilter;
55
56    /// Returns a reference to the implementation of the ProcessFault handler
57    /// this platform wants the kernel to use.
58    fn process_fault(&self) -> &Self::ProcessFault;
59
60    /// Returns a reference to the implementation of the ContextSwitchCallback
61    /// for this platform.
62    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback;
63
64    /// Returns a reference to the implementation of the Scheduler this platform
65    /// wants the kernel to use.
66    fn scheduler(&self) -> &Self::Scheduler;
67
68    /// Returns a reference to the implementation of the SchedulerTimer timer
69    /// for this platform.
70    fn scheduler_timer(&self) -> &Self::SchedulerTimer;
71
72    /// Returns a reference to the implementation of the WatchDog on this
73    /// platform.
74    fn watchdog(&self) -> &Self::WatchDog;
75}
76
77/// Configure the system call dispatch mapping.
78///
79/// Each board should define a struct which implements this trait. This trait is
80/// the core for how syscall dispatching is handled, and the implementation is
81/// responsible for dispatching to drivers for each system call number.
82///
83/// ## Example
84///
85/// ```ignore
86/// struct Hail {
87///     console: &'static capsules::console::Console<'static>,
88///     ipc: kernel::ipc::IPC,
89///     dac: &'static capsules::dac::Dac<'static>,
90/// }
91///
92/// impl SyscallDriverLookup for Hail {
93///     fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
94///     where
95///         F: FnOnce(Option<&dyn kernel::SyscallDriver>) -> R,
96///     {
97///         match driver_num {
98///             capsules::console::DRIVER_NUM => f(Some(self.console)),
99///             kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
100///             capsules::dac::DRIVER_NUM => f(Some(self.dac)),
101///
102///             _ => f(None),
103///         }
104///     }
105/// }
106/// ```
107pub trait SyscallDriverLookup {
108    /// Platform-specific mapping of syscall numbers to objects that implement
109    /// the Driver methods for that syscall.
110    ///
111    ///
112    /// An implementation
113    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
114    where
115        F: FnOnce(Option<&dyn SyscallDriver>) -> R;
116}
117
118/// Trait for implementing system call filters that the kernel uses to decide
119/// whether to handle a specific system call or not.
120pub trait SyscallFilter {
121    /// Check the platform-provided system call filter for all non-yield system
122    /// calls. If the system call is allowed for the provided process then
123    /// return `Ok(())`. Otherwise, return `Err()` with an `ErrorCode` that will
124    /// be returned to the calling application. The default implementation
125    /// allows all system calls.
126    ///
127    /// This API should be considered unstable, and is likely to change in the
128    /// future.
129    fn filter_syscall(
130        &self,
131        _process: &dyn process::Process,
132        _syscall: &syscall::Syscall,
133    ) -> Result<(), errorcode::ErrorCode> {
134        Ok(())
135    }
136}
137
138/// Implement default allow all SyscallFilter trait for unit.
139impl SyscallFilter for () {}
140
141/// Trait for implementing process fault handlers to run when a process faults.
142pub trait ProcessFault {
143    /// This function is called when an app faults.
144    ///
145    /// This is an optional function that can be implemented by `Platform`s that
146    /// allows the chip to handle the app fault and not terminate or restart the
147    /// app.
148    ///
149    /// If `Ok(())` is returned by this function then the kernel will not
150    /// terminate or restart the app, but instead allow it to continue running.
151    /// NOTE in this case the chip must have fixed the underlying reason for
152    /// fault otherwise it will re-occur.
153    ///
154    /// This can not be used for apps to circumvent Tock's protections. If for
155    /// example this function just ignored the error and allowed the app to
156    /// continue the fault would continue to occur.
157    ///
158    /// If `Err(())` is returned then the kernel will set the app as faulted and
159    /// follow the `FaultResponse` protocol.
160    ///
161    /// It is unlikely a `Platform` will need to implement this. This should be
162    /// used for only a handful of use cases. Possible use cases include:
163    ///    - Allowing the kernel to emulate unimplemented instructions This
164    ///      could be used to allow apps to run on hardware that doesn't
165    ///      implement some instructions, for example atomics.
166    ///    - Allow the kernel to handle hardware faults, triggered by the app.
167    ///      This can allow an app to continue running if it triggers certain
168    ///      types of faults. For example if an app triggers a memory parity
169    ///      error the kernel can handle the error and allow the app to continue
170    ///      (or not).
171    ///    - Allow an app to execute from external QSPI. This could be used to
172    ///      allow an app to execute from external QSPI where access faults can
173    ///      be handled by the `Platform` to ensure the QPSI is mapped
174    ///      correctly.
175    #[allow(unused_variables)]
176    fn process_fault_hook(&self, process: &dyn process::Process) -> Result<(), ()> {
177        Err(())
178    }
179}
180
181/// Implement default ProcessFault trait for unit.
182impl ProcessFault for () {}
183
184/// Trait for implementing handlers on userspace context switches.
185pub trait ContextSwitchCallback {
186    /// This function is called before the kernel switches to a process.
187    ///
188    /// `process` is the app that is about to run
189    fn context_switch_hook(&self, process: &dyn process::Process);
190}
191
192/// Implement default ContextSwitchCallback trait for unit.
193impl ContextSwitchCallback for () {
194    fn context_switch_hook(&self, _process: &dyn process::Process) {}
195}