Skip to main content

aarch32_rt/
lib.rs

1//! # Run-time support for AArch32 Processors
2//!
3//! This library implements a simple Arm vector table, suitable for getting into
4//! a Rust application running in System Mode. It also provides a reference
5//! start up method. Most AArch32 based systems will require chip specific
6//! start-up code, so the start-up method can be overridden.
7//!
8//! The default startup routine provided by this crate does not include any
9//! special handling for multi-core support because this is oftentimes
10//! implementation defined and the exact handling depends on the specific chip
11//! in use. Many implementations only run the startup routine with one core and
12//! will keep other cores in reset until they are woken up by an implementation
13//! specific mechanism. For other implementations where multi-core specific
14//! startup adaptions are necessary, the startup routine can be overwritten by
15//! the user.
16//!
17//! ## Features
18//!
19//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target.
20//!
21//! - `fpu-d32`: Make the interrupt context store routines save the upper
22//!   double-precision registers.
23//!
24//!   If your program is using all 32 double-precision registers (e.g. if you
25//!   have set the `+d32` target feature) then you need to enable this option
26//!   otherwise important FPU state may be lost when an exception occurs.
27//
28//! - `el2-mode`: Leave the processor in EL2/PL2 mode on boot-up, and expect to
29//!   handle interrupts in HYP mode using ELR_hyp. Useful if you want to write a
30//!   hypervisor or other low-level firmware.
31//!
32//! - `svc-stack-interrupt`: Use the SVC stack when an interrupt occurs, instead
33//!   of using the SYS stack. Useful if you are writing an RTOS and your SYS
34//!   stack is actually the USR stack for the running task.
35//!
36//! ## Information about the Run-Time
37//!
38//! Transferring from System Mode to User Mode (i.e. implementing an RTOS) is
39//! not handled here.
40//!
41//! If your processor starts in Hyp mode, this runtime will be transfer it to
42//! System mode. If you wish to write a hypervisor, you will need to replace
43//! this library with something more advanced.
44//!
45//! We assume that a set of symbols exist, either for constants or for C
46//! compatible functions or for naked raw-assembly functions. They are described
47//! in the next three sections.
48//!
49//! ## Constants
50//!
51//! * `_num_cores` - the number of CPU core (and hence the number of copies of
52//!   each stack). Must be > 0. Defaults to 1.
53//! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
54//!   aligned.
55//! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
56//!   aligned.
57//! * `_fiq_stack_size` - the number of bytes to be reserved for stack space
58//!   when in FIQ mode; will be padded to a multiple of 8.
59//! * `_irq_stack_size` - the number of bytes to be reserved for stack space
60//!   when in FIQ mode; will be padded to a multiple of 8.
61//! * `_svc_stack_size` - the number of bytes to be reserved for stack space
62//!   when in SVC mode; will be padded to a multiple of 8.
63//! * `_und_stack_size` - the number of bytes to be reserved for stack space
64//!   when in Undefined mode; will be padded to a multiple of 8.
65//! * `_abt_stack_size` - the number of bytes to be reserved for stack space
66//!   when in Abort mode; will be padded to a multiple of 8.
67//! * `_hyp_stack_size` - the number of bytes to be reserved for stack space
68//!   when in Hyp mode; will be padded to a multiple of 8.
69//! * `_sys_stack_size` - the number of bytes to be reserved for stack space
70//!   when in System mode; will be padded to a multiple of 8.
71//! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
72//! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
73//! * `__sidata` - the start of the initialisation values for data, in read-only
74//!   memory. Must be 4-byte aligned.
75//!
76//! Using our default start-up function `_default_start`, the memory between
77//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and
78//! `__edata` is initialised with the data found at `__sidata`.
79//!
80//! ## Stacks
81//!
82//! Stacks are located in `.stacks` section which is mapped to the `STACKS`
83//! memory region. Per default, the stacks are pushed to the end of the `STACKS`
84//! by a filler section. We allocate stacks for each core, based on the
85//! `_num_cores` linker symbol.
86//!
87//! The stacks look like:
88//!
89//! ```text
90//! +------------------+ <---- ORIGIN(STACKS) + LENGTH(STACKS)
91//! |     SYS Stack    | } _sys_stack_size * _num_cores bytes
92//! +------------------+
93//! |     FIQ Stack    | } _fiq_stack_size * _num_cores bytes
94//! +------------------+
95//! |     IRQ Stack    | } _irq_stack_size * _num_cores bytes
96//! +------------------+
97//! |     HYP Stack    | } _hyp_stack_size * _num_cores bytes (only used on Armv8-R)
98//! +------------------+
99//! |     ABT Stack    | } _abt_stack_size * _num_cores bytes
100//! +------------------+
101//! |     SVC Stack    | } _svc_stack_size * _num_cores bytes
102//! +------------------+
103//! |     UND Stack    | } _und_stack_size * _num_cores bytes
104//! +------------------+
105//! |  filler section  |
106//! +------------------+ <---- ORIGIN(STACKS)
107//! ```
108//!
109//! Our linker script PROVIDEs a symbol `_pack_stacks`. By setting this symbol
110//! to 0 in memory.x, the stacks can be moved to the beginning of the `STACKS`
111//! region or the end of the previous section located in STACKS or its alias.
112//!
113//! ## C-Compatible Functions
114//!
115//! ### Main Function
116//!
117//! The symbol `kmain` should be an `extern "C"` function. It is called in SYS
118//! mode after all the global variables have been initialised. There is no
119//! default - this function is mandatory.
120//!
121//! ```rust
122//! #[unsafe(no_mangle)]
123//! extern "C" fn kmain() -> ! {
124//!     loop { }
125//! }
126//! ```
127//!
128//! You can also create a 'kmain' function by using the `#[entry]` attribute on
129//! a normal Rust function. The function will be renamed in such a way that the
130//! start-up assembly code can find it, but normal Rust code cannot. Therefore
131//! you can be assured that the function will only be called once (unless
132//! someone resorts to `unsafe` Rust to import the `kmain` symbol as an `extern
133//! "C" fn`).
134//!
135//! ```rust
136//! use aarch32_rt::entry;
137//!
138//! #[entry]
139//! fn my_main() -> ! {
140//!     loop { }
141//! }
142//! ```
143//!
144//! ### Undefined Handler
145//!
146//! The symbol `_undefined_handler` should be an `extern "C"` function. It is
147//! called in UND mode when an [Undefined Instruction Exception] occurs.
148//!
149//! [Undefined Instruction Exception]:
150//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Undefined-Instruction-exception?lang=en
151//!
152//! Our linker script PROVIDEs a default `_undefined_handler` symbol which is an
153//! alias for the `_default_handler` function. You can override it by defining
154//! your own `_undefined_handler` function, like:
155//!
156//! ```rust
157//! /// Does not return
158//! #[unsafe(no_mangle)]
159//! extern "C" fn _undefined_handler(addr: usize) -> ! {
160//!     loop { }
161//! }
162//! ```
163//!
164//! or:
165//!
166//! ```rust
167//! /// Execution will continue from the returned address.
168//! ///
169//! /// Return `addr` to go back and execute the faulting instruction again.
170//! #[unsafe(no_mangle)]
171//! unsafe extern "C" fn _undefined_handler(addr: usize) -> usize {
172//!     // do stuff here, then return to the address *after* the one
173//!     // that failed
174//!     addr + 4
175//! }
176//! ```
177//!
178//! You can create a `_undefined_handler` function by using the
179//! `#[exception(Undefined)]` attribute on a Rust function with the appropriate
180//! arguments and return type.
181//!
182//! ```rust
183//! use aarch32_rt::exception;
184//!
185//! #[exception(Undefined)]
186//! fn my_handler(addr: usize) -> ! {
187//!     loop { }
188//! }
189//! ```
190//!
191//! or:
192//!
193//! ```rust
194//! use aarch32_rt::exception;
195//!
196//! #[exception(Undefined)]
197//! unsafe fn my_handler(addr: usize) -> usize {
198//!     // do stuff here, then return the address to return to
199//!     addr + 4
200//! }
201//! ```
202//!
203//! ### Supervisor Call Handler
204//!
205//! The symbol `_svc_handler` should be an `extern "C"` function. It is called
206//! in SVC mode when an [Supervisor Call Exception] occurs.
207//!
208//! [Supervisor Call Exception]:
209//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en
210//!
211//! Returning from this function will cause execution to resume at the function
212//! the triggered the exception, immediately after the SVC instruction. You
213//! cannot control where execution resumes. The function is passed the literal
214//! integer argument to the `svc` instruction, which is extracted from the
215//! machine code for you by the default assembly trampoline, along with
216//! registers r0 through r5, in the form of a reference to a `Frame` structure.
217//!
218//! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias
219//! for the `_default_handler` function. You can override it by defining your
220//! own `_svc_handler` function, like:
221//!
222//! ```rust
223//! #[unsafe(no_mangle)]
224//! extern "C" fn _svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 {
225//!     // do stuff here
226//!     todo!()
227//! }
228//! ```
229//!
230//! You can also create a `_svc_handler` function by using the
231//! `#[exception(SupervisorCall)]` attribute on a normal Rust function.
232//!
233//! ```rust
234//! use aarch32_rt::exception;
235//!
236//! #[exception(SupervisorCall)]
237//! fn svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 {
238//!     // do stuff here
239//!     todo!()
240//! }
241//! ```
242//!
243//! ### Hypervisor Call Handler
244//!
245//! The symbol `_hvc_handler` should be an `extern "C"` function. It is called
246//! in HYP mode when an [Hypervisor Call Exception] occurs.
247//!
248//! [Hypervisor Call Exception]:
249//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Hypervisor-Call--HVC--exception?lang=en
250//!
251//! Returning from this function will cause execution to resume at the function
252//! the triggered the exception, immediately after the HVC instruction. You
253//! cannot control where execution resumes. The function is passed contents of
254//! the Hypervisor Syndrome Register (HSR) register, which is fetched by the
255//! default assembly trampoline, along with registers r0 through r5, in the form
256//! of a reference to a `Frame` structure.
257//!
258//! Our linker script PROVIDEs a default `_hvc_handler` symbol which is an alias
259//! for the `_default_handler` function. You can override it by defining your
260//! own `_hvc_handler` function, like:
261//!
262//! ```rust
263//! #[unsafe(no_mangle)]
264//! extern "C" fn _hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 {
265//!     // do stuff here
266//!     todo!()
267//! }
268//! ```
269//!
270//! You can also create a `_hvc_handler` function by using the
271//! `#[exception(HypervisorCall)]` attribute on a normal Rust function.
272//!
273//! ```rust
274//! use aarch32_rt::exception;
275//!
276//! #[exception(HypervisorCall)]
277//! fn my_hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 {
278//!     // do stuff here
279//!     todo!()
280//! }
281//! ```
282//!
283//! If you wish to inspect the HSR value, you can use the `aarch32-cpu` crate:
284//!
285//! ```rust,ignore
286//! let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr);
287//! ```
288//!
289//! ### Prefetch Abort Handler
290//!
291//! The symbol `_prefetch_abort_handler` should be an `extern "C"` function. It
292//! is called in ABT mode when a [Prefetch Abort Exception] occurs.
293//!
294//! [Prefetch Abort Exception]:
295//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Prefetch-Abort-exception?lang=en
296//!
297//! Our linker script PROVIDEs a default `_prefetch_abort_handler` symbol which
298//! is an alias for the `_default_handler` function. You can override it by
299//! defining your own `_undefined_handler` function.
300//!
301//! This function takes the address of faulting instruction, and can either not
302//! return:
303//!
304//! ```rust
305//! #[unsafe(no_mangle)]
306//! extern "C" fn _prefetch_abort_handler(addr: usize) -> ! {
307//!     loop { }
308//! }
309//! ```
310//!
311//! Or it can return an address where execution should resume after the
312//! Exception handler is complete (which is unsafe):
313//!
314//! ```rust
315//! #[unsafe(no_mangle)]
316//! unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize {
317//!     // do stuff, then go back to the instruction after the one that failed
318//!     addr + 4
319//! }
320//! ```
321//!
322//! You can create a `_prefetch_abort_handler` function by using the
323//! `#[exception(PrefetchAbort)]` macro on a Rust function with the appropriate
324//! arguments and return type.
325//!
326//! ```rust
327//! use aarch32_rt::exception;
328//!
329//! #[exception(PrefetchAbort)]
330//! fn my_handler(addr: usize) -> ! {
331//!     loop { }
332//! }
333//! ```
334//!
335//! or:
336//!
337//! ```rust
338//! use aarch32_rt::exception;
339//!
340//! #[exception(PrefetchAbort)]
341//! unsafe fn my_handler(addr: usize) -> usize {
342//!     // do stuff, then go back to the instruction after the one that failed
343//!     addr + 4
344//! }
345//! ```
346//!
347//! ### Data Abort Handler
348//!
349//! The symbol `_data_abort_handler` should be an `extern "C"` function. It is
350//! called in ABT mode when a Data Abort Exception occurs.
351//!
352//! [Data Abort Exception]:
353//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Data-Abort-exception?lang=en
354//!
355//! Our linker script PROVIDEs a default `_data_abort_handler` symbol which is
356//! an alias for the `_default_handler` function. You can override it by
357//! defining your own `_undefined_handler` function.
358//!
359//! This function takes the address of faulting instruction, and can either not
360//! return:
361//!
362//! ```rust
363//! #[unsafe(no_mangle)]
364//! extern "C" fn _data_abort_handler(addr: usize) -> ! {
365//!     loop { }
366//! }
367//! ```
368//!
369//! Or it can return an address where execution should resume after the
370//! Exception handler is complete (which is unsafe):
371//!
372//! ```rust
373//! #[unsafe(no_mangle)]
374//! unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize {
375//!     // do stuff, then go back to the instruction after the one that failed
376//!     addr + 4
377//! }
378//! ```
379//!
380//! You can create a `_data_abort_handler` function by using the
381//! `#[exception(DataAbort)]` macro on a Rust function with the appropriate
382//! arguments and return type.
383//!
384//! ```rust
385//! use aarch32_rt::exception;
386//!
387//! #[exception(DataAbort)]
388//! fn my_handler(addr: usize) -> ! {
389//!     loop { }
390//! }
391//! ```
392//!
393//! or:
394//!
395//! ```rust
396//! use aarch32_rt::exception;
397//!
398//! #[exception(DataAbort)]
399//! unsafe fn my_handler(addr: usize) -> usize {
400//!     // do stuff, then go back to the instruction after the one that failed
401//!     addr + 4
402//! }
403//! ```
404//!
405//! ### IRQ Handler
406//!
407//! The symbol `_irq_handler` should be an `extern "C"` function. It is called
408//! in SYS mode or SVC mode (not IRQ mode!) when an [Interrupt] occurs. Use the
409//! `svc-stack-interrupt` feature to select SVC mode instead of the default SYS
410//! mode. You might want to use `svc-stack-interrupt` if you are running an RTOS
411//! and you don't want to push a bunch of state into the running thread's stack
412//! when an interrupt occurs.
413//!
414//! [Interrupt]:
415//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/IRQ-exception?lang=en
416//!
417//! Returning from this function will cause execution to resume at wherever it
418//! was interrupted. You cannot control where execution resumes.
419//!
420//! This function is entered with interrupts masked, but you may unmask (i.e.
421//! enable) interrupts inside this function if desired. You will probably want
422//! to talk to your interrupt controller first, otherwise you'll just keep
423//! re-entering this interrupt handler recursively until you stack overflow.
424//!
425//! Our linker script PROVIDEs a default `_irq_handler` symbol which is an alias
426//! for `_default_handler`. You can override it by defining your own
427//! `_irq_handler` function.
428//!
429//! Expected prototype:
430//!
431//! ```rust
432//! #[unsafe(no_mangle)]
433//! extern "C" fn _irq_handler() {
434//!     // 1. Talk to interrupt controller
435//!     // 2. Handle interrupt
436//!     // 3. Clear interrupt
437//! }
438//! ```
439//!
440//! You can also create a `_irq_handler` function by using the `#[irq]`
441//! attribute on a normal Rust function.
442//!
443//! ```rust
444//! use aarch32_rt::irq;
445//!
446//! #[irq]
447//! fn my_irq_handler() {
448//!     // 1. Talk to interrupt controller
449//!     // 2. Handle interrupt
450//!     // 3. Clear interrupt
451//! }
452//! ```
453//!
454//! ## ASM functions
455//!
456//! These are the naked 'raw' assembly functions the run-time requires:
457//!
458//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function
459//!   at `_default_start` but you can override it. The provided default start
460//!   function will initialise all global variables and then call `kmain` in SYS
461//!   mode. Some SoCs require a chip specific startup for tasks like MPU
462//!   initialization or chip specific initialization routines, so if our
463//!   start-up routine doesn't work for you, supply your own `_start` function
464//!   (but feel free to call our `_default_start` as part of it).
465//!
466//! * `_asm_undefined_handler` - a naked function to call when an Undefined
467//!   Exception occurs. Our linker script PROVIDEs a default function at
468//!   `_asm_default_undefined_handler` but you can override it. The provided
469//!   default handler will call `_undefined_handler` in UND mode, saving state
470//!   as required.
471//!
472//! * `_asm_svc_handler` - a naked function to call when an Supervisor Call
473//!   (SVC) Exception occurs. Our linker script PROVIDEs a default function at
474//!   `_asm_default_svc_handler` but you can override it. The provided default
475//!   handler will call `_svc_handler` in SVC mode, saving state as required.
476//!
477//! * `_asm_prefetch_abort_handler` - a naked function to call when a Prefetch
478//!   Abort Exception occurs. Our linker script PROVIDEs a default function at
479//!   `_asm_default_prefetch_abort_handler` but you can override it. The
480//!   provided default handler will call `_prefetch_abort_handler`, saving state
481//!   as required. Note that Prefetch Abort Exceptions are handled in Abort Mode
482//!   (ABT), Monitor Mode (MON) or Hyp Mode (HYP), depending on CPU
483//!   configuration.
484//!
485//! * `_asm_data_abort_handler` - a naked function to call when a Data Abort
486//!   Exception occurs. Our linker script PROVIDEs a default function at
487//!   `_asm_default_data_abort_handler` but you can override it. The provided
488//!   default handler will call `_data_abort_handler` in ABT mode, saving state
489//!   as required.
490//!
491//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception
492//!   occurs. Our linker script PROVIDEs a default function at
493//!   `_asm_default_irq_handler` but you can override it. The provided default
494//!   handler will call `_irq_handler` in SYS mode or SVC mode (but not IRQ
495//!   mode), saving state as required.
496//!
497//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt
498//!   Request (FIQ) occurs. Our linker script PROVIDEs a default function at
499//!   `_asm_default_fiq_handler` but you can override it. The provided default
500//!   just spins forever.
501//!
502//! ## SMP Support
503//!
504//! This library supports SMP operation on ARMv7-A, ARMv7-R and ARMv8-R.
505//!
506//! To enable SMP support, add `PROVIDE(_num_cores = N)` to your linker script
507//! (e.g. your `memory.x` file). This will cause space for 'N' copies of each
508//! stack to be reserved so that each core gets its own stack (see the section
509//! on 'Stacks', above).
510//!
511//! You must also write a function called `_asm_secondary_core_park` (which must
512//! be written in assembly, and not Rust, because it is executed before stacks
513//! and global memory are initialised). On start-up the bottom eight bits of
514//! MPIDR register are taken as the Core ID. On Core ID 0, normal start-up will
515//! occur. For non-zero Core IDs (so-called *secondary cores*), the cores call
516//! the `_asm_secondary_core_park` function, passing the core ID in `r0`. This
517//! function (which defaults to an infinite loop) should put the running core to
518//! sleep and cause it to wait for some sort of signal from Core 0. This allows
519//! Core 0 to complete the initialisation of global memory (`.data`, `.bss`,
520//! etc) before the secondary cores run (and those cores must not re-initialise
521//! global memory). After the cores have left the park routine and completed
522//! their local initialisation (i.e. set their stack pointers to their unique
523//! stacks), they execute the function `kmain_secondary` (recall that Core 0
524//! executes a function called `kmain`).
525//!
526//! In our example for the MPS3-AN536, we have the secondary core wait on a
527//! hardware register in one of the peripherals, because it has a known value at
528//! reset.
529//!
530//! ```rust,ignore
531//! #[unsafe(naked)]
532//! #[unsafe(no_mangle)]
533//! pub unsafe extern "C" fn _asm_secondary_core_park() {
534//!     core::arch::naked_asm!(
535//!         r#"
536//!         // Some hardware register
537//!         ldr     r0, =0xE020_2000
538//!     1:
539//!         // Wait until Core 0 does a 'sev'
540//!         wfe
541//!         // Spin until register is non-zero.
542//!         ldr     r1, [r0]
543//!         cmp     r1, 0
544//!         beq     1b
545//!         // return to start-up
546//!         bx      lr
547//!     "#,
548//!     );
549//! }
550//! ```
551//!
552//! ## Outputs
553//!
554//! This library produces global symbols called:
555//!
556//! * `_vector_table` - the start of the interrupt vector table
557//! * `_default_start` - the default Reset handler, that sets up some stacks and
558//!   calls an `extern "C"` function called `kmain`.
559//! * `_asm_default_undefined_handler` - assembly language trampoline that calls
560//!   `_undefined_handler`
561//! * `_asm_default_svc_handler` - assembly language trampoline that calls
562//!   `_svc_handler`
563//! * `_asm_default_prefetch_abort_handler` - assembly language trampoline that
564//!   calls `_prefetch_abort_handler`
565//! * `_asm_default_data_abort_handler` - assembly language trampoline that
566//!   calls `_data_abort_handler`
567//! * `_asm_default_irq_handler` - assembly language trampoline that calls
568//!   `_irq_handler`
569//! * `_asm_default_fiq_handler` - an FIQ handler that just spins
570//! * `_asm_default_secondary_core_park` - spins secondary cores forever
571//! * `_default_handler` - a C compatible function that spins forever.
572//! * `_asm_init_segments` - initialises `.bss` and `.data` and zeroes the
573//!   stacks
574//! * `_asm_core_start` - sets up stacks, enables FPU (if required), and jumps
575//!   to `kmain` or `kmain_secondary`. Takes the Core ID in `r0`.
576//! * `_asm_stack_setup_preallocated` - initialises UND, SVC, ABT, IRQ, FIQ and SYS
577//!   stacks from the `.stacks` section defined in link.x, based on
578//!   `_xxx_stack_size` values. Takes the Core ID in `r0`.
579//! * `_xxx_stack_high_end` and `_xxx_stack_low_end` where the former is the top
580//!   and the latter the bottom of the stack for each mode (`und`, `svc`, `abt`,
581//!   `irq`, `fiq`, `sys`)
582//!
583//! The assembly language trampolines are required because AArch32 processors do
584//! not save a great deal of state on entry to an exception handler, unlike
585//! Armv7-M (and other M-Profile) processors. We must therefore save this state
586//! to the stack using assembly language, before transferring to an `extern "C"`
587//! function. Because FIQ is often performance-sensitive, we don't supply an FIQ
588//! trampoline; if you want to use FIQ, you have to write your own assembly
589//! routine, allowing you to preserve only whatever state is important to you.
590//!
591//! ## Examples
592//!
593//! You can find example code using QEMU inside the [project
594//! repository](https://github.com/rust-embedded/aarch32/tree/main/examples)
595
596#![no_std]
597
598// *****************************************************************************
599// Public Modules
600// *****************************************************************************
601
602pub mod sections;
603pub mod stacks;
604
605// *****************************************************************************
606// Public Imports
607// *****************************************************************************
608
609pub use aarch32_rt_macros::{entry, exception, irq};
610
611// *****************************************************************************
612// Private Modules
613// *****************************************************************************
614
615#[cfg(all(arm_architecture = "v8-r", feature = "el2-mode"))]
616mod arch_v8_hyp;
617
618#[cfg(all(
619    armv7_or_higher,
620    not(all(arm_architecture = "v8-r", feature = "el2-mode"))
621))]
622mod arch_v7;
623
624#[cfg(armv6_or_lower)]
625mod arch_v4;
626
627// *****************************************************************************
628// Private Imports
629// *****************************************************************************
630
631#[cfg(target_arch = "arm")]
632use aarch32_cpu::register::{cpsr::ProcessorMode, Cpsr};
633
634// *****************************************************************************
635// Types
636// *****************************************************************************
637
638/// Arguments stacked on interrupt
639///
640/// This struct is very carefully designed to match the layout of the
641/// registers pushed to the stack in our SVC handler.
642#[derive(Debug, Clone, PartialEq, Eq)]
643#[repr(C)]
644pub struct Frame {
645    pub r0: u32,
646    pub r1: u32,
647    pub r2: u32,
648    pub r3: u32,
649    pub r4: u32,
650    pub r5: u32,
651}
652
653// *****************************************************************************
654// Macros
655// *****************************************************************************
656
657/// This macro expands to nothing.
658///
659/// It's a placeholder for the FPU saving/restoring routine, used on
660/// targets without FPU support.
661#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))]
662#[macro_export]
663macro_rules! fpu_context {
664    ("save") => {
665        ""
666    };
667    ("restore") => {
668        ""
669    };
670}
671
672/// This macro expands to code for saving/restoring FPU context in an exception
673/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment.
674/// It may damage R0-R3.
675///
676/// On entry to this block, we assume that we are in exception context.
677///
678/// This version saves FPU state, assuming 16 DP registers (a 'D16' or 'D16SP'
679/// FPU configuration). Note that SP-only FPUs still have DP registers
680/// - each DP register holds two SP values.
681///
682/// EABI specifies D8-D15 as callee-save, and so we don't
683/// preserve them because any C function we call to handle the exception will
684/// preserve/restore them itself as required.
685#[cfg(all(
686    any(target_abi = "eabihf", feature = "eabi-fpu"),
687    not(feature = "fpu-d32")
688))]
689#[macro_export]
690macro_rules! fpu_context {
691    // save all D16 FPU context, except D8-D15
692    ("save") => {
693        r#"
694        vpush   {{ d0-d7 }}
695        vmrs    r0, FPSCR
696        vmrs    r1, FPEXC
697        push    {{ r0-r1 }}
698        "#
699    };
700    // restore all D16 FPU context, except D8-D15
701    ("restore") => {
702        r#"
703        pop     {{ r0-r1 }}
704        vmsr    FPEXC, r1
705        vmsr    FPSCR, r0
706        vpop    {{ d0-d7 }}
707        "#
708    };
709}
710
711/// This macro expands to code for saving/restoring FPU context in an exception
712/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. It
713/// may damage R0-R3.
714///
715/// On entry to this block, we assume that we are in exception context.
716///
717/// This version saves FPU state assuming 32 DP registers (a 'D32' FPU
718/// configuration).
719///
720/// EABI specifies D8-D15 as callee-save, and so we don't preserve them because
721/// any C function we call to handle the exception will preserve/restore them
722/// itself as required.
723#[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "fpu-d32"))]
724#[macro_export]
725macro_rules! fpu_context {
726    // save all D32 FPU context, except D8-D15
727    ("save") => {
728        r#"
729        vpush   {{ d0-d7 }}
730        vpush   {{ d16-d31 }}
731        vmrs    r0, FPSCR
732        vmrs    r1, FPEXC
733        push    {{ r0-r1 }}
734        "#
735    };
736    // restore all D32 FPU context, except D8-D15
737    ("restore") => {
738        r#"
739        pop     {{ r0-r1 }}
740        vmsr    FPEXC, r1
741        vmsr    FPSCR, r0
742        vpop    {{ d16-d31 }}
743        vpop    {{ d0-d7 }}
744        "#
745    };
746}
747
748// *****************************************************************************
749// Functions
750// *****************************************************************************
751
752// The Interrupt Vector Table, and some default assembly-language handler.
753//
754// Needs to be aligned to 5bits/2^5 to be stored correctly in VBAR
755//
756// Need to be assembled as Arm-mode because the Thumb Exception bit is cleared
757#[cfg(target_arch = "arm")]
758core::arch::global_asm!(
759    r#"
760    .pushsection .vector_table,"ax",%progbits
761    .arm
762    .global _vector_table
763    .type _vector_table, %function
764    .p2align 2
765    .align 5
766    _vector_table:
767        ldr     pc, =_start
768        ldr     pc, =_asm_undefined_handler
769        ldr     pc, =_asm_svc_handler
770        ldr     pc, =_asm_prefetch_abort_handler
771        ldr     pc, =_asm_data_abort_handler
772        ldr     pc, =_asm_hvc_handler
773        ldr     pc, =_asm_irq_handler
774        ldr     pc, =_asm_fiq_handler
775    .size _vector_table, . - _vector_table
776    .popsection
777    "#
778);
779
780// # _asm_core_start
781//
782// The _asm_core_start function takes the core number in r0. It sets
783// up the stack pointers, the FPU (if required), and jumps to kmain.
784#[cfg(target_arch = "arm")]
785core::arch::global_asm!(
786    r#"
787    // Work around https://github.com/rust-lang/rust/issues/127269
788    .fpu vfp2
789    .pushsection .text._asm_core_start
790    .arm
791    .global _asm_core_start
792    .type _asm_core_start, %function
793    .p2align 2
794    _asm_core_start:
795        // Keep our core number for later
796        mov     r12, r0
797        // Set up stacks (core number in r0)
798        bl      _asm_stack_setup_preallocated
799    "#,
800    #[cfg(armv6_or_higher)]
801    r#"
802        // Clear Thumb Exception bit
803        mrc     p15, 0, r0, c1, c0, 0
804        bic     r0, #0x40000000
805        mcr     p15, 0, r0, c1, c0, 0
806    "#,
807    #[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))]
808    r#"
809        // Allow VFP coprocessor access
810        mrc     p15, 0, r0, c1, c0, 2
811        orr     r0, r0, #0xF00000
812        mcr     p15, 0, r0, c1, c0, 2
813        // Enable VFP
814        mov     r0, #0x40000000
815        vmsr    fpexc, r0
816    "#,
817    r#"
818        // Zero all registers before calling kmain (except r0)
819        mov     r1, 0
820        mov     r2, 0
821        mov     r3, 0
822        mov     r4, 0
823        mov     r5, 0
824        mov     r6, 0
825        mov     r7, 0
826        mov     r8, 0
827        mov     r9, 0
828        mov     r10, 0
829        mov     r11, 0
830        // Check if this is the primary core
831        mov     r0, r12
832        mov     r12, 0
833        cmp     r0, 0
834        bne     1f
835        // Jump to application with primary core
836        bl      kmain
837        // In case the application returns, loop forever
838        b       .
839    1:
840        // Jump to application with secondary core
841        bl      kmain_secondary
842        // In case the application returns, loop forever
843        b       .
844    .size _asm_core_start, . - _asm_core_start
845    .popsection
846    "#
847);
848
849/// Spins secondary cores.
850///
851/// This function is exported so the linker can use it as a default
852/// implementation of `kmain_secondary`, but it's considered an internal API
853/// that we don't expect you to call.
854#[unsafe(no_mangle)]
855#[cfg(target_arch = "arm")]
856pub extern "C" fn _default_kmain_secondary() {
857    #[cfg(armv7_or_higher)]
858    loop {
859        aarch32_cpu::asm::wfe();
860    }
861    #[cfg(not(armv7_or_higher))]
862    loop {
863        core::hint::spin_loop();
864    }
865}
866
867// # _asm_stack_setup_preallocated
868//
869// Configure a stack for every mode using linker provided constants.
870//
871// Leaves you in SYS mode at the end.
872//
873// Pass the core number in r0
874#[cfg(target_arch = "arm")]
875core::arch::global_asm!(
876    r#"
877    .pushsection .text._asm_stack_setup_preallocated
878    .arm
879    .global _asm_stack_setup_preallocated
880    .type _asm_stack_setup_preallocated, %function
881    .p2align 2
882    _asm_stack_setup_preallocated:
883        // Save LR from whatever mode we're currently in
884        mov     r3, lr
885        // (we might not be in the same mode when we return).
886        // Set stack pointer and mask interrupts for UND mode (Mode 0x1B)
887        msr     cpsr_c, {und_mode}
888        ldr	    r2, =_und_stack_high_end
889        ldr	    r1, =_und_stack_size
890        muls    r1, r1, r0
891        subs    sp, r2, r1
892        // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13)
893        msr     cpsr_c, {svc_mode}
894        ldr	    r2, =_svc_stack_high_end
895        ldr	    r1, =_svc_stack_size
896        muls    r1, r1, r0
897        subs    sp, r2, r1
898        // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17)
899        msr     cpsr_c, {abt_mode}
900        ldr	    r2, =_abt_stack_high_end
901        ldr	    r1, =_abt_stack_size
902        muls    r1, r1, r0
903        subs    sp, r2, r1
904        // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12)
905        msr     cpsr_c, {irq_mode}
906        ldr	    r2, =_irq_stack_high_end
907        ldr	    r1, =_irq_stack_size
908        muls    r1, r1, r0
909        subs    sp, r2, r1
910        // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11)
911        msr     cpsr_c, {fiq_mode}
912        ldr	    r2, =_fiq_stack_high_end
913        ldr	    r1, =_fiq_stack_size
914        muls    r1, r1, r0
915        subs    sp, r2, r1
916        // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F)
917        msr     cpsr_c, {sys_mode}
918        ldr	    r2, =_sys_stack_high_end
919        ldr	    r1, =_sys_stack_size
920        muls    r1, r1, r0
921        subs    sp, r2, r1
922        // return to caller
923        bx      r3
924    .size _asm_stack_setup_preallocated, . - _asm_stack_setup_preallocated
925    .popsection
926    "#,
927    und_mode = const {
928        Cpsr::new_with_raw_value(0)
929            .with_mode(ProcessorMode::Und)
930            .with_i(true)
931            .with_f(true)
932            .raw_value()
933    },
934    svc_mode = const {
935        Cpsr::new_with_raw_value(0)
936            .with_mode(ProcessorMode::Svc)
937            .with_i(true)
938            .with_f(true)
939            .raw_value()
940    },
941    abt_mode = const {
942        Cpsr::new_with_raw_value(0)
943            .with_mode(ProcessorMode::Abt)
944            .with_i(true)
945            .with_f(true)
946            .raw_value()
947    },
948    fiq_mode = const {
949        Cpsr::new_with_raw_value(0)
950            .with_mode(ProcessorMode::Fiq)
951            .with_i(true)
952            .with_f(true)
953            .raw_value()
954    },
955    irq_mode = const {
956        Cpsr::new_with_raw_value(0)
957            .with_mode(ProcessorMode::Irq)
958            .with_i(true)
959            .with_f(true)
960            .raw_value()
961    },
962    sys_mode = const {
963        Cpsr::new_with_raw_value(0)
964            .with_mode(ProcessorMode::Sys)
965            .with_i(true)
966            .with_f(true)
967            .raw_value()
968    },
969);
970
971// # _asm_init_segments
972//
973// Initialises stacks, .data and .bss
974#[cfg(target_arch = "arm")]
975core::arch::global_asm!(
976    r#"
977    // Work around https://github.com/rust-lang/rust/issues/127269
978    .fpu vfp2
979
980    .pushsection .text._asm_init_segments
981    .arm
982    .global _asm_init_segments
983    .type _asm_init_segments, %function
984    .p2align 2
985    _asm_init_segments:
986        // Zero .bss
987        ldr     r0, =__sbss
988        ldr     r1, =__ebss
989        mov     r2, 0
990    0:
991        cmp     r1, r0
992        beq     1f
993        stm     r0!, {{r2}}
994        b       0b
995    1:
996        // Zero the stacks
997        ldr     r0, =_stacks_low_end
998        ldr     r1, =_stacks_high_end
999        mov     r2, 0
1000    0:
1001        cmp     r1, r0
1002        beq     1f
1003        stm     r0!, {{r2}}
1004        b       0b
1005    1:
1006        // Initialise .data
1007        ldr     r0, =__sdata
1008        ldr     r1, =__edata
1009        ldr     r2, =__sidata
1010    0:
1011        cmp     r1, r0
1012        beq     1f
1013        ldm     r2!, {{r3}}
1014        stm     r0!, {{r3}}
1015        b       0b
1016    1:
1017    	// return to caller
1018        bx      lr
1019    .size _asm_init_segments, . - _asm_init_segments
1020    .popsection
1021    "#,
1022);
1023
1024// # _asm_default_fiq_handler
1025//
1026// Default asm FIQ exception handler (it's just a spin-loop)
1027//
1028// We end up here if a FIQ fires and the weak 'PROVIDE' in the link.x
1029// file hasn't been over-ridden.
1030//
1031// Cannot be a Rust/C function because it can only touch registers R8 to R12, SP and LR
1032//
1033// This function must produce A32 machine code, because it's called by the Vector Table
1034// with a raw PC load and the Vector Table is always in A32 machine code.
1035#[cfg(target_arch = "arm")]
1036core::arch::global_asm!(
1037    r#"
1038    .pushsection .text._asm_default_fiq_handler
1039    .arm
1040    .global _asm_default_fiq_handler
1041    .type _asm_default_fiq_handler, %function
1042    .p2align 2
1043    _asm_default_fiq_handler:
1044        b       _asm_default_fiq_handler
1045    .size    _asm_default_fiq_handler, . - _asm_default_fiq_handler
1046    .popsection
1047    "#,
1048);
1049
1050/// Our default exception handler.
1051///
1052/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
1053/// file hasn't been over-ridden.
1054///
1055/// The assembly trampolines allow this to be a normal `extern "C"` function,
1056/// because they save and restore the necessary state.
1057#[unsafe(no_mangle)]
1058pub extern "C" fn _default_handler() {
1059    loop {
1060        core::hint::spin_loop();
1061    }
1062}
1063
1064/// LLVM intrinsic for memory barriers
1065///
1066/// Only required on Armv4T and Armv5TE, because Armv6K onwards support atomics.
1067#[unsafe(no_mangle)]
1068#[cfg(armv5te_or_lower)]
1069pub extern "C" fn __sync_synchronize() {
1070    // we don't have a barrier instruction - the linux kernel just uses an empty inline asm block
1071    // so we do the same.
1072    unsafe {
1073        core::arch::asm!("");
1074    }
1075}
1076
1077// *****************************************************************************
1078// End of file
1079// *****************************************************************************