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