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