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