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 core::fmt::Write;
10
11/// Interface for individual MCUs.
12///
13/// The trait defines chip-specific properties of Tock's operation. These
14/// include whether and which memory protection mechanism and scheduler_timer to
15/// use, how to switch between the kernel and userland applications, and how to
16/// handle hardware events.
17///
18/// Each microcontroller should define a struct and implement this trait.
19pub trait Chip {
20 /// The particular Memory Protection Unit (MPU) for this chip.
21 type MPU: mpu::MPU;
22
23 /// Provider to query the currently running thread ID.
24 type ThreadIdProvider: ThreadIdProvider;
25
26 /// The implementation of the interface between userspace and the kernel for
27 /// this specific chip. Likely this is architecture specific, but individual
28 /// chips may have various custom requirements.
29 type UserspaceKernelBoundary: syscall::UserspaceKernelBoundary;
30
31 /// The kernel calls this function to tell the chip to check for all pending
32 /// interrupts and to correctly dispatch them to the peripheral drivers for
33 /// the chip.
34 ///
35 /// This function should loop internally until all interrupts have been
36 /// handled. It is ok, however, if an interrupt occurs after the last check
37 /// but before this function returns. The kernel will handle this edge case.
38 fn service_pending_interrupts(&self);
39
40 /// Ask the chip to check if there are any pending interrupts.
41 fn has_pending_interrupts(&self) -> bool;
42
43 /// Returns a reference to the implementation for the MPU on this chip.
44 fn mpu(&self) -> &Self::MPU;
45
46 /// Returns a reference to the implementation for the interface between
47 /// userspace and kernelspace.
48 fn userspace_kernel_boundary(&self) -> &Self::UserspaceKernelBoundary;
49
50 /// Called when there is nothing left for the chip to do and it should enter
51 /// a low power sleep state. This low power sleep state should allow
52 /// interrupts to still be active so that the next interrupt event wakes the
53 /// chip and resumes the scheduler.
54 fn sleep(&self);
55
56 /// Run a function in an atomic state w.r.t. to the current core. This
57 /// means that interrupts are disabled so that an interrupt will not fire
58 /// during the passed in function's execution, but *does not* make any
59 /// guarantees about memory consistency on a multi-core system.
60 unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
61 where
62 F: FnOnce() -> R;
63
64 /// Print out chip state (system registers) to a supplied
65 /// writer. This does not print out the execution context
66 /// (data registers), as this depends on how they are stored;
67 /// that is implemented by
68 /// `syscall::UserspaceKernelBoundary::print_context`.
69 /// This also does not print out a process memory state,
70 /// that is implemented by `process::Process::print_memory_map`.
71 /// The MPU state is printed by the MPU's implementation of
72 /// the Display trait.
73 /// Used by panic.
74 unsafe fn print_state(&self, writer: &mut dyn Write);
75}
76
77/// Interface for retrieving the currently executing thread.
78///
79/// This is used to enforce correctness with shared state that has access
80/// restrictions (e.g., only a single thread can access a specific value).
81///
82/// Many embedded platforms are single-core and only permit a single execution
83/// thread at a time. However, interrupts can typically occur at any time, and
84/// the execution of an interrupt service routine (ISR) constitutes a second
85/// thread. Implementations of this trait must be able to differentiate between
86/// at minimum the main thread of execution and an ISR execution, but may also
87/// consider multiple execution threads if available on a particular device.
88///
89/// # Safety
90///
91/// This thread is marked as `unsafe` as implementation must guarantee its
92/// correctness. Users of this trait are allowed to make soundness guarantees
93/// based on the implementation being correct. Failing to provide a correct
94/// implementation can lead to unsound behavior. By implementing this trait,
95/// providers are guaranteeing the implementations are always correct for the
96/// given hardware platform.
97pub unsafe trait ThreadIdProvider {
98 /// Return a unique ID for the currently executing thread.
99 ///
100 /// The unique ID must fit in a `usize` and must be unique and consistent
101 /// for the currently running thread. The actual value is opaque and there
102 /// is no assumption about the meaning of the assigned IDs. Implementations
103 /// are allowed to arbitrarily assign IDs to threads as long as the IDs are
104 /// unique and consistent.
105 fn running_thread_id() -> usize;
106}
107
108/// Interface for handling interrupts on a hardware chip.
109///
110/// Each board must construct an implementation of this trait to handle specific
111/// interrupts. When an interrupt (identified by number) has triggered and
112/// should be handled, the implementation of this trait will be called with the
113/// interrupt number. The implementation can then handle the interrupt, or
114/// return `false` to signify that it does not know how to handle the interrupt.
115///
116/// This functionality is given this `InterruptService` interface so that
117/// multiple objects can be chained together to handle interrupts for a chip.
118/// This is useful for code organization and removing the need for duplication
119/// when multiple variations of a specific microcontroller exist. Then a shared,
120/// base object can handle most interrupts, and variation-specific objects can
121/// handle the variation-specific interrupts.
122///
123/// To simplify structuring the Rust code when using `InterruptService`, the
124/// interrupt number should be passed "top-down". That is, an interrupt to be
125/// handled will first be passed to the `InterruptService` object that is most
126/// specific. If that object cannot handle the interrupt, then it should
127/// maintain a reference to the second most specific object, and return by
128/// calling to that object to handle the interrupt. This continues until the
129/// base object handles the interrupt or decides that the chip does not know how
130/// to handle the interrupt. For example, consider a `nRF52840` chip that
131/// depends on the `nRF52` crate. If both have specific interrupts they know how
132/// to handle, the flow would look like:
133///
134/// ```ignore
135/// +---->nrf52840_peripherals
136/// | |
137/// | |
138/// | v
139/// kernel-->nrf52 nrf52_peripherals
140/// ```
141/// where the kernel instructs the `nrf52` crate to handle interrupts, and if
142/// there is an interrupt ready then that interrupt is passed through the
143/// InterruptService objects until something can service it.
144pub trait InterruptService {
145 /// Service an interrupt, if supported by this chip. If this interrupt
146 /// number is not supported, return false.
147 unsafe fn service_interrupt(&self, interrupt: u32) -> bool;
148}
149
150/// A default implementation of `InterruptService` that handles nothing and returns `false`.
151impl InterruptService for () {
152 unsafe fn service_interrupt(&self, _interrupt: u32) -> bool {
153 false
154 }
155}
156
157/// Generic operations that clock-like things are expected to support.
158pub trait ClockInterface {
159 fn is_enabled(&self) -> bool;
160 fn enable(&self);
161 fn disable(&self);
162}
163
164/// Helper struct for interfaces that expect clocks, but have no clock control.
165pub struct NoClockControl {}
166impl ClockInterface for NoClockControl {
167 fn is_enabled(&self) -> bool {
168 true
169 }
170 fn enable(&self) {}
171 fn disable(&self) {}
172}
173
174/// Instance of NoClockControl for things that need references to
175/// `ClockInterface` objects.
176pub const NO_CLOCK_CONTROL: NoClockControl = NoClockControl {};