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