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