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
//! Some helper macros to generates a NOR flash header ("flash control block"),
//! initial vector table, and small shim code that can be written to the start
//! of the NOR flash connected to FlexSPI0 on an i.MX RT500-series chip to deal
//! with its unusual requirements before jumping into a more normal-looking
//! embedded Rust application linked to somewhere else in memory -- typically
//! a later page or block of the same Flash memory, but that's not required.
//!
//! This series of chips has no built-in flash memory and so instead has a
//! boot ROM that probes various peripherals to try to find some external
//! memory containing a boot image. One option is a NOR flash connected to
//! FlexSPI0, but that requires some chip-specific header information in
//! the first page of flash that is intermingled with the initial vector
//! table, and is thus incompatible with the image layout typically generated
//! by the `cortex-m-rt` crate.
//!
//! To allow using `cortex-m-rt` in the normal way, this crate can help
//! generate a small stub image to write into the first page of memory,
//! separately from the main application, which then expects to find a
//! normal-looking `cortex-m-rt`-based application at some other memory
//! address and begins executing it.
//!
//! This does mean "wasting" at least a page of flash memory and having
//! a redundant extra vector table used only by the boot stub, but that's
//! typically a small price to pay for the convenience of meeting the
//! expectations of the embedded Rust ecosystem for Cortex-M-based platforms.
//!
//! # Bootstub Styles
//!
//! For flexibility for different application needs, this library offers
//! two different variations of the boot stub generator macro. Each
//! application must include exactly one call to exactly one of these.
//!
//! * [`bootstub_builtin`] is the most straightforward option, which embeds
//! a bootstub directly inside the application and transfers control to
//! the application code using the vector table generated by the
//! `cortex-m-rt` linker script.
//!
//! This approach provides the most "normal" development experience,
//! but at the expense of slightly bloating your application image
//! by embedding the boot stub directly into it.
//! * [`bootstub_standalone`] is a more specialized option which allows
//! building an executable that is _only_ a boot stub, expecting to find
//! the real application vector table at a fixed memory location.
//!
//! This approach in theory allows flashing the bootstub image only
//! once and then flashing normal application code with its vectors at
//! the designated address as a separate operation. The bootstub and
//! the application are independent from one another and can be updated
//! separately.
//!
//! # Flash Control Block
//!
//! The RT500 boot ROM also requires the NOR flash to contain a
//! _Flash Control Block_, which is a data structure that both declares that
//! the flash memory is intended as boot media and helps the boot ROM to
//! configure the FlexSPI0 peripheral to read from it efficiently.
//!
//! If your flash does not include a flash control block then the boot ROM
//! will not consider the NOR flash as a candidate boot medium.
//!
//! Use [`fcb`] to declare a Flash Control Block, which should then be linked
//! at the appropriate location using your linker script.
#![no_std]
use core::arch::asm;
pub mod bootrom;
extern "C" {
#[doc(hidden)]
pub fn __mimxrt500_bootstub_main(app_vectors: *const u32);
#[doc(hidden)]
pub fn __mimxrt500_bootstub();
#[doc(hidden)]
fn __mimxrt500_bootstub_image_start();
#[doc(hidden)]
pub fn __vector_table();
}
/// Generates a bootstub intended for flashing separately from any real
/// application, which expects to find a vector table at a fixed location.
///
/// This allows a more advanced usage pattern where you can write a standalone
/// bootstub program and write it just once to the beginning of the flash
/// memory, and then use it for arbitrary applications written with their
/// vector tables at the designated location without them needing to include
/// any special boot stub code themselves.
///
/// ```
/// bootstub_standalone!(0x10000);
/// ```
///
/// A boot stub created in this way expects to find a suitable vector table
/// at the given address but does not include one itself. If you choose your
/// vector table address so that it's in a separate flash memory page than
/// the boot stub then you can re-flash the application without also
/// re-flashing the boot stub.
#[macro_export]
macro_rules! bootstub_standalone {
($app_vectors:literal) => {
::core::arch::global_asm!(
".cfi_sections .debug_frame",
".section .mimxrt500_bootstub.text, \"ax\"",
".global {entry}",
".type {entry},%function",
".thumb_func",
".cfi_startproc",
"{entry}:",
concat!("ldr r0,=", $app_vectors),
"b {bootstub}",
".cfi_endproc",
".size {entry}, . - {entry}",
entry = sym $crate::__mimxrt500_bootstub,
bootstub = sym $crate::__mimxrt500_bootstub_main,
);
}
}
/// Generates a bootstub for inclusion as part of the flash image for an
/// application.
///
/// With no arguments the boot stub will try to automatically locate the
/// vector table generated by the `cortex-m-rt` crate's linker script. This
/// is a good option if you are intending to follow embedded Rust idiom for
/// your application's build process.
///
/// You can optionally provide an argument which refers to a static variable
/// that must be a valid ARMv8-M vector table. This macro cannot verify that
/// the given symbol _does_ match the processor's requirements for a vector
/// table, and so if you specify an unreasonable symbol the behavior will
/// be unsound.
#[macro_export]
macro_rules! bootstub_builtin {
() => {
::core::arch::global_asm!(
".cfi_sections .debug_frame",
".section .mimxrt500_bootstub.text, \"ax\"",
".global {entry}",
".type {entry},%function",
".thumb_func",
".cfi_startproc",
"{entry}:",
"ldr r0,={vectors}",
"b {bootstub}",
".cfi_endproc",
".size {entry}, . - {entry}",
entry = sym $crate::__mimxrt500_bootstub,
bootstub = sym $crate::__mimxrt500_bootstub_main,
vectors = sym $crate::__vector_table,
);
};
($app_vectors:path) => {
::core::arch::global_asm!(
".cfi_sections .debug_frame",
".section .mimxrt500_bootstub.text, \"ax\"",
".global {entry}",
".type {entry},%function",
".thumb_func",
".cfi_startproc",
"{entry}:",
"ldr r0,={vectors}",
"b {bootstub}",
".cfi_endproc",
".size {entry}, . - {entry}",
entry = sym $crate::__mimxrt500_bootstub,
bootstub = sym $crate::__mimxrt500_bootstub_main,
vectors = sym $app_vectors,
);
};
}
/// Generates a Flash Control Block as a global static symbol.
///
/// The argument must be a value of type [`bootrom::FlexSpiNorFlashConfig`].
/// This macro will arrange for that value to be placed into the appropriate
/// section so that a correctly-written linker script will place it at the
/// location where the on-chip boot ROM expects to find it.
#[macro_export]
macro_rules! fcb {
($data:expr) => {
#[doc(hidden)]
#[link_section = ".mimxrt500_bootstub.fcb"]
#[no_mangle]
static __MIMXRT500_FCB: $crate::bootrom::FlexSpiNorFlashConfig = $data;
};
}
::core::arch::global_asm!(
".cfi_sections .debug_frame",
".section .mimxrt500_bootstub.text, \"ax\"",
".global {bootstub}",
".type {bootstub},%function",
".thumb_func",
".cfi_startproc",
"{bootstub}:",
// Use the application's vector table
"ldr r1, =0xe000ed08", // r1 points at VTOR
"str r0, [r1]", // store app_vectors argument (r0) to VTOR (*r1)
// Load application's initial stack pointer
"ldr sp, [r0, #0]",
// Jump to application's reset vector
"ldr pc, [r0, #4]",
"1:",
"b 1b",
".cfi_endproc",
".size {bootstub}, . - {bootstub}",
bootstub = sym __mimxrt500_bootstub_main,
);
#[link_section = ".mimxrt500_bootstub.text"]
extern "C" fn default_exception_handler() {
loop {
unsafe { asm!("wfi") };
}
}
#[doc(hidden)]
pub union Vector {
handler: unsafe extern "C" fn(),
reserved: u32,
}
const DEFAULT_VECTOR: Vector = Vector {
handler: default_exception_handler,
};
const RESERVED_VECTOR: Vector = Vector { reserved: 0 };
const IMGTYPE_PLAIN_NO_SECURE: u32 = 0x00004000;
#[doc(hidden)]
#[link_section = ".mimxrt500_bootstub.exceptions"]
#[no_mangle]
pub static __mimxrt500_bootstub_exceptions: [Vector; 16] = [
// Initial stack pointer is irrelevant because we don't use the stack,
// but the boot ROM seems to verify that this points to a reasonable
// address in RAM, so we'll just arbitrarily choose one.
Vector { reserved: 0x5000 },
// Reset vector is the generated boot stub
Vector {
handler: __mimxrt500_bootstub,
},
// NMI Exception
DEFAULT_VECTOR,
// HardFault Exception
DEFAULT_VECTOR,
// MemManage Exception
DEFAULT_VECTOR,
// BusFault Exception
DEFAULT_VECTOR,
// UsageFault Exception
DEFAULT_VECTOR,
// SecureFault Exception
DEFAULT_VECTOR,
// Entry 8 is used by the RT500 boot ROM as the image size, which
// isn't really relevant here because we're not using the boot ROM's
// checksum and copy-to-RAM features. We'll just claim that the
// vector table is the entirety of the image.
Vector { reserved: 256 * 4 },
// Entry 9 is used by the RT500 boot ROM as the image type.
Vector {
reserved: IMGTYPE_PLAIN_NO_SECURE,
},
// Reserved entry
RESERVED_VECTOR,
// SVCall Exception
DEFAULT_VECTOR,
// Debug Monitor Exception
DEFAULT_VECTOR,
// Entry 13 is used by the RT500 boot ROM as the image load address,
// which must point to the start of this vector table for a flash XIP
// image.
Vector {
handler: __mimxrt500_bootstub_image_start,
},
// PendSV Exception
DEFAULT_VECTOR,
// SysTick Exception.
DEFAULT_VECTOR,
];