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;
15use tock_tbf::types::CommandPermissions;
16
17/// Combination trait that boards provide to the kernel that includes all of
18/// the extensible operations the kernel supports.
19///
20/// This is the primary method for configuring the kernel for a specific board.
21pub trait KernelResources<C: Chip> {
22    /// The implementation of the system call dispatch mechanism the kernel
23    /// will use.
24    type SyscallDriverLookup: SyscallDriverLookup;
25
26    /// The implementation of the system call filtering mechanism the kernel
27    /// will use.
28    type SyscallFilter: SyscallFilter;
29
30    /// The implementation of the process fault handling mechanism the kernel
31    /// will use.
32    type ProcessFault: ProcessFault;
33
34    /// The implementation of the context switch callback handler
35    /// the kernel will use.
36    type ContextSwitchCallback: ContextSwitchCallback;
37
38    /// The implementation of the scheduling algorithm the kernel will use.
39    type Scheduler: Scheduler<C>;
40
41    /// The implementation of the timer used to create the timeslices provided
42    /// to applications.
43    type SchedulerTimer: scheduler_timer::SchedulerTimer;
44
45    /// The implementation of the WatchDog timer used to monitor the running
46    /// of the kernel.
47    type WatchDog: watchdog::WatchDog;
48
49    /// Returns a reference to the implementation of the SyscallDriverLookup this
50    /// platform will use to route syscalls.
51    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup;
52
53    /// Returns a reference to the implementation of the SyscallFilter this
54    /// platform wants the kernel to use.
55    fn syscall_filter(&self) -> &Self::SyscallFilter;
56
57    /// Returns a reference to the implementation of the ProcessFault handler
58    /// this platform wants the kernel to use.
59    fn process_fault(&self) -> &Self::ProcessFault;
60
61    /// Returns a reference to the implementation of the ContextSwitchCallback
62    /// for this platform.
63    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback;
64
65    /// Returns a reference to the implementation of the Scheduler this platform
66    /// wants the kernel to use.
67    fn scheduler(&self) -> &Self::Scheduler;
68
69    /// Returns a reference to the implementation of the SchedulerTimer timer
70    /// for this platform.
71    fn scheduler_timer(&self) -> &Self::SchedulerTimer;
72
73    /// Returns a reference to the implementation of the WatchDog on this
74    /// platform.
75    fn watchdog(&self) -> &Self::WatchDog;
76}
77
78/// Configure the system call dispatch mapping.
79///
80/// Each board should define a struct which implements this trait. This trait is
81/// the core for how syscall dispatching is handled, and the implementation is
82/// responsible for dispatching to drivers for each system call number.
83///
84/// ## Example
85///
86/// ```ignore
87/// struct Hail {
88///     console: &'static capsules::console::Console<'static>,
89///     ipc: kernel::ipc::IPC,
90///     dac: &'static capsules::dac::Dac<'static>,
91/// }
92///
93/// impl SyscallDriverLookup for Hail {
94///     fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
95///     where
96///         F: FnOnce(Option<&dyn kernel::SyscallDriver>) -> R,
97///     {
98///         match driver_num {
99///             capsules::console::DRIVER_NUM => f(Some(self.console)),
100///             kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
101///             capsules::dac::DRIVER_NUM => f(Some(self.dac)),
102///
103///             _ => f(None),
104///         }
105///     }
106/// }
107/// ```
108pub trait SyscallDriverLookup {
109    /// Platform-specific mapping of syscall numbers to objects that implement
110    /// the Driver methods for that syscall.
111    ///
112    ///
113    /// An implementation
114    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
115    where
116        F: FnOnce(Option<&dyn SyscallDriver>) -> R;
117}
118
119/// Trait for implementing system call filters that the kernel uses to decide
120/// whether to handle a specific system call or not.
121pub trait SyscallFilter {
122    /// Check the platform-provided system call filter for all non-yield system
123    /// calls. If the system call is allowed for the provided process then
124    /// return `Ok(())`. Otherwise, return `Err()` with an `ErrorCode` that will
125    /// be returned to the calling application. The default implementation
126    /// allows all system calls.
127    ///
128    /// This API should be considered unstable, and is likely to change in the
129    /// future.
130    fn filter_syscall(
131        &self,
132        _process: &dyn process::Process,
133        _syscall: &syscall::Syscall,
134    ) -> Result<(), errorcode::ErrorCode> {
135        Ok(())
136    }
137}
138
139/// Implement default allow all SyscallFilter trait for unit.
140impl SyscallFilter for () {}
141
142/// An allow list system call filter based on the TBF header, with a default
143/// allow all fallback.
144///
145/// This will check if the process has TbfHeaderPermissions specified. If the
146/// process has TbfHeaderPermissions they will be used to determine access
147/// permissions. For details on this see the TockBinaryFormat documentation. If
148/// no permissions are specified the default is to allow the syscall.
149pub struct TbfHeaderFilterDefaultAllow {}
150
151/// Implement default SyscallFilter trait for filtering based on the TBF header.
152impl SyscallFilter for TbfHeaderFilterDefaultAllow {
153    fn filter_syscall(
154        &self,
155        process: &dyn process::Process,
156        syscall: &syscall::Syscall,
157    ) -> Result<(), errorcode::ErrorCode> {
158        match syscall {
159            // Subscribe is allowed if any commands are
160            syscall::Syscall::Subscribe {
161                driver_number,
162                subdriver_number: _,
163                upcall_ptr: _,
164                appdata: _,
165            } => match process.get_command_permissions(*driver_number, 0) {
166                CommandPermissions::NoPermsAtAll => Ok(()),
167                CommandPermissions::NoPermsThisDriver => Err(errorcode::ErrorCode::NODEVICE),
168                CommandPermissions::Mask(_allowed) => Ok(()),
169            },
170
171            syscall::Syscall::Command {
172                driver_number,
173                subdriver_number,
174                arg0: _,
175                arg1: _,
176            } => match process.get_command_permissions(*driver_number, subdriver_number / 64) {
177                CommandPermissions::NoPermsAtAll => Ok(()),
178                CommandPermissions::NoPermsThisDriver => Err(errorcode::ErrorCode::NODEVICE),
179                CommandPermissions::Mask(allowed) => {
180                    if (1 << (subdriver_number % 64)) & allowed > 0 {
181                        Ok(())
182                    } else {
183                        Err(errorcode::ErrorCode::NODEVICE)
184                    }
185                }
186            },
187
188            // Allow is allowed if any commands are
189            syscall::Syscall::ReadWriteAllow {
190                driver_number,
191                subdriver_number: _,
192                allow_address: _,
193                allow_size: _,
194            } => match process.get_command_permissions(*driver_number, 0) {
195                CommandPermissions::NoPermsAtAll => Ok(()),
196                CommandPermissions::NoPermsThisDriver => Err(errorcode::ErrorCode::NODEVICE),
197                CommandPermissions::Mask(_allowed) => Ok(()),
198            },
199
200            // Allow is allowed if any commands are
201            syscall::Syscall::UserspaceReadableAllow {
202                driver_number,
203                subdriver_number: _,
204                allow_address: _,
205                allow_size: _,
206            } => match process.get_command_permissions(*driver_number, 0) {
207                CommandPermissions::NoPermsAtAll => Ok(()),
208                CommandPermissions::NoPermsThisDriver => Err(errorcode::ErrorCode::NODEVICE),
209                CommandPermissions::Mask(_allowed) => Ok(()),
210            },
211
212            // Allow is allowed if any commands are
213            syscall::Syscall::ReadOnlyAllow {
214                driver_number,
215                subdriver_number: _,
216                allow_address: _,
217                allow_size: _,
218            } => match process.get_command_permissions(*driver_number, 0) {
219                CommandPermissions::NoPermsAtAll => Ok(()),
220                CommandPermissions::NoPermsThisDriver => Err(errorcode::ErrorCode::NODEVICE),
221                CommandPermissions::Mask(_allowed) => Ok(()),
222            },
223
224            // Non-filterable system calls
225            syscall::Syscall::Yield { .. }
226            | syscall::Syscall::Memop { .. }
227            | syscall::Syscall::Exit { .. } => Ok(()),
228        }
229    }
230}
231
232/// Trait for implementing process fault handlers to run when a process faults.
233pub trait ProcessFault {
234    /// This function is called when an app faults.
235    ///
236    /// This is an optional function that can be implemented by `Platform`s that
237    /// allows the chip to handle the app fault and not terminate or restart the
238    /// app.
239    ///
240    /// If `Ok(())` is returned by this function then the kernel will not
241    /// terminate or restart the app, but instead allow it to continue running.
242    /// NOTE in this case the chip must have fixed the underlying reason for
243    /// fault otherwise it will re-occur.
244    ///
245    /// This can not be used for apps to circumvent Tock's protections. If for
246    /// example this function just ignored the error and allowed the app to
247    /// continue the fault would continue to occur.
248    ///
249    /// If `Err(())` is returned then the kernel will set the app as faulted and
250    /// follow the `FaultResponse` protocol.
251    ///
252    /// It is unlikely a `Platform` will need to implement this. This should be
253    /// used for only a handful of use cases. Possible use cases include:
254    ///    - Allowing the kernel to emulate unimplemented instructions This
255    ///      could be used to allow apps to run on hardware that doesn't
256    ///      implement some instructions, for example atomics.
257    ///    - Allow the kernel to handle hardware faults, triggered by the app.
258    ///      This can allow an app to continue running if it triggers certain
259    ///      types of faults. For example if an app triggers a memory parity
260    ///      error the kernel can handle the error and allow the app to continue
261    ///      (or not).
262    ///    - Allow an app to execute from external QSPI. This could be used to
263    ///      allow an app to execute from external QSPI where access faults can
264    ///      be handled by the `Platform` to ensure the QPSI is mapped
265    ///      correctly.
266    #[allow(unused_variables)]
267    fn process_fault_hook(&self, process: &dyn process::Process) -> Result<(), ()> {
268        Err(())
269    }
270}
271
272/// Implement default ProcessFault trait for unit.
273impl ProcessFault for () {}
274
275/// Trait for implementing handlers on userspace context switches.
276pub trait ContextSwitchCallback {
277    /// This function is called before the kernel switches to a process.
278    ///
279    /// `process` is the app that is about to run
280    fn context_switch_hook(&self, process: &dyn process::Process);
281}
282
283/// Implement default ContextSwitchCallback trait for unit.
284impl ContextSwitchCallback for () {
285    fn context_switch_hook(&self, _process: &dyn process::Process) {}
286}