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// *****************************************************************************