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