kernel/platform/chip.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 microcontrollers in Tock.
6
7use crate::platform::mpu;
8use crate::syscall;
9use crate::utilities::io_write::IoWrite;
10use core::fmt::Write;
11use core::panic::PanicInfo;
12
13/// Interface for individual MCUs.
14///
15/// The trait defines chip-specific properties of Tock's operation. These
16/// include whether and which memory protection mechanism and scheduler_timer to
17/// use, how to switch between the kernel and userland applications, and how to
18/// handle hardware events.
19///
20/// Each microcontroller should define a struct and implement this trait.
21pub trait Chip {
22 /// The particular Memory Protection Unit (MPU) for this chip.
23 type MPU: mpu::MPU;
24
25 /// Provider to query the currently running thread ID.
26 type ThreadIdProvider: ThreadIdProvider;
27
28 /// The implementation of the interface between userspace and the kernel for
29 /// this specific chip. Likely this is architecture specific, but individual
30 /// chips may have various custom requirements.
31 type UserspaceKernelBoundary: syscall::UserspaceKernelBoundary;
32
33 /// Run any necessary initialization for this chip.
34 ///
35 /// This should be called first by the board in its `main()` function.
36 ///
37 /// This should contain any necessary initialization steps, including:
38 ///
39 /// - Any architecture-specific setup
40 /// - Any errata fixes
41 /// - Any configuration needed for the chip to continue booting the kernel
42 ///
43 /// `init()` does not use `&self` so it can be called before the actual
44 /// `Chip` is instantiated. These initializations may be needed very soon
45 /// after the chip boots, and potentially much before the actual `Chip` can
46 /// be created.
47 ///
48 /// `init()` is marked safe as the implementation must handle any safety
49 /// requirements of the underlying initialization steps. As this will be
50 /// called very early when the chip boots, it is not useful or reasonable
51 /// for the kernel's main function to address any safety requirements.
52 fn init();
53
54 /// The kernel calls this function to tell the chip to check for all pending
55 /// interrupts and to correctly dispatch them to the peripheral drivers for
56 /// the chip.
57 ///
58 /// This function should loop internally until all interrupts have been
59 /// handled. It is ok, however, if an interrupt occurs after the last check
60 /// but before this function returns. The kernel will handle this edge case.
61 fn service_pending_interrupts(&self);
62
63 /// Ask the chip to check if there are any pending interrupts.
64 fn has_pending_interrupts(&self) -> bool;
65
66 /// Returns a reference to the implementation for the MPU on this chip.
67 fn mpu(&self) -> &Self::MPU;
68
69 /// Returns a reference to the implementation for the interface between
70 /// userspace and kernelspace.
71 fn userspace_kernel_boundary(&self) -> &Self::UserspaceKernelBoundary;
72
73 /// Called when there is nothing left for the chip to do and it should enter
74 /// a low power sleep state. This low power sleep state should allow
75 /// interrupts to still be active so that the next interrupt event wakes the
76 /// chip and resumes the scheduler.
77 fn sleep(&self);
78
79 /// Run a function in an atomic state w.r.t. to the current core. This
80 /// means that interrupts are disabled so that an interrupt will not fire
81 /// during the passed in function's execution, but *does not* make any
82 /// guarantees about memory consistency on a multi-core system.
83 fn with_interrupts_disabled<F, R>(&self, f: F) -> R
84 where
85 F: FnOnce() -> R;
86
87 /// Print out debug information about the current chip state (system
88 /// registers, MPU configuration, etc.) to a supplied writer.
89 ///
90 /// This function may be called across thread boundaries (such as from a
91 /// panic handler). As implementors of `Chip` do not have to be `Send` or
92 /// `Sync`, `&self` may not be available in these contexts. Therefore, this
93 /// function instead accepts an `Option<&Self>` parameter named `this`. In
94 /// contexts where `&self` is available, callers should invoke this function
95 /// by passing `Some(&self)` to `this`. Otherwise, `this` will be set to
96 /// `None`. The implementation of `print_state` may not print certain
97 /// information if it depends on runtime-accessible state in `Self`, but
98 /// that reference is not provided.
99 ///
100 /// # Safety
101 ///
102 /// Implementations may need to access low-level chip-specific hardware that
103 /// isn't safe to access during the normal operation of the chip. This
104 /// function may only be called from a panic context.
105 unsafe fn print_state(this: Option<&Self>, writer: &mut dyn Write);
106}
107
108/// Interface for retrieving the currently executing thread.
109///
110/// This is used to enforce correctness with shared state that has access
111/// restrictions (e.g., only a single thread can access a specific value).
112///
113/// Many embedded platforms are single-core and only permit a single execution
114/// thread at a time. However, interrupts can typically occur at any time, and
115/// the execution of an interrupt service routine (ISR) constitutes a second
116/// thread. Implementations of this trait must be able to differentiate between
117/// at minimum the main thread of execution and an ISR execution, but may also
118/// consider multiple execution threads if available on a particular device.
119///
120/// # Safety
121///
122/// This thread is marked as `unsafe` as implementation must guarantee its
123/// correctness. Users of this trait are allowed to make soundness guarantees
124/// based on the implementation being correct. Failing to provide a correct
125/// implementation can lead to unsound behavior. By implementing this trait,
126/// providers are guaranteeing the implementations are always correct for the
127/// given hardware platform.
128pub unsafe trait ThreadIdProvider {
129 /// Return a unique ID for the currently executing thread.
130 ///
131 /// The unique ID must fit in a `usize` and must be unique and consistent
132 /// for the currently running thread. The actual value is opaque and there
133 /// is no assumption about the meaning of the assigned IDs. Implementations
134 /// are allowed to arbitrarily assign IDs to threads as long as the IDs are
135 /// unique and consistent.
136 fn running_thread_id() -> usize;
137}
138
139/// Interface for handling interrupts on a hardware chip.
140///
141/// Each board must construct an implementation of this trait to handle specific
142/// interrupts. When an interrupt (identified by number) has triggered and
143/// should be handled, the implementation of this trait will be called with the
144/// interrupt number. The implementation can then handle the interrupt, or
145/// return `false` to signify that it does not know how to handle the interrupt.
146///
147/// This functionality is given this `InterruptService` interface so that
148/// multiple objects can be chained together to handle interrupts for a chip.
149/// This is useful for code organization and removing the need for duplication
150/// when multiple variations of a specific microcontroller exist. Then a shared,
151/// base object can handle most interrupts, and variation-specific objects can
152/// handle the variation-specific interrupts.
153///
154/// To simplify structuring the Rust code when using `InterruptService`, the
155/// interrupt number should be passed "top-down". That is, an interrupt to be
156/// handled will first be passed to the `InterruptService` object that is most
157/// specific. If that object cannot handle the interrupt, then it should
158/// maintain a reference to the second most specific object, and return by
159/// calling to that object to handle the interrupt. This continues until the
160/// base object handles the interrupt or decides that the chip does not know how
161/// to handle the interrupt. For example, consider a `nRF52840` chip that
162/// depends on the `nRF52` crate. If both have specific interrupts they know how
163/// to handle, the flow would look like:
164///
165/// ```ignore
166/// +---->nrf52840_peripherals
167/// | |
168/// | |
169/// | v
170/// kernel-->nrf52 nrf52_peripherals
171/// ```
172/// where the kernel instructs the `nrf52` crate to handle interrupts, and if
173/// there is an interrupt ready then that interrupt is passed through the
174/// InterruptService objects until something can service it.
175pub trait InterruptService {
176 /// Service an interrupt, if supported by this chip. If this interrupt
177 /// number is not supported, return false.
178 fn service_interrupt(&self, interrupt: u32) -> bool;
179}
180
181/// A default implementation of `InterruptService` that handles nothing and returns `false`.
182impl InterruptService for () {
183 fn service_interrupt(&self, _interrupt: u32) -> bool {
184 false
185 }
186}
187
188/// Generic operations that clock-like things are expected to support.
189pub trait ClockInterface {
190 fn is_enabled(&self) -> bool;
191 fn enable(&self);
192 fn disable(&self);
193}
194
195/// Helper struct for interfaces that expect clocks, but have no clock control.
196pub struct NoClockControl {}
197impl ClockInterface for NoClockControl {
198 fn is_enabled(&self) -> bool {
199 true
200 }
201 fn enable(&self) {}
202 fn disable(&self) {}
203}
204
205/// Instance of NoClockControl for things that need references to
206/// `ClockInterface` objects.
207pub const NO_CLOCK_CONTROL: NoClockControl = NoClockControl {};
208
209/// Interface for chips to create a synchronous writer for panics.
210///
211/// Any mechanism that can output a panic message during a panic must implement
212/// [`PanicWriter`] to enable the `panic()` functions to write the output. This
213/// requires the mechanism to provide a new constructor for the writer that
214/// creates a synchronous writer that implements [`IoWrite`].
215///
216/// This is a dedicated trait because synchronous I/O is only used for panic
217/// handling. This allows chips to clearly separate synchronous implementations
218/// that are a special case only for panics.
219pub trait PanicWriter {
220 /// The configuration data the mechanism needs to configure the writer for
221 /// panic output.
222 type Config;
223
224 /// Create a new synchronous writer capable of sending panic messages.
225 ///
226 /// The writer must implement [`IoWrite`] (which is just `std:io::Write`
227 /// implemented for no_std).
228 ///
229 /// This function requires a [`PanicInfo`] reference, but the implementation
230 /// should not use it. This only enforces that this function is only called
231 /// from a panic context and not during normal operation.
232 fn create_panic_writer(
233 config: Self::Config,
234 _panic: &PanicInfo,
235 ) -> impl IoWrite + core::fmt::Write;
236}