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