cortex_a_rt/
lib.rs

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