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. Defaults to 1.
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//! ## SMP Support
503//!
504//! This library supports SMP operation on ARMv7-A, ARMv7-R and ARMv8-R.
505//!
506//! To enable SMP support, add `PROVIDE(_num_cores = N)` to your linker script
507//! (e.g. your `memory.x` file). This will cause space for 'N' copies of each
508//! stack to be reserved so that each core gets its own stack (see the section
509//! on 'Stacks', above).
510//!
511//! You must also write a function called `_asm_secondary_core_park` (which must
512//! be written in assembly, and not Rust, because it is executed before stacks
513//! and global memory are initialised). On start-up the bottom eight bits of
514//! MPIDR register are taken as the Core ID. On Core ID 0, normal start-up will
515//! occur. For non-zero Core IDs (so-called *secondary cores*), the cores call
516//! the `_asm_secondary_core_park` function, passing the core ID in `r0`. This
517//! function (which defaults to an infinite loop) should put the running core to
518//! sleep and cause it to wait for some sort of signal from Core 0. This allows
519//! Core 0 to complete the initialisation of global memory (`.data`, `.bss`,
520//! etc) before the secondary cores run (and those cores must not re-initialise
521//! global memory). After the cores have left the park routine and completed
522//! their local initialisation (i.e. set their stack pointers to their unique
523//! stacks), they execute the function `kmain_secondary` (recall that Core 0
524//! executes a function called `kmain`).
525//!
526//! In our example for the MPS3-AN536, we have the secondary core wait on a
527//! hardware register in one of the peripherals, because it has a known value at
528//! reset.
529//!
530//! ```rust,ignore
531//! #[unsafe(naked)]
532//! #[unsafe(no_mangle)]
533//! pub unsafe extern "C" fn _asm_secondary_core_park() {
534//! core::arch::naked_asm!(
535//! r#"
536//! // Some hardware register
537//! ldr r0, =0xE020_2000
538//! 1:
539//! // Wait until Core 0 does a 'sev'
540//! wfe
541//! // Spin until register is non-zero.
542//! ldr r1, [r0]
543//! cmp r1, 0
544//! beq 1b
545//! // return to start-up
546//! bx lr
547//! "#,
548//! );
549//! }
550//! ```
551//!
552//! ## Outputs
553//!
554//! This library produces global symbols called:
555//!
556//! * `_vector_table` - the start of the interrupt vector table
557//! * `_default_start` - the default Reset handler, that sets up some stacks and
558//! calls an `extern "C"` function called `kmain`.
559//! * `_asm_default_undefined_handler` - assembly language trampoline that calls
560//! `_undefined_handler`
561//! * `_asm_default_svc_handler` - assembly language trampoline that calls
562//! `_svc_handler`
563//! * `_asm_default_prefetch_abort_handler` - assembly language trampoline that
564//! calls `_prefetch_abort_handler`
565//! * `_asm_default_data_abort_handler` - assembly language trampoline that
566//! calls `_data_abort_handler`
567//! * `_asm_default_irq_handler` - assembly language trampoline that calls
568//! `_irq_handler`
569//! * `_asm_default_fiq_handler` - an FIQ handler that just spins
570//! * `_asm_default_secondary_core_park` - spins secondary cores forever
571//! * `_default_handler` - a C compatible function that spins forever.
572//! * `_asm_init_segments` - initialises `.bss` and `.data` and zeroes the
573//! stacks
574//! * `_asm_core_start` - sets up stacks, enables FPU (if required), and jumps
575//! to `kmain` or `kmain_secondary`. Takes the Core ID in `r0`.
576//! * `_asm_stack_setup_preallocated` - initialises UND, SVC, ABT, IRQ, FIQ and SYS
577//! stacks from the `.stacks` section defined in link.x, based on
578//! `_xxx_stack_size` values. Takes the Core ID in `r0`.
579//! * `_xxx_stack_high_end` and `_xxx_stack_low_end` where the former is the top
580//! and the latter the bottom of the stack for each mode (`und`, `svc`, `abt`,
581//! `irq`, `fiq`, `sys`)
582//!
583//! The assembly language trampolines are required because AArch32 processors do
584//! not save a great deal of state on entry to an exception handler, unlike
585//! Armv7-M (and other M-Profile) processors. We must therefore save this state
586//! to the stack using assembly language, before transferring to an `extern "C"`
587//! function. Because FIQ is often performance-sensitive, we don't supply an FIQ
588//! trampoline; if you want to use FIQ, you have to write your own assembly
589//! routine, allowing you to preserve only whatever state is important to you.
590//!
591//! ## Examples
592//!
593//! You can find example code using QEMU inside the [project
594//! repository](https://github.com/rust-embedded/aarch32/tree/main/examples)
595
596#![no_std]
597
598// *****************************************************************************
599// Public Modules
600// *****************************************************************************
601
602pub mod sections;
603pub mod stacks;
604
605// *****************************************************************************
606// Public Imports
607// *****************************************************************************
608
609pub use aarch32_rt_macros::{entry, exception, irq};
610
611// *****************************************************************************
612// Private Modules
613// *****************************************************************************
614
615#[cfg(all(arm_architecture = "v8-r", feature = "el2-mode"))]
616mod arch_v8_hyp;
617
618#[cfg(all(
619 armv7_or_higher,
620 not(all(arm_architecture = "v8-r", feature = "el2-mode"))
621))]
622mod arch_v7;
623
624#[cfg(armv6_or_lower)]
625mod arch_v4;
626
627// *****************************************************************************
628// Private Imports
629// *****************************************************************************
630
631#[cfg(target_arch = "arm")]
632use aarch32_cpu::register::{cpsr::ProcessorMode, Cpsr};
633
634// *****************************************************************************
635// Types
636// *****************************************************************************
637
638/// Arguments stacked on interrupt
639///
640/// This struct is very carefully designed to match the layout of the
641/// registers pushed to the stack in our SVC handler.
642#[derive(Debug, Clone, PartialEq, Eq)]
643#[repr(C)]
644pub struct Frame {
645 pub r0: u32,
646 pub r1: u32,
647 pub r2: u32,
648 pub r3: u32,
649 pub r4: u32,
650 pub r5: u32,
651}
652
653// *****************************************************************************
654// Macros
655// *****************************************************************************
656
657/// This macro expands to nothing.
658///
659/// It's a placeholder for the FPU saving/restoring routine, used on
660/// targets without FPU support.
661#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))]
662#[macro_export]
663macro_rules! fpu_context {
664 ("save") => {
665 ""
666 };
667 ("restore") => {
668 ""
669 };
670}
671
672/// This macro expands to code for saving/restoring FPU context in an exception
673/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment.
674/// It may damage R0-R3.
675///
676/// On entry to this block, we assume that we are in exception context.
677///
678/// This version saves FPU state, assuming 16 DP registers (a 'D16' or 'D16SP'
679/// FPU configuration). Note that SP-only FPUs still have DP registers
680/// - each DP register holds two SP values.
681///
682/// EABI specifies D8-D15 as callee-save, and so we don't
683/// preserve them because any C function we call to handle the exception will
684/// preserve/restore them itself as required.
685#[cfg(all(
686 any(target_abi = "eabihf", feature = "eabi-fpu"),
687 not(feature = "fpu-d32")
688))]
689#[macro_export]
690macro_rules! fpu_context {
691 // save all D16 FPU context, except D8-D15
692 ("save") => {
693 r#"
694 vpush {{ d0-d7 }}
695 vmrs r0, FPSCR
696 vmrs r1, FPEXC
697 push {{ r0-r1 }}
698 "#
699 };
700 // restore all D16 FPU context, except D8-D15
701 ("restore") => {
702 r#"
703 pop {{ r0-r1 }}
704 vmsr FPEXC, r1
705 vmsr FPSCR, r0
706 vpop {{ d0-d7 }}
707 "#
708 };
709}
710
711/// This macro expands to code for saving/restoring FPU context in an exception
712/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. It
713/// may damage R0-R3.
714///
715/// On entry to this block, we assume that we are in exception context.
716///
717/// This version saves FPU state assuming 32 DP registers (a 'D32' FPU
718/// configuration).
719///
720/// EABI specifies D8-D15 as callee-save, and so we don't preserve them because
721/// any C function we call to handle the exception will preserve/restore them
722/// itself as required.
723#[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "fpu-d32"))]
724#[macro_export]
725macro_rules! fpu_context {
726 // save all D32 FPU context, except D8-D15
727 ("save") => {
728 r#"
729 vpush {{ d0-d7 }}
730 vpush {{ d16-d31 }}
731 vmrs r0, FPSCR
732 vmrs r1, FPEXC
733 push {{ r0-r1 }}
734 "#
735 };
736 // restore all D32 FPU context, except D8-D15
737 ("restore") => {
738 r#"
739 pop {{ r0-r1 }}
740 vmsr FPEXC, r1
741 vmsr FPSCR, r0
742 vpop {{ d16-d31 }}
743 vpop {{ d0-d7 }}
744 "#
745 };
746}
747
748// *****************************************************************************
749// Functions
750// *****************************************************************************
751
752// The Interrupt Vector Table, and some default assembly-language handler.
753//
754// Needs to be aligned to 5bits/2^5 to be stored correctly in VBAR
755//
756// Need to be assembled as Arm-mode because the Thumb Exception bit is cleared
757#[cfg(target_arch = "arm")]
758core::arch::global_asm!(
759 r#"
760 .pushsection .vector_table,"ax",%progbits
761 .arm
762 .global _vector_table
763 .type _vector_table, %function
764 .p2align 2
765 .align 5
766 _vector_table:
767 ldr pc, =_start
768 ldr pc, =_asm_undefined_handler
769 ldr pc, =_asm_svc_handler
770 ldr pc, =_asm_prefetch_abort_handler
771 ldr pc, =_asm_data_abort_handler
772 ldr pc, =_asm_hvc_handler
773 ldr pc, =_asm_irq_handler
774 ldr pc, =_asm_fiq_handler
775 .size _vector_table, . - _vector_table
776 .popsection
777 "#
778);
779
780// # _asm_core_start
781//
782// The _asm_core_start function takes the core number in r0. It sets
783// up the stack pointers, the FPU (if required), and jumps to kmain.
784#[cfg(target_arch = "arm")]
785core::arch::global_asm!(
786 r#"
787 // Work around https://github.com/rust-lang/rust/issues/127269
788 .fpu vfp2
789 .pushsection .text._asm_core_start
790 .arm
791 .global _asm_core_start
792 .type _asm_core_start, %function
793 .p2align 2
794 _asm_core_start:
795 // Keep our core number for later
796 mov r12, r0
797 // Set up stacks (core number in r0)
798 bl _asm_stack_setup_preallocated
799 "#,
800 #[cfg(armv6_or_higher)]
801 r#"
802 // Clear Thumb Exception bit
803 mrc p15, 0, r0, c1, c0, 0
804 bic r0, #0x40000000
805 mcr p15, 0, r0, c1, c0, 0
806 "#,
807 #[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))]
808 r#"
809 // Allow VFP coprocessor access
810 mrc p15, 0, r0, c1, c0, 2
811 orr r0, r0, #0xF00000
812 mcr p15, 0, r0, c1, c0, 2
813 // Enable VFP
814 mov r0, #0x40000000
815 vmsr fpexc, r0
816 "#,
817 r#"
818 // Zero all registers before calling kmain (except r0)
819 mov r1, 0
820 mov r2, 0
821 mov r3, 0
822 mov r4, 0
823 mov r5, 0
824 mov r6, 0
825 mov r7, 0
826 mov r8, 0
827 mov r9, 0
828 mov r10, 0
829 mov r11, 0
830 // Check if this is the primary core
831 mov r0, r12
832 mov r12, 0
833 cmp r0, 0
834 bne 1f
835 // Jump to application with primary core
836 bl kmain
837 // In case the application returns, loop forever
838 b .
839 1:
840 // Jump to application with secondary core
841 bl kmain_secondary
842 // In case the application returns, loop forever
843 b .
844 .size _asm_core_start, . - _asm_core_start
845 .popsection
846 "#
847);
848
849/// Spins secondary cores.
850///
851/// This function is exported so the linker can use it as a default
852/// implementation of `kmain_secondary`, but it's considered an internal API
853/// that we don't expect you to call.
854#[unsafe(no_mangle)]
855#[cfg(target_arch = "arm")]
856pub extern "C" fn _default_kmain_secondary() {
857 #[cfg(armv7_or_higher)]
858 loop {
859 aarch32_cpu::asm::wfe();
860 }
861 #[cfg(not(armv7_or_higher))]
862 loop {
863 core::hint::spin_loop();
864 }
865}
866
867// # _asm_stack_setup_preallocated
868//
869// Configure a stack for every mode using linker provided constants.
870//
871// Leaves you in SYS mode at the end.
872//
873// Pass the core number in r0
874#[cfg(target_arch = "arm")]
875core::arch::global_asm!(
876 r#"
877 .pushsection .text._asm_stack_setup_preallocated
878 .arm
879 .global _asm_stack_setup_preallocated
880 .type _asm_stack_setup_preallocated, %function
881 .p2align 2
882 _asm_stack_setup_preallocated:
883 // Save LR from whatever mode we're currently in
884 mov r3, lr
885 // (we might not be in the same mode when we return).
886 // Set stack pointer and mask interrupts for UND mode (Mode 0x1B)
887 msr cpsr_c, {und_mode}
888 ldr r2, =_und_stack_high_end
889 ldr r1, =_und_stack_size
890 muls r1, r1, r0
891 subs sp, r2, r1
892 // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13)
893 msr cpsr_c, {svc_mode}
894 ldr r2, =_svc_stack_high_end
895 ldr r1, =_svc_stack_size
896 muls r1, r1, r0
897 subs sp, r2, r1
898 // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17)
899 msr cpsr_c, {abt_mode}
900 ldr r2, =_abt_stack_high_end
901 ldr r1, =_abt_stack_size
902 muls r1, r1, r0
903 subs sp, r2, r1
904 // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12)
905 msr cpsr_c, {irq_mode}
906 ldr r2, =_irq_stack_high_end
907 ldr r1, =_irq_stack_size
908 muls r1, r1, r0
909 subs sp, r2, r1
910 // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11)
911 msr cpsr_c, {fiq_mode}
912 ldr r2, =_fiq_stack_high_end
913 ldr r1, =_fiq_stack_size
914 muls r1, r1, r0
915 subs sp, r2, r1
916 // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F)
917 msr cpsr_c, {sys_mode}
918 ldr r2, =_sys_stack_high_end
919 ldr r1, =_sys_stack_size
920 muls r1, r1, r0
921 subs sp, r2, r1
922 // return to caller
923 bx r3
924 .size _asm_stack_setup_preallocated, . - _asm_stack_setup_preallocated
925 .popsection
926 "#,
927 und_mode = const {
928 Cpsr::new_with_raw_value(0)
929 .with_mode(ProcessorMode::Und)
930 .with_i(true)
931 .with_f(true)
932 .raw_value()
933 },
934 svc_mode = const {
935 Cpsr::new_with_raw_value(0)
936 .with_mode(ProcessorMode::Svc)
937 .with_i(true)
938 .with_f(true)
939 .raw_value()
940 },
941 abt_mode = const {
942 Cpsr::new_with_raw_value(0)
943 .with_mode(ProcessorMode::Abt)
944 .with_i(true)
945 .with_f(true)
946 .raw_value()
947 },
948 fiq_mode = const {
949 Cpsr::new_with_raw_value(0)
950 .with_mode(ProcessorMode::Fiq)
951 .with_i(true)
952 .with_f(true)
953 .raw_value()
954 },
955 irq_mode = const {
956 Cpsr::new_with_raw_value(0)
957 .with_mode(ProcessorMode::Irq)
958 .with_i(true)
959 .with_f(true)
960 .raw_value()
961 },
962 sys_mode = const {
963 Cpsr::new_with_raw_value(0)
964 .with_mode(ProcessorMode::Sys)
965 .with_i(true)
966 .with_f(true)
967 .raw_value()
968 },
969);
970
971// # _asm_init_segments
972//
973// Initialises stacks, .data and .bss
974#[cfg(target_arch = "arm")]
975core::arch::global_asm!(
976 r#"
977 // Work around https://github.com/rust-lang/rust/issues/127269
978 .fpu vfp2
979
980 .pushsection .text._asm_init_segments
981 .arm
982 .global _asm_init_segments
983 .type _asm_init_segments, %function
984 .p2align 2
985 _asm_init_segments:
986 // Zero .bss
987 ldr r0, =__sbss
988 ldr r1, =__ebss
989 mov r2, 0
990 0:
991 cmp r1, r0
992 beq 1f
993 stm r0!, {{r2}}
994 b 0b
995 1:
996 // Zero the stacks
997 ldr r0, =_stacks_low_end
998 ldr r1, =_stacks_high_end
999 mov r2, 0
1000 0:
1001 cmp r1, r0
1002 beq 1f
1003 stm r0!, {{r2}}
1004 b 0b
1005 1:
1006 // Initialise .data
1007 ldr r0, =__sdata
1008 ldr r1, =__edata
1009 ldr r2, =__sidata
1010 0:
1011 cmp r1, r0
1012 beq 1f
1013 ldm r2!, {{r3}}
1014 stm r0!, {{r3}}
1015 b 0b
1016 1:
1017 // return to caller
1018 bx lr
1019 .size _asm_init_segments, . - _asm_init_segments
1020 .popsection
1021 "#,
1022);
1023
1024// # _asm_default_fiq_handler
1025//
1026// Default asm FIQ exception handler (it's just a spin-loop)
1027//
1028// We end up here if a FIQ fires and the weak 'PROVIDE' in the link.x
1029// file hasn't been over-ridden.
1030//
1031// Cannot be a Rust/C function because it can only touch registers R8 to R12, SP and LR
1032//
1033// This function must produce A32 machine code, because it's called by the Vector Table
1034// with a raw PC load and the Vector Table is always in A32 machine code.
1035#[cfg(target_arch = "arm")]
1036core::arch::global_asm!(
1037 r#"
1038 .pushsection .text._asm_default_fiq_handler
1039 .arm
1040 .global _asm_default_fiq_handler
1041 .type _asm_default_fiq_handler, %function
1042 .p2align 2
1043 _asm_default_fiq_handler:
1044 b _asm_default_fiq_handler
1045 .size _asm_default_fiq_handler, . - _asm_default_fiq_handler
1046 .popsection
1047 "#,
1048);
1049
1050/// Our default exception handler.
1051///
1052/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
1053/// file hasn't been over-ridden.
1054///
1055/// The assembly trampolines allow this to be a normal `extern "C"` function,
1056/// because they save and restore the necessary state.
1057#[unsafe(no_mangle)]
1058pub extern "C" fn _default_handler() {
1059 loop {
1060 core::hint::spin_loop();
1061 }
1062}
1063
1064/// LLVM intrinsic for memory barriers
1065///
1066/// Only required on Armv4T and Armv5TE, because Armv6K onwards support atomics.
1067#[unsafe(no_mangle)]
1068#[cfg(armv5te_or_lower)]
1069pub extern "C" fn __sync_synchronize() {
1070 // we don't have a barrier instruction - the linux kernel just uses an empty inline asm block
1071 // so we do the same.
1072 unsafe {
1073 core::arch::asm!("");
1074 }
1075}
1076
1077// *****************************************************************************
1078// End of file
1079// *****************************************************************************