Skip to main content

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//!
21//! - `fpu-d32`: Make the interrupt context store routines save the upper
22//!   double-precision registers.
23//!
24//!   If your program is using all 32 double-precision registers (e.g. if you
25//!   have set the `+d32` target feature) then you need to enable this option
26//!   otherwise important FPU state may be lost when an exception occurs.
27//
28//! - `el2-mode`: Leave the processor in EL2/PL2 mode on boot-up, and expect to
29//!   handle interrupts in HYP mode using ELR_hyp. Useful if you want to write a
30//!   hypervisor or other low-level firmware.
31//!
32//! - `svc-stack-interrupt`: Use the SVC stack when an interrupt occurs, instead
33//!   of using the SYS stack. Useful if you are writing an RTOS and your SYS
34//!   stack is actually the USR stack for the running task.
35//!
36//! ## Information about the Run-Time
37//!
38//! Transferring from System Mode to User Mode (i.e. implementing an RTOS) is
39//! not handled here.
40//!
41//! If your processor starts in Hyp mode, this runtime will be transfer it to
42//! System mode. If you wish to write a hypervisor, you will need to replace
43//! this library with something more advanced.
44//!
45//! We assume that a set of symbols exist, either for constants or for C
46//! compatible functions or for naked raw-assembly functions. They are described
47//! in the next three sections.
48//!
49//! ## Constants
50//!
51//! * `_num_cores` - the number of CPU core (and hence the number of copies of
52//!   each stack). Must be > 0.
53//! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
54//!   aligned.
55//! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
56//!   aligned.
57//! * `_fiq_stack_size` - the number of bytes to be reserved for stack space
58//!   when in FIQ mode; will be padded to a multiple of 8.
59//! * `_irq_stack_size` - the number of bytes to be reserved for stack space
60//!   when in FIQ mode; will be padded to a multiple of 8.
61//! * `_svc_stack_size` - the number of bytes to be reserved for stack space
62//!   when in SVC mode; will be padded to a multiple of 8.
63//! * `_und_stack_size` - the number of bytes to be reserved for stack space
64//!   when in Undefined mode; will be padded to a multiple of 8.
65//! * `_abt_stack_size` - the number of bytes to be reserved for stack space
66//!   when in Abort mode; will be padded to a multiple of 8.
67//! * `_hyp_stack_size` - the number of bytes to be reserved for stack space
68//!   when in Hyp mode; will be padded to a multiple of 8.
69//! * `_sys_stack_size` - the number of bytes to be reserved for stack space
70//!   when in System mode; will be padded to a multiple of 8.
71//! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
72//! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
73//! * `__sidata` - the start of the initialisation values for data, in read-only
74//!   memory. Must be 4-byte aligned.
75//!
76//! Using our default start-up function `_default_start`, the memory between
77//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and
78//! `__edata` is initialised with the data found at `__sidata`.
79//!
80//! ## Stacks
81//!
82//! Stacks are located in `.stacks` section which is mapped to the `STACKS`
83//! memory region. Per default, the stacks are pushed to the end of the `STACKS`
84//! by a filler section. We allocate stacks for each core, based on the
85//! `_num_cores` linker symbol.
86//!
87//! The stacks look like:
88//!
89//! ```text
90//! +------------------+ <---- ORIGIN(STACKS) + LENGTH(STACKS)
91//! |     SYS Stack    | } _sys_stack_size * _num_cores bytes
92//! +------------------+
93//! |     FIQ Stack    | } _fiq_stack_size * _num_cores bytes
94//! +------------------+
95//! |     IRQ Stack    | } _irq_stack_size * _num_cores bytes
96//! +------------------+
97//! |     HYP Stack    | } _hyp_stack_size * _num_cores bytes (only used on Armv8-R)
98//! +------------------+
99//! |     ABT Stack    | } _abt_stack_size * _num_cores bytes
100//! +------------------+
101//! |     SVC Stack    | } _svc_stack_size * _num_cores bytes
102//! +------------------+
103//! |     UND Stack    | } _und_stack_size * _num_cores bytes
104//! +------------------+
105//! |  filler section  |
106//! +------------------+ <---- ORIGIN(STACKS)
107//! ```
108//!
109//! Our linker script PROVIDEs a symbol `_pack_stacks`. By setting this symbol
110//! to 0 in memory.x, the stacks can be moved to the beginning of the `STACKS`
111//! region or the end of the previous section located in STACKS or its alias.
112//!
113//! ## C-Compatible Functions
114//!
115//! ### Main Function
116//!
117//! The symbol `kmain` should be an `extern "C"` function. It is called in SYS
118//! mode after all the global variables have been initialised. There is no
119//! default - this function is mandatory.
120//!
121//! ```rust
122//! #[unsafe(no_mangle)]
123//! extern "C" fn kmain() -> ! {
124//!     loop { }
125//! }
126//! ```
127//!
128//! You can also create a 'kmain' function by using the `#[entry]` attribute on
129//! a normal Rust function. The function will be renamed in such a way that the
130//! start-up assembly code can find it, but normal Rust code cannot. Therefore
131//! you can be assured that the function will only be called once (unless
132//! someone resorts to `unsafe` Rust to import the `kmain` symbol as an `extern
133//! "C" fn`).
134//!
135//! ```rust
136//! use aarch32_rt::entry;
137//!
138//! #[entry]
139//! fn my_main() -> ! {
140//!     loop { }
141//! }
142//! ```
143//!
144//! ### Undefined Handler
145//!
146//! The symbol `_undefined_handler` should be an `extern "C"` function. It is
147//! called in UND mode when an [Undefined Instruction Exception] occurs.
148//!
149//! [Undefined Instruction Exception]:
150//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Undefined-Instruction-exception?lang=en
151//!
152//! Our linker script PROVIDEs a default `_undefined_handler` symbol which is an
153//! alias for the `_default_handler` function. You can override it by defining
154//! your own `_undefined_handler` function, like:
155//!
156//! ```rust
157//! /// Does not return
158//! #[unsafe(no_mangle)]
159//! extern "C" fn _undefined_handler(addr: usize) -> ! {
160//!     loop { }
161//! }
162//! ```
163//!
164//! or:
165//!
166//! ```rust
167//! /// Execution will continue from the returned address.
168//! ///
169//! /// Return `addr` to go back and execute the faulting instruction again.
170//! #[unsafe(no_mangle)]
171//! unsafe extern "C" fn _undefined_handler(addr: usize) -> usize {
172//!     // do stuff here, then return to the address *after* the one
173//!     // that failed
174//!     addr + 4
175//! }
176//! ```
177//!
178//! You can create a `_undefined_handler` function by using the
179//! `#[exception(Undefined)]` attribute on a Rust function with the appropriate
180//! arguments and return type.
181//!
182//! ```rust
183//! use aarch32_rt::exception;
184//!
185//! #[exception(Undefined)]
186//! fn my_handler(addr: usize) -> ! {
187//!     loop { }
188//! }
189//! ```
190//!
191//! or:
192//!
193//! ```rust
194//! use aarch32_rt::exception;
195//!
196//! #[exception(Undefined)]
197//! unsafe fn my_handler(addr: usize) -> usize {
198//!     // do stuff here, then return the address to return to
199//!     addr + 4
200//! }
201//! ```
202//!
203//! ### Supervisor Call Handler
204//!
205//! The symbol `_svc_handler` should be an `extern "C"` function. It is called
206//! in SVC mode when an [Supervisor Call Exception] occurs.
207//!
208//! [Supervisor Call Exception]:
209//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en
210//!
211//! Returning from this function will cause execution to resume at the function
212//! the triggered the exception, immediately after the SVC instruction. You
213//! cannot control where execution resumes. The function is passed the literal
214//! integer argument to the `svc` instruction, which is extracted from the
215//! machine code for you by the default assembly trampoline, along with
216//! registers r0 through r5, in the form of a reference to a `Frame` structure.
217//!
218//! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias
219//! for the `_default_handler` function. You can override it by defining your
220//! own `_svc_handler` function, like:
221//!
222//! ```rust
223//! #[unsafe(no_mangle)]
224//! extern "C" fn _svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 {
225//!     // do stuff here
226//!     todo!()
227//! }
228//! ```
229//!
230//! You can also create a `_svc_handler` function by using the
231//! `#[exception(SupervisorCall)]` attribute on a normal Rust function.
232//!
233//! ```rust
234//! use aarch32_rt::exception;
235//!
236//! #[exception(SupervisorCall)]
237//! fn svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 {
238//!     // do stuff here
239//!     todo!()
240//! }
241//! ```
242//!
243//! ### Hypervisor Call Handler
244//!
245//! The symbol `_hvc_handler` should be an `extern "C"` function. It is called
246//! in HYP mode when an [Hypervisor Call Exception] occurs.
247//!
248//! [Hypervisor Call Exception]:
249//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Hypervisor-Call--HVC--exception?lang=en
250//!
251//! Returning from this function will cause execution to resume at the function
252//! the triggered the exception, immediately after the HVC instruction. You
253//! cannot control where execution resumes. The function is passed contents of
254//! the Hypervisor Syndrome Register (HSR) register, which is fetched by the
255//! default assembly trampoline, along with registers r0 through r5, in the form
256//! of a reference to a `Frame` structure.
257//!
258//! Our linker script PROVIDEs a default `_hvc_handler` symbol which is an alias
259//! for the `_default_handler` function. You can override it by defining your
260//! own `_hvc_handler` function, like:
261//!
262//! ```rust
263//! #[unsafe(no_mangle)]
264//! extern "C" fn _hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 {
265//!     // do stuff here
266//!     todo!()
267//! }
268//! ```
269//!
270//! You can also create a `_hvc_handler` function by using the
271//! `#[exception(HypervisorCall)]` attribute on a normal Rust function.
272//!
273//! ```rust
274//! use aarch32_rt::exception;
275//!
276//! #[exception(HypervisorCall)]
277//! fn my_hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 {
278//!     // do stuff here
279//!     todo!()
280//! }
281//! ```
282//!
283//! If you wish to inspect the HSR value, you can use the `aarch32-cpu` crate:
284//!
285//! ```rust,ignore
286//! let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr);
287//! ```
288//!
289//! ### Prefetch Abort Handler
290//!
291//! The symbol `_prefetch_abort_handler` should be an `extern "C"` function. It
292//! is called in ABT mode when a [Prefetch Abort Exception] occurs.
293//!
294//! [Prefetch Abort Exception]:
295//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Prefetch-Abort-exception?lang=en
296//!
297//! Our linker script PROVIDEs a default `_prefetch_abort_handler` symbol which
298//! is an alias for the `_default_handler` function. You can override it by
299//! defining your own `_undefined_handler` function.
300//!
301//! This function takes the address of faulting instruction, and can either not
302//! return:
303//!
304//! ```rust
305//! #[unsafe(no_mangle)]
306//! extern "C" fn _prefetch_abort_handler(addr: usize) -> ! {
307//!     loop { }
308//! }
309//! ```
310//!
311//! Or it can return an address where execution should resume after the
312//! Exception handler is complete (which is unsafe):
313//!
314//! ```rust
315//! #[unsafe(no_mangle)]
316//! unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize {
317//!     // do stuff, then go back to the instruction after the one that failed
318//!     addr + 4
319//! }
320//! ```
321//!
322//! You can create a `_prefetch_abort_handler` function by using the
323//! `#[exception(PrefetchAbort)]` macro on a Rust function with the appropriate
324//! arguments and return type.
325//!
326//! ```rust
327//! use aarch32_rt::exception;
328//!
329//! #[exception(PrefetchAbort)]
330//! fn my_handler(addr: usize) -> ! {
331//!     loop { }
332//! }
333//! ```
334//!
335//! or:
336//!
337//! ```rust
338//! use aarch32_rt::exception;
339//!
340//! #[exception(PrefetchAbort)]
341//! unsafe fn my_handler(addr: usize) -> usize {
342//!     // do stuff, then go back to the instruction after the one that failed
343//!     addr + 4
344//! }
345//! ```
346//!
347//! ### Data Abort Handler
348//!
349//! The symbol `_data_abort_handler` should be an `extern "C"` function. It is
350//! called in ABT mode when a Data Abort Exception occurs.
351//!
352//! [Data Abort Exception]:
353//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Data-Abort-exception?lang=en
354//!
355//! Our linker script PROVIDEs a default `_data_abort_handler` symbol which is
356//! an alias for the `_default_handler` function. You can override it by
357//! defining your own `_undefined_handler` function.
358//!
359//! This function takes the address of faulting instruction, and can either not
360//! return:
361//!
362//! ```rust
363//! #[unsafe(no_mangle)]
364//! extern "C" fn _data_abort_handler(addr: usize) -> ! {
365//!     loop { }
366//! }
367//! ```
368//!
369//! Or it can return an address where execution should resume after the
370//! Exception handler is complete (which is unsafe):
371//!
372//! ```rust
373//! #[unsafe(no_mangle)]
374//! unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize {
375//!     // do stuff, then go back to the instruction after the one that failed
376//!     addr + 4
377//! }
378//! ```
379//!
380//! You can create a `_data_abort_handler` function by using the
381//! `#[exception(DataAbort)]` macro on a Rust function with the appropriate
382//! arguments and return type.
383//!
384//! ```rust
385//! use aarch32_rt::exception;
386//!
387//! #[exception(DataAbort)]
388//! fn my_handler(addr: usize) -> ! {
389//!     loop { }
390//! }
391//! ```
392//!
393//! or:
394//!
395//! ```rust
396//! use aarch32_rt::exception;
397//!
398//! #[exception(DataAbort)]
399//! unsafe fn my_handler(addr: usize) -> usize {
400//!     // do stuff, then go back to the instruction after the one that failed
401//!     addr + 4
402//! }
403//! ```
404//!
405//! ### IRQ Handler
406//!
407//! The symbol `_irq_handler` should be an `extern "C"` function. It is called
408//! in SYS mode or SVC mode (not IRQ mode!) when an [Interrupt] occurs. Use the
409//! `svc-stack-interrupt` feature to select SVC mode instead of the default SYS
410//! mode. You might want to use `svc-stack-interrupt` if you are running an RTOS
411//! and you don't want to push a bunch of state into the running thread's stack
412//! when an interrupt occurs.
413//!
414//! [Interrupt]:
415//!     https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/IRQ-exception?lang=en
416//!
417//! Returning from this function will cause execution to resume at wherever it
418//! was interrupted. You cannot control where execution resumes.
419//!
420//! This function is entered with interrupts masked, but you may unmask (i.e.
421//! enable) interrupts inside this function if desired. You will probably want
422//! to talk to your interrupt controller first, otherwise you'll just keep
423//! re-entering this interrupt handler recursively until you stack overflow.
424//!
425//! Our linker script PROVIDEs a default `_irq_handler` symbol which is an alias
426//! for `_default_handler`. You can override it by defining your own
427//! `_irq_handler` function.
428//!
429//! Expected prototype:
430//!
431//! ```rust
432//! #[unsafe(no_mangle)]
433//! extern "C" fn _irq_handler() {
434//!     // 1. Talk to interrupt controller
435//!     // 2. Handle interrupt
436//!     // 3. Clear interrupt
437//! }
438//! ```
439//!
440//! You can also create a `_irq_handler` function by using the `#[irq]`
441//! attribute on a normal Rust function.
442//!
443//! ```rust
444//! use aarch32_rt::irq;
445//!
446//! #[irq]
447//! fn my_irq_handler() {
448//!     // 1. Talk to interrupt controller
449//!     // 2. Handle interrupt
450//!     // 3. Clear interrupt
451//! }
452//! ```
453//!
454//! ## ASM functions
455//!
456//! These are the naked 'raw' assembly functions the run-time requires:
457//!
458//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function
459//!   at `_default_start` but you can override it. The provided default start
460//!   function will initialise all global variables and then call `kmain` in SYS
461//!   mode. Some SoCs require a chip specific startup for tasks like MPU
462//!   initialization or chip specific initialization routines, so if our
463//!   start-up routine doesn't work for you, supply your own `_start` function
464//!   (but feel free to call our `_default_start` as part of it).
465//!
466//! * `_asm_undefined_handler` - a naked function to call when an Undefined
467//!   Exception occurs. Our linker script PROVIDEs a default function at
468//!   `_asm_default_undefined_handler` but you can override it. The provided
469//!   default handler will call `_undefined_handler` in UND mode, saving state
470//!   as required.
471//!
472//! * `_asm_svc_handler` - a naked function to call when an Supervisor Call
473//!   (SVC) Exception occurs. Our linker script PROVIDEs a default function at
474//!   `_asm_default_svc_handler` but you can override it. The provided default
475//!   handler will call `_svc_handler` in SVC mode, saving state as required.
476//!
477//! * `_asm_prefetch_abort_handler` - a naked function to call when a Prefetch
478//!   Abort Exception occurs. Our linker script PROVIDEs a default function at
479//!   `_asm_default_prefetch_abort_handler` but you can override it. The
480//!   provided default handler will call `_prefetch_abort_handler`, saving state
481//!   as required. Note that Prefetch Abort Exceptions are handled in Abort Mode
482//!   (ABT), Monitor Mode (MON) or Hyp Mode (HYP), depending on CPU
483//!   configuration.
484//!
485//! * `_asm_data_abort_handler` - a naked function to call when a Data Abort
486//!   Exception occurs. Our linker script PROVIDEs a default function at
487//!   `_asm_default_data_abort_handler` but you can override it. The provided
488//!   default handler will call `_data_abort_handler` in ABT mode, saving state
489//!   as required.
490//!
491//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception
492//!   occurs. Our linker script PROVIDEs a default function at
493//!   `_asm_default_irq_handler` but you can override it. The provided default
494//!   handler will call `_irq_handler` in SYS mode or SVC mode (but not IRQ
495//!   mode), saving state as required.
496//!
497//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt
498//!   Request (FIQ) occurs. Our linker script PROVIDEs a default function at
499//!   `_asm_default_fiq_handler` but you can override it. The provided default
500//!   just spins forever.
501//!
502//! ## Outputs
503//!
504//! This library produces global symbols called:
505//!
506//! * `_vector_table` - the start of the interrupt vector table
507//! * `_default_start` - the default Reset handler, that sets up some stacks and
508//!   calls an `extern "C"` function called `kmain`.
509//! * `_asm_default_undefined_handler` - assembly language trampoline that calls
510//!   `_undefined_handler`
511//! * `_asm_default_svc_handler` - assembly language trampoline that calls
512//!   `_svc_handler`
513//! * `_asm_default_prefetch_abort_handler` - assembly language trampoline that
514//!   calls `_prefetch_abort_handler`
515//! * `_asm_default_data_abort_handler` - assembly language trampoline that
516//!   calls `_data_abort_handler`
517//! * `_asm_default_irq_handler` - assembly language trampoline that calls
518//!   `_irq_handler`
519//! * `_asm_default_fiq_handler` - an FIQ handler that just spins
520//! * `_default_handler` - a C compatible function that spins forever.
521//! * `_init_segments` - initialises `.bss` and `.data` and zeroes the stacks
522//! * `_stack_setup_preallocated` - initialises UND, SVC, ABT, IRQ, FIQ and SYS
523//!   stacks from the `.stacks` section defined in link.x, based on
524//!   _xxx_stack_size values, and the core number given in `r0`
525//! * `_xxx_stack_high_end` and `_xxx_stack_low_end` where the former is the top
526//!   and the latter the bottom of the stack for each mode (`und`, `svc`, `abt`,
527//!   `irq`, `fiq`, `sys`)
528//!
529//! The assembly language trampolines are required because AArch32 processors do
530//! not save a great deal of state on entry to an exception handler, unlike
531//! Armv7-M (and other M-Profile) processors. We must therefore save this state
532//! to the stack using assembly language, before transferring to an `extern "C"`
533//! function. Because FIQ is often performance-sensitive, we don't supply an FIQ
534//! trampoline; if you want to use FIQ, you have to write your own assembly
535//! routine, allowing you to preserve only whatever state is important to you.
536//!
537//! ## Examples
538//!
539//! You can find example code using QEMU inside the [project
540//! repository](https://github.com/rust-embedded/aarch32/tree/main/examples)
541
542#![no_std]
543
544// *****************************************************************************
545// Public Modules
546// *****************************************************************************
547
548pub mod sections;
549pub mod stacks;
550
551// *****************************************************************************
552// Public Imports
553// *****************************************************************************
554
555pub use aarch32_rt_macros::{entry, exception, irq};
556
557// *****************************************************************************
558// Private Modules
559// *****************************************************************************
560
561#[cfg(all(arm_architecture = "v8-r", feature = "el2-mode"))]
562mod arch_v8_hyp;
563
564#[cfg(all(
565    armv7_or_higher,
566    not(all(arm_architecture = "v8-r", feature = "el2-mode"))
567))]
568mod arch_v7;
569
570#[cfg(armv6_or_lower)]
571mod arch_v4;
572
573// *****************************************************************************
574// Private Imports
575// *****************************************************************************
576
577#[cfg(target_arch = "arm")]
578use aarch32_cpu::register::{cpsr::ProcessorMode, Cpsr};
579
580// *****************************************************************************
581// Types
582// *****************************************************************************
583
584/// Arguments stacked on interrupt
585///
586/// This struct is very carefully designed to match the layout of the
587/// registers pushed to the stack in our SVC handler.
588#[derive(Debug, Clone, PartialEq, Eq)]
589#[repr(C)]
590pub struct Frame {
591    pub r0: u32,
592    pub r1: u32,
593    pub r2: u32,
594    pub r3: u32,
595    pub r4: u32,
596    pub r5: u32,
597}
598
599// *****************************************************************************
600// Macros
601// *****************************************************************************
602
603/// This macro expands to nothing.
604///
605/// It's a placeholder for the FPU saving/restoring routine, used on
606/// targets without FPU support.
607#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))]
608#[macro_export]
609macro_rules! fpu_context {
610    ("save") => {
611        ""
612    };
613    ("restore") => {
614        ""
615    };
616}
617
618/// This macro expands to code for saving/restoring FPU context in an exception
619/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment.
620/// It may damage R0-R3.
621///
622/// On entry to this block, we assume that we are in exception context.
623///
624/// This version saves FPU state, assuming 16 DP registers (a 'D16' or 'D16SP'
625/// FPU configuration). Note that SP-only FPUs still have DP registers
626/// - each DP register holds two SP values.
627///
628/// EABI specifies D8-D15 as callee-save, and so we don't
629/// preserve them because any C function we call to handle the exception will
630/// preserve/restore them itself as required.
631#[cfg(all(
632    any(target_abi = "eabihf", feature = "eabi-fpu"),
633    not(feature = "fpu-d32")
634))]
635#[macro_export]
636macro_rules! fpu_context {
637    // save all D16 FPU context, except D8-D15
638    ("save") => {
639        r#"
640        vpush   {{ d0-d7 }}
641        vmrs    r0, FPSCR
642        vmrs    r1, FPEXC
643        push    {{ r0-r1 }}
644        "#
645    };
646    // restore all D16 FPU context, except D8-D15
647    ("restore") => {
648        r#"
649        pop     {{ r0-r1 }}
650        vmsr    FPEXC, r1
651        vmsr    FPSCR, r0
652        vpop    {{ d0-d7 }}
653        "#
654    };
655}
656
657/// This macro expands to code for saving/restoring FPU context in an exception
658/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. It
659/// may damage R0-R3.
660///
661/// On entry to this block, we assume that we are in exception context.
662///
663/// This version saves FPU state assuming 32 DP registers (a 'D32' FPU
664/// configuration).
665///
666/// EABI specifies D8-D15 as callee-save, and so we don't preserve them because
667/// any C function we call to handle the exception will preserve/restore them
668/// itself as required.
669#[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "fpu-d32"))]
670#[macro_export]
671macro_rules! fpu_context {
672    // save all D32 FPU context, except D8-D15
673    ("save") => {
674        r#"
675        vpush   {{ d0-d7 }}
676        vpush   {{ d16-d31 }}
677        vmrs    r0, FPSCR
678        vmrs    r1, FPEXC
679        push    {{ r0-r1 }}
680        "#
681    };
682    // restore all D32 FPU context, except D8-D15
683    ("restore") => {
684        r#"
685        pop     {{ r0-r1 }}
686        vmsr    FPEXC, r1
687        vmsr    FPSCR, r0
688        vpop    {{ d16-d31 }}
689        vpop    {{ d0-d7 }}
690        "#
691    };
692}
693
694// *****************************************************************************
695// Functions
696// *****************************************************************************
697
698// The Interrupt Vector Table, and some default assembly-language handler.
699//
700// Needs to be aligned to 5bits/2^5 to be stored correctly in VBAR
701//
702// Need to be assembled as Arm-mode because the Thumb Exception bit is cleared
703#[cfg(target_arch = "arm")]
704core::arch::global_asm!(
705    r#"
706    .pushsection .vector_table,"ax",%progbits
707    .arm
708    .global _vector_table
709    .type _vector_table, %function
710    .align 5
711    _vector_table:
712        ldr     pc, =_start
713        ldr     pc, =_asm_undefined_handler
714        ldr     pc, =_asm_svc_handler
715        ldr     pc, =_asm_prefetch_abort_handler
716        ldr     pc, =_asm_data_abort_handler
717        ldr     pc, =_asm_hvc_handler
718        ldr     pc, =_asm_irq_handler
719        ldr     pc, =_asm_fiq_handler
720    .size _vector_table, . - _vector_table
721    .popsection
722    "#
723);
724
725// Shared library routines for all architectures
726#[cfg(target_arch = "arm")]
727core::arch::global_asm!(
728    r#"
729    // Work around https://github.com/rust-lang/rust/issues/127269
730    .fpu vfp2
731
732    // Configure a stack for every mode. Leaves you in sys mode.
733    //
734    // Pass the core number in r0
735    .pushsection .text._stack_setup_preallocated
736    .global _stack_setup_preallocated
737    .arm
738    .type _stack_setup_preallocated, %function
739    _stack_setup_preallocated:
740        // Save LR from whatever mode we're currently in
741        mov     r3, lr
742        // (we might not be in the same mode when we return).
743        // Set stack pointer and mask interrupts for UND mode (Mode 0x1B)
744        msr     cpsr_c, {und_mode}
745        ldr	    r2, =_und_stack_high_end
746        ldr	    r1, =_und_stack_size
747        muls    r1, r1, r0
748        subs    sp, r2, r1
749        // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13)
750        msr     cpsr_c, {svc_mode}
751        ldr	    r2, =_svc_stack_high_end
752        ldr	    r1, =_svc_stack_size
753        muls    r1, r1, r0
754        subs    sp, r2, r1
755        // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17)
756        msr     cpsr_c, {abt_mode}
757        ldr	    r2, =_abt_stack_high_end
758        ldr	    r1, =_abt_stack_size
759        muls    r1, r1, r0
760        subs    sp, r2, r1
761        // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12)
762        msr     cpsr_c, {irq_mode}
763        ldr	    r2, =_irq_stack_high_end
764        ldr	    r1, =_irq_stack_size
765        muls    r1, r1, r0
766        subs    sp, r2, r1
767        // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11)
768        msr     cpsr_c, {fiq_mode}
769        ldr	    r2, =_fiq_stack_high_end
770        ldr	    r1, =_fiq_stack_size
771        muls    r1, r1, r0
772        subs    sp, r2, r1
773        // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F)
774        msr     cpsr_c, {sys_mode}
775        ldr	    r2, =_sys_stack_high_end
776        ldr	    r1, =_sys_stack_size
777        muls    r1, r1, r0
778        subs    sp, r2, r1
779        // return to caller
780        bx      r3
781    .size _stack_setup_preallocated, . - _stack_setup_preallocated
782    .popsection
783
784    // Initialises stacks, .data and .bss
785    .pushsection .text._init_segments
786    .arm
787    .global _init_segments
788    .type _init_segments, %function
789    _init_segments:
790        // Zero .bss
791        ldr     r0, =__sbss
792        ldr     r1, =__ebss
793        mov     r2, 0
794    0:
795        cmp     r1, r0
796        beq     1f
797        stm     r0!, {{r2}}
798        b       0b
799    1:
800        // Zero the stacks
801        ldr     r0, =_stacks_low_end
802        ldr     r1, =_stacks_high_end
803        mov     r2, 0
804    0:
805        cmp     r1, r0
806        beq     1f
807        stm     r0!, {{r2}}
808        b       0b
809    1:
810        // Initialise .data
811        ldr     r0, =__sdata
812        ldr     r1, =__edata
813        ldr     r2, =__sidata
814    0:
815        cmp     r1, r0
816        beq     1f
817        ldm     r2!, {{r3}}
818        stm     r0!, {{r3}}
819        b       0b
820    1:
821    	// return to caller
822        bx      lr
823    .size _init_segments, . - _init_segments
824    .popsection
825    "#,
826    und_mode = const {
827        Cpsr::new_with_raw_value(0)
828            .with_mode(ProcessorMode::Und)
829            .with_i(true)
830            .with_f(true)
831            .raw_value()
832    },
833    svc_mode = const {
834        Cpsr::new_with_raw_value(0)
835            .with_mode(ProcessorMode::Svc)
836            .with_i(true)
837            .with_f(true)
838            .raw_value()
839    },
840    abt_mode = const {
841        Cpsr::new_with_raw_value(0)
842            .with_mode(ProcessorMode::Abt)
843            .with_i(true)
844            .with_f(true)
845            .raw_value()
846    },
847    fiq_mode = const {
848        Cpsr::new_with_raw_value(0)
849            .with_mode(ProcessorMode::Fiq)
850            .with_i(true)
851            .with_f(true)
852            .raw_value()
853    },
854    irq_mode = const {
855        Cpsr::new_with_raw_value(0)
856            .with_mode(ProcessorMode::Irq)
857            .with_i(true)
858            .with_f(true)
859            .raw_value()
860    },
861    sys_mode = const {
862        Cpsr::new_with_raw_value(0)
863            .with_mode(ProcessorMode::Sys)
864            .with_i(true)
865            .with_f(true)
866            .raw_value()
867    },
868);
869
870// Default asm FIQ exception handler (it's just a spin-loop)
871//
872// We end up here if a FIQ fires and the weak 'PROVIDE' in the link.x
873// file hasn't been over-ridden.
874//
875// Cannot be a Rust/C function because it can only touch registers R8 to R12, SP and LR
876#[cfg(target_arch = "arm")]
877core::arch::global_asm!(
878    r#"
879    .pushsection .text._asm_default_fiq_handler
880
881    // Our default FIQ handler
882    .global _asm_default_fiq_handler
883    .type _asm_default_fiq_handler, %function
884    _asm_default_fiq_handler:
885        b       _asm_default_fiq_handler
886    .size    _asm_default_fiq_handler, . - _asm_default_fiq_handler
887    .popsection
888    "#,
889);
890
891/// Our default exception handler.
892///
893/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
894/// file hasn't been over-ridden.
895///
896/// The assembly trampolines allow this to be a normal `extern "C"` function,
897/// because they save and restore the necessary state.
898#[unsafe(no_mangle)]
899pub extern "C" fn _default_handler() {
900    loop {
901        core::hint::spin_loop();
902    }
903}
904
905/// LLVM intrinsic for memory barriers
906///
907/// Only required on Armv4T and Armv5TE, because Armv6K onwards support atomics.
908#[unsafe(no_mangle)]
909#[cfg(armv5te_or_lower)]
910pub extern "C" fn __sync_synchronize() {
911    // we don't have a barrier instruction - the linux kernel just uses an empty inline asm block
912    // so we do the same.
913    unsafe {
914        core::arch::asm!("");
915    }
916}
917
918// *****************************************************************************
919// End of file
920// *****************************************************************************