cortexm/
lib.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//! Generic support for all Cortex-M platforms.
6
7#![no_std]
8
9use core::fmt::Write;
10
11pub mod dcb;
12pub mod dwt;
13pub mod mpu;
14pub mod nvic;
15pub mod scb;
16pub mod support;
17pub mod syscall;
18pub mod systick;
19pub mod thread_id;
20
21// These constants are defined in the linker script.
22extern "C" {
23    static _szero: *const u32;
24    static _ezero: *const u32;
25    static _etext: *const u32;
26    static _srelocate: *const u32;
27    static _erelocate: *const u32;
28}
29
30/// Trait to encapsulate differences in between Cortex-M variants
31///
32/// This trait contains functions and other associated data (constants) which
33/// differs in between different Cortex-M architecture variants (e.g. Cortex-M0,
34/// Cortex-M4, etc.). It is not designed to be implemented by an instantiable
35/// type and passed around as a runtime-accessible object, but is to be used as
36/// a well-defined collection of functions and data to be exposed to
37/// architecture-dependent code paths. Hence, implementations can use an `enum`
38/// without variants to implement this trait.
39///
40/// The fact that some functions are proper trait functions, while others are
41/// exposed via associated constants is necessitated by the associated const
42/// functions being `#\[naked\]`. To wrap these functions in proper trait
43/// functions would require these trait functions to also be `#\[naked\]` to
44/// avoid generating a function prologue and epilogue, and we'd have to call the
45/// respective backing function from within an asm! block. The associated
46/// constants allow us to simply reference any function in scope and export it
47/// through the provided CortexMVariant trait infrastructure.
48// This approach outlined above has some significant benefits over passing
49// functions via symbols, as done before this change (tock/tock#3080):
50//
51// - By using a trait carrying proper first-level Rust functions, the type
52//   signatures of the trait and implementing functions are properly validated.
53//   Before these changes, some Cortex-M variants previously used incorrect
54//   type signatures (e.g. `*mut u8` instead of `*const usize`) for the
55//   user_stack argument. It also ensures that all functions are provided by a
56//   respective sub-architecture at compile time, instead of throwing linker
57//   errors.
58//
59// - Determining the respective functions at compile time, Rust might be able to
60//   perform more aggressive inlining, especially if more device-specific
61//   proper Rust functions (non hardware-exposed symbols, i.e. not fault or
62//   interrupt handlers) were to be added.
63//
64// - Most importantly, this avoid ambiguity with respect to a compiler fence
65//   being inserted by the compiler around calls to switch_to_user. The asm!
66//   macro in that function call will cause Rust to emit a compiler fence given
67//   the nomem option is not passed, but the opaque extern "C" function call
68//   obscured that code path. While this is probably fine and Rust is obliged
69//   to generate a compiler fence when switching to C code, having a traceable
70//   code path for Rust to the asm! macro will remove any remaining ambiguity
71//   and allow us to argue against requiring volatile accesses to userspace
72//   memory(during context switches). See tock/tock#2582 for further discussion
73//   of this issue.
74pub trait CortexMVariant {
75    /// All ISRs not caught by a more specific handler are caught by this
76    /// handler. This must ensure the interrupt is disabled (per Tock's
77    /// interrupt model) and then as quickly as possible resume the main thread
78    /// (i.e. leave the interrupt context). The interrupt will be marked as
79    /// pending and handled when the scheduler checks if there are any pending
80    /// interrupts.
81    ///
82    /// If the ISR is called while an app is running, this will switch control
83    /// to the kernel.
84    const GENERIC_ISR: unsafe extern "C" fn();
85
86    /// The `systick_handler` is called when the systick interrupt occurs,
87    /// signaling that an application executed for longer than its timeslice.
88    /// This interrupt handler is no longer responsible for signaling to the
89    /// kernel thread that an interrupt has occurred, but is slightly more
90    /// efficient than the `generic_isr` handler on account of not needing to
91    /// mark the interrupt as pending.
92    const SYSTICK_HANDLER: unsafe extern "C" fn();
93
94    /// This is called after a `svc` instruction, both when switching to
95    /// userspace and when userspace makes a system call.
96    const SVC_HANDLER: unsafe extern "C" fn();
97
98    /// Hard fault handler.
99    const HARD_FAULT_HANDLER: unsafe extern "C" fn();
100
101    /// Assembly function called from `UserspaceKernelBoundary` to switch to an
102    /// an application. This handles storing and restoring application state
103    /// before and after the switch.
104    unsafe fn switch_to_user(
105        user_stack: *const usize,
106        process_regs: &mut [usize; 8],
107    ) -> *const usize;
108
109    /// Format and display architecture-specific state useful for debugging.
110    ///
111    /// This is generally used after a `panic!()` to aid debugging.
112    unsafe fn print_cortexm_state(writer: &mut dyn Write);
113}
114
115#[cfg(any(doc, all(target_arch = "arm", target_os = "none")))]
116pub unsafe extern "C" fn unhandled_interrupt() {
117    use core::arch::asm;
118    let mut interrupt_number: u32;
119
120    // IPSR[8:0] holds the currently active interrupt
121    asm!(
122        "
123    mrs r0, ipsr
124        ",
125        out("r0") interrupt_number,
126        options(nomem, nostack, preserves_flags),
127    );
128
129    interrupt_number &= 0x1ff;
130
131    panic!("Unhandled Interrupt. ISR {} is active.", interrupt_number);
132}
133
134/// Assembly function to initialize the .bss and .data sections in RAM.
135///
136/// We need to (unfortunately) do these operations in assembly because it is
137/// not valid to run Rust code without RAM initialized.
138///
139/// See <https://github.com/tock/tock/issues/2222> for more information.
140#[cfg(any(doc, all(target_arch = "arm", target_os = "none")))]
141#[unsafe(naked)]
142pub unsafe extern "C" fn initialize_ram_jump_to_main() {
143    use core::arch::naked_asm;
144    naked_asm!(
145        "
146    // Start by initializing .bss memory. The Tock linker script defines
147    // `_szero` and `_ezero` to mark the .bss segment.
148    ldr r0, ={sbss}     // r0 = first address of .bss
149    ldr r1, ={ebss}     // r1 = first address after .bss
150
151    movs r2, #0         // r2 = 0
152
153100: // bss_init_loop
154    cmp r1, r0          // We increment r0. Check if we have reached r1
155                        // (end of .bss), and stop if so.
156    beq 101f            // If r0 == r1, we are done.
157    stm r0!, {{r2}}     // Write a word to the address in r0, and increment r0.
158                        // Since r2 contains zero, this will clear the memory
159                        // pointed to by r0. Using `stm` (store multiple) with the
160                        // bang allows us to also increment r0 automatically.
161    b 100b              // Continue the loop.
162
163101: // bss_init_done
164
165    // Now initialize .data memory. This involves coping the values right at the
166    // end of the .text section (in flash) into the .data section (in RAM).
167    ldr r0, ={sdata}    // r0 = first address of data section in RAM
168    ldr r1, ={edata}    // r1 = first address after data section in RAM
169    ldr r2, ={etext}    // r2 = address of stored data initial values
170
171200: // data_init_loop
172    cmp r1, r0          // We increment r0. Check if we have reached the end
173                        // of the data section, and if so we are done.
174    beq 201f            // r0 == r1, and we have iterated through the .data section
175    ldm r2!, {{r3}}     // r3 = *(r2), r2 += 1. Load the initial value into r3,
176                        // and use the bang to increment r2.
177    stm r0!, {{r3}}     // *(r0) = r3, r0 += 1. Store the value to memory, and
178                        // increment r0.
179    b 200b              // Continue the loop.
180
181201: // data_init_done
182
183    // Now that memory has been initialized, we can jump to main() where the
184    // board initialization takes place and Rust code starts.
185    bl main
186        ",
187        sbss = sym _szero,
188        ebss = sym _ezero,
189        sdata = sym _srelocate,
190        edata = sym _erelocate,
191        etext = sym _etext,
192    );
193}
194
195pub unsafe fn print_cortexm_state(writer: &mut dyn Write) {
196    let _ccr = syscall::SCB_REGISTERS[0];
197    let cfsr = syscall::SCB_REGISTERS[1];
198    let hfsr = syscall::SCB_REGISTERS[2];
199    let mmfar = syscall::SCB_REGISTERS[3];
200    let bfar = syscall::SCB_REGISTERS[4];
201
202    let iaccviol = (cfsr & 0x01) == 0x01;
203    let daccviol = (cfsr & 0x02) == 0x02;
204    let munstkerr = (cfsr & 0x08) == 0x08;
205    let mstkerr = (cfsr & 0x10) == 0x10;
206    let mlsperr = (cfsr & 0x20) == 0x20;
207    let mmfarvalid = (cfsr & 0x80) == 0x80;
208
209    let ibuserr = ((cfsr >> 8) & 0x01) == 0x01;
210    let preciserr = ((cfsr >> 8) & 0x02) == 0x02;
211    let impreciserr = ((cfsr >> 8) & 0x04) == 0x04;
212    let unstkerr = ((cfsr >> 8) & 0x08) == 0x08;
213    let stkerr = ((cfsr >> 8) & 0x10) == 0x10;
214    let lsperr = ((cfsr >> 8) & 0x20) == 0x20;
215    let bfarvalid = ((cfsr >> 8) & 0x80) == 0x80;
216
217    let undefinstr = ((cfsr >> 16) & 0x01) == 0x01;
218    let invstate = ((cfsr >> 16) & 0x02) == 0x02;
219    let invpc = ((cfsr >> 16) & 0x04) == 0x04;
220    let nocp = ((cfsr >> 16) & 0x08) == 0x08;
221    let unaligned = ((cfsr >> 16) & 0x100) == 0x100;
222    let divbyzero = ((cfsr >> 16) & 0x200) == 0x200;
223
224    let vecttbl = (hfsr & 0x02) == 0x02;
225    let forced = (hfsr & 0x40000000) == 0x40000000;
226
227    let _ = writer.write_fmt(format_args!("\r\n---| Cortex-M Fault Status |---\r\n"));
228
229    if iaccviol {
230        let _ = writer.write_fmt(format_args!(
231            "Instruction Access Violation:       {}\r\n",
232            iaccviol
233        ));
234    }
235    if daccviol {
236        let _ = writer.write_fmt(format_args!(
237            "Data Access Violation:              {}\r\n",
238            daccviol
239        ));
240    }
241    if munstkerr {
242        let _ = writer.write_fmt(format_args!(
243            "Memory Management Unstacking Fault: {}\r\n",
244            munstkerr
245        ));
246    }
247    if mstkerr {
248        let _ = writer.write_fmt(format_args!(
249            "Memory Management Stacking Fault:   {}\r\n",
250            mstkerr
251        ));
252    }
253    if mlsperr {
254        let _ = writer.write_fmt(format_args!(
255            "Memory Management Lazy FP Fault:    {}\r\n",
256            mlsperr
257        ));
258    }
259
260    if ibuserr {
261        let _ = writer.write_fmt(format_args!(
262            "Instruction Bus Error:              {}\r\n",
263            ibuserr
264        ));
265    }
266    if preciserr {
267        let _ = writer.write_fmt(format_args!(
268            "Precise Data Bus Error:             {}\r\n",
269            preciserr
270        ));
271    }
272    if impreciserr {
273        let _ = writer.write_fmt(format_args!(
274            "Imprecise Data Bus Error:           {}\r\n",
275            impreciserr
276        ));
277    }
278    if unstkerr {
279        let _ = writer.write_fmt(format_args!(
280            "Bus Unstacking Fault:               {}\r\n",
281            unstkerr
282        ));
283    }
284    if stkerr {
285        let _ = writer.write_fmt(format_args!(
286            "Bus Stacking Fault:                 {}\r\n",
287            stkerr
288        ));
289    }
290    if lsperr {
291        let _ = writer.write_fmt(format_args!(
292            "Bus Lazy FP Fault:                  {}\r\n",
293            lsperr
294        ));
295    }
296    if undefinstr {
297        let _ = writer.write_fmt(format_args!(
298            "Undefined Instruction Usage Fault:  {}\r\n",
299            undefinstr
300        ));
301    }
302    if invstate {
303        let _ = writer.write_fmt(format_args!(
304            "Invalid State Usage Fault:          {}\r\n",
305            invstate
306        ));
307    }
308    if invpc {
309        let _ = writer.write_fmt(format_args!(
310            "Invalid PC Load Usage Fault:        {}\r\n",
311            invpc
312        ));
313    }
314    if nocp {
315        let _ = writer.write_fmt(format_args!(
316            "No Coprocessor Usage Fault:         {}\r\n",
317            nocp
318        ));
319    }
320    if unaligned {
321        let _ = writer.write_fmt(format_args!(
322            "Unaligned Access Usage Fault:       {}\r\n",
323            unaligned
324        ));
325    }
326    if divbyzero {
327        let _ = writer.write_fmt(format_args!(
328            "Divide By Zero:                     {}\r\n",
329            divbyzero
330        ));
331    }
332
333    if vecttbl {
334        let _ = writer.write_fmt(format_args!(
335            "Bus Fault on Vector Table Read:     {}\r\n",
336            vecttbl
337        ));
338    }
339    if forced {
340        let _ = writer.write_fmt(format_args!(
341            "Forced Hard Fault:                  {}\r\n",
342            forced
343        ));
344    }
345
346    if mmfarvalid {
347        let _ = writer.write_fmt(format_args!(
348            "Faulting Memory Address:            {:#010X}\r\n",
349            mmfar
350        ));
351    }
352    if bfarvalid {
353        let _ = writer.write_fmt(format_args!(
354            "Bus Fault Address:                  {:#010X}\r\n",
355            bfar
356        ));
357    }
358
359    if cfsr == 0 && hfsr == 0 {
360        let _ = writer.write_fmt(format_args!("No Cortex-M faults detected.\r\n"));
361    } else {
362        let _ = writer.write_fmt(format_args!(
363            "Fault Status Register (CFSR):       {:#010X}\r\n",
364            cfsr
365        ));
366        let _ = writer.write_fmt(format_args!(
367            "Hard Fault Status Register (HFSR):  {:#010X}\r\n",
368            hfsr
369        ));
370    }
371}
372
373///////////////////////////////////////////////////////////////////
374// Mock implementations for running tests on CI.
375//
376// Since tests run on the local architecture, we have to remove any
377// ARM assembly since it will not compile.
378///////////////////////////////////////////////////////////////////
379
380#[cfg(not(any(doc, all(target_arch = "arm", target_os = "none"))))]
381pub unsafe extern "C" fn unhandled_interrupt() {
382    unimplemented!()
383}
384
385#[cfg(not(any(doc, all(target_arch = "arm", target_os = "none"))))]
386pub unsafe extern "C" fn initialize_ram_jump_to_main() {
387    unimplemented!()
388}