cortex_r_rt/
lib.rs

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