mimxrt500_bootstub/
lib.rs

1//! Some helper macros to generates a NOR flash header ("flash control block"),
2//! initial vector table, and small shim code that can be written to the start
3//! of the NOR flash connected to FlexSPI0 on an i.MX RT500-series chip to deal
4//! with its unusual requirements before jumping into a more normal-looking
5//! embedded Rust application linked to somewhere else in memory -- typically
6//! a later page or block of the same Flash memory, but that's not required.
7//!
8//! This series of chips has no built-in flash memory and so instead has a
9//! boot ROM that probes various peripherals to try to find some external
10//! memory containing a boot image. One option is a NOR flash connected to
11//! FlexSPI0, but that requires some chip-specific header information in
12//! the first page of flash that is intermingled with the initial vector
13//! table, and is thus incompatible with the image layout typically generated
14//! by the `cortex-m-rt` crate.
15//!
16//! To allow using `cortex-m-rt` in the normal way, this crate can help
17//! generate a small stub image to write into the first page of memory,
18//! separately from the main application, which then expects to find a
19//! normal-looking `cortex-m-rt`-based application at some other memory
20//! address and begins executing it.
21//!
22//! This does mean "wasting" at least a page of flash memory and having
23//! a redundant extra vector table used only by the boot stub, but that's
24//! typically a small price to pay for the convenience of meeting the
25//! expectations of the embedded Rust ecosystem for Cortex-M-based platforms.
26//!
27//! # Bootstub Styles
28//!
29//! For flexibility for different application needs, this library offers
30//! two different variations of the boot stub generator macro. Each
31//! application must include exactly one call to exactly one of these.
32//!
33//! * [`bootstub_builtin`] is the most straightforward option, which embeds
34//!   a bootstub directly inside the application and transfers control to
35//!   the application code using the vector table generated by the
36//!   `cortex-m-rt` linker script.
37//!
38//!     This approach provides the most "normal" development experience,
39//!     but at the expense of slightly bloating your application image
40//!     by embedding the boot stub directly into it.
41//! * [`bootstub_standalone`] is a more specialized option which allows
42//!   building an executable that is _only_ a boot stub, expecting to find
43//!   the real application vector table at a fixed memory location.
44//!
45//!     This approach in theory allows flashing the bootstub image only
46//!     once and then flashing normal application code with its vectors at
47//!     the designated address as a separate operation. The bootstub and
48//!     the application are independent from one another and can be updated
49//!     separately.
50//!
51//! # Flash Control Block
52//!
53//! The RT500 boot ROM also requires the NOR flash to contain a
54//! _Flash Control Block_, which is a data structure that both declares that
55//! the flash memory is intended as boot media and helps the boot ROM to
56//! configure the FlexSPI0 peripheral to read from it efficiently.
57//!
58//! If your flash does not include a flash control block then the boot ROM
59//! will not consider the NOR flash as a candidate boot medium.
60//!
61//! Use [`fcb`] to declare a Flash Control Block, which should then be linked
62//! at the appropriate location using your linker script.
63#![no_std]
64
65use core::arch::asm;
66
67pub mod bootrom;
68
69extern "C" {
70    #[doc(hidden)]
71    pub fn __mimxrt500_bootstub_main(app_vectors: *const u32);
72    #[doc(hidden)]
73    pub fn __mimxrt500_bootstub();
74    #[doc(hidden)]
75    fn __mimxrt500_bootstub_image_start();
76    #[doc(hidden)]
77    pub fn __vector_table();
78}
79
80/// Generates a bootstub intended for flashing separately from any real
81/// application, which expects to find a vector table at a fixed location.
82///
83/// This allows a more advanced usage pattern where you can write a standalone
84/// bootstub program and write it just once to the beginning of the flash
85/// memory, and then use it for arbitrary applications written with their
86/// vector tables at the designated location without them needing to include
87/// any special boot stub code themselves.
88///
89/// ```
90/// bootstub_standalone!(0x10000);
91/// ```
92///
93/// A boot stub created in this way expects to find a suitable vector table
94/// at the given address but does not include one itself. If you choose your
95/// vector table address so that it's in a separate flash memory page than
96/// the boot stub then you can re-flash the application without also
97/// re-flashing the boot stub.
98#[macro_export]
99macro_rules! bootstub_standalone {
100    ($app_vectors:literal) => {
101        ::core::arch::global_asm!(
102            ".cfi_sections .debug_frame",
103            ".section .mimxrt500_bootstub.text, \"ax\"",
104            ".global {entry}",
105            ".type {entry},%function",
106            ".thumb_func",
107            ".cfi_startproc",
108            "{entry}:",
109            concat!("ldr r0,=", $app_vectors),
110            "b {bootstub}",
111            ".cfi_endproc",
112            ".size {entry}, . - {entry}",
113            entry = sym $crate::__mimxrt500_bootstub,
114            bootstub = sym $crate::__mimxrt500_bootstub_main,
115        );
116    }
117}
118
119/// Generates a bootstub for inclusion as part of the flash image for an
120/// application.
121///
122/// With no arguments the boot stub will try to automatically locate the
123/// vector table generated by the `cortex-m-rt` crate's linker script. This
124/// is a good option if you are intending to follow embedded Rust idiom for
125/// your application's build process.
126///
127/// You can optionally provide an argument which refers to a static variable
128/// that must be a valid ARMv8-M vector table. This macro cannot verify that
129/// the given symbol _does_ match the processor's requirements for a vector
130/// table, and so if you specify an unreasonable symbol the behavior will
131/// be unsound.
132#[macro_export]
133macro_rules! bootstub_builtin {
134    () => {
135        ::core::arch::global_asm!(
136            ".cfi_sections .debug_frame",
137            ".section .mimxrt500_bootstub.text, \"ax\"",
138            ".global {entry}",
139            ".type {entry},%function",
140            ".thumb_func",
141            ".cfi_startproc",
142            "{entry}:",
143            "ldr r0,={vectors}",
144            "b {bootstub}",
145            ".cfi_endproc",
146            ".size {entry}, . - {entry}",
147            entry = sym $crate::__mimxrt500_bootstub,
148            bootstub = sym $crate::__mimxrt500_bootstub_main,
149            vectors = sym $crate::__vector_table,
150        );
151    };
152    ($app_vectors:path) => {
153        ::core::arch::global_asm!(
154            ".cfi_sections .debug_frame",
155            ".section .mimxrt500_bootstub.text, \"ax\"",
156            ".global {entry}",
157            ".type {entry},%function",
158            ".thumb_func",
159            ".cfi_startproc",
160            "{entry}:",
161            "ldr r0,={vectors}",
162            "b {bootstub}",
163            ".cfi_endproc",
164            ".size {entry}, . - {entry}",
165            entry = sym $crate::__mimxrt500_bootstub,
166            bootstub = sym $crate::__mimxrt500_bootstub_main,
167            vectors = sym $app_vectors,
168        );
169    };
170}
171
172/// Generates a Flash Control Block as a global static symbol.
173///
174/// The argument must be a value of type [`bootrom::FlexSpiNorFlashConfig`].
175/// This macro will arrange for that value to be placed into the appropriate
176/// section so that a correctly-written linker script will place it at the
177/// location where the on-chip boot ROM expects to find it.
178#[macro_export]
179macro_rules! fcb {
180    ($data:expr) => {
181        #[doc(hidden)]
182        #[link_section = ".mimxrt500_bootstub.fcb"]
183        #[no_mangle]
184        static __MIMXRT500_FCB: $crate::bootrom::FlexSpiNorFlashConfig = $data;
185    };
186}
187
188::core::arch::global_asm!(
189    ".cfi_sections .debug_frame",
190    ".section .mimxrt500_bootstub.text, \"ax\"",
191    ".global {bootstub}",
192    ".type {bootstub},%function",
193    ".thumb_func",
194    ".cfi_startproc",
195    "{bootstub}:",
196
197    // Use the application's vector table
198    "ldr r1, =0xe000ed08", // r1 points at VTOR
199    "str r0, [r1]", // store app_vectors argument (r0) to VTOR (*r1)
200
201    // Load application's initial stack pointer
202    "ldr sp, [r0, #0]",
203
204    // Jump to application's reset vector
205    "ldr pc, [r0, #4]",
206
207    "1:",
208    "b 1b",
209    ".cfi_endproc",
210    ".size {bootstub}, . - {bootstub}",
211    bootstub = sym __mimxrt500_bootstub_main,
212);
213
214#[link_section = ".mimxrt500_bootstub.text"]
215extern "C" fn default_exception_handler() {
216    loop {
217        unsafe { asm!("wfi") };
218    }
219}
220
221#[doc(hidden)]
222pub union Vector {
223    handler: unsafe extern "C" fn(),
224    reserved: u32,
225}
226
227const DEFAULT_VECTOR: Vector = Vector {
228    handler: default_exception_handler,
229};
230const RESERVED_VECTOR: Vector = Vector { reserved: 0 };
231
232const IMGTYPE_PLAIN_NO_SECURE: u32 = 0x00004000;
233
234#[doc(hidden)]
235#[link_section = ".mimxrt500_bootstub.exceptions"]
236#[no_mangle]
237pub static __mimxrt500_bootstub_exceptions: [Vector; 16] = [
238    // Initial stack pointer is irrelevant because we don't use the stack,
239    // but the boot ROM seems to verify that this points to a reasonable
240    // address in RAM, so we'll just arbitrarily choose one.
241    Vector { reserved: 0x5000 },
242    // Reset vector is the generated boot stub
243    Vector {
244        handler: __mimxrt500_bootstub,
245    },
246    // NMI Exception
247    DEFAULT_VECTOR,
248    // HardFault Exception
249    DEFAULT_VECTOR,
250    // MemManage Exception
251    DEFAULT_VECTOR,
252    // BusFault Exception
253    DEFAULT_VECTOR,
254    // UsageFault Exception
255    DEFAULT_VECTOR,
256    // SecureFault Exception
257    DEFAULT_VECTOR,
258    // Entry 8 is used by the RT500 boot ROM as the image size, which
259    // isn't really relevant here because we're not using the boot ROM's
260    // checksum and copy-to-RAM features. We'll just claim that the
261    // vector table is the entirety of the image.
262    Vector { reserved: 256 * 4 },
263    // Entry 9 is used by the RT500 boot ROM as the image type.
264    Vector {
265        reserved: IMGTYPE_PLAIN_NO_SECURE,
266    },
267    // Reserved entry
268    RESERVED_VECTOR,
269    // SVCall Exception
270    DEFAULT_VECTOR,
271    // Debug Monitor Exception
272    DEFAULT_VECTOR,
273    // Entry 13 is used by the RT500 boot ROM as the image load address,
274    // which must point to the start of this vector table for a flash XIP
275    // image.
276    Vector {
277        handler: __mimxrt500_bootstub_image_start,
278    },
279    // PendSV Exception
280    DEFAULT_VECTOR,
281    // SysTick Exception.
282    DEFAULT_VECTOR,
283];