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];