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}