cortex_a_rt/lib.rs
1//! # Run-time support for Arm Cortex-A (AArch32)
2//!
3//! This library implements a simple Arm vector table, suitable for getting into
4//! a Rust application running in System Mode. It also provides a reference
5//! start up method. Most Cortex-A 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//! - `vfp-dp`: Enables support for the double-precision VFP floating point
20//! support. If your target CPU has this feature or support for NEON which
21//! also implies double-precision support, this feature should be activated.
22//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target.
23//!
24//! ## Information about the Run-Time
25//!
26//! Transferring from System Mode to User Mode (i.e. implementing an RTOS) is
27//! not handled here.
28//!
29//! If your processor starts in Hyp mode, this runtime will be transfer it to
30//! System mode. If you wish to write a hypervisor, you will need to replace
31//! this library with something more advanced.
32//!
33//! We assume that a set of symbols exist, either for constants or for C
34//! compatible functions or for naked raw-assembly functions. They are described
35//! in the next three sections.
36//!
37//! ## Constants
38//!
39//! * `_stack_top` - the address of the top of some region of RAM that we can
40//! use as stack space, with eight-byte alignment. Our linker script PROVIDEs
41//! a default pointing at the top of RAM.
42//! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
43//! aligned.
44//! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
45//! aligned.
46//! * `_fiq_stack_size` - the number of bytes to be reserved for stack space
47//! when in FIQ mode; must be a multiple of 8.
48//! * `_irq_stack_size` - the number of bytes to be reserved for stack space
49//! when in FIQ mode; must be a multiple of 8.
50//! * `_svc_stack_size` - the number of bytes to be reserved for stack space
51//! when in SVC mode; must be a multiple of 8.
52//! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
53//! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
54//! * `__sidata` - the start of the initialisation values for data, in read-only
55//! memory. Must be 4-byte aligned.
56//!
57//! Using our default start-up function `_default_start`, the memory between
58//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and
59//! `__edata` is initialised with the data found at `__sidata`.
60//!
61//! The stacks look like:
62//!
63//! ```text
64//! +------------------+ <----_stack_top
65//! | UND Stack | } _und_stack_size bytes
66//! +------------------+
67//! | SVC Stack | } _svc_stack_size bytes
68//! +------------------+
69//! | ABT Stack | } _abt_stack_size bytes
70//! +------------------+
71//! | IRQ Stack | } _irq_stack_size bytes
72//! +------------------+
73//! | FIQ Stack | } _fiq_stack_size bytes
74//! +------------------+
75//! | SYS Stack | } No specific size
76//! +------------------+
77//! ```
78//!
79//! ## C-Compatible Functions
80//!
81//! ### Main Function
82//!
83//! The symbol `kmain` should be an `extern "C"` function. It is called in SYS
84//! mode after all the global variables have been initialised. There is no
85//! default - this function is mandatory.
86//!
87//! ```rust
88//! #[unsafe(no_mangle)]
89//! extern "C" fn kmain() -> ! {
90//! loop { }
91//! }
92//! ```
93//!
94//! You can also create a 'kmain' function by using the `#[entry]` attribute on
95//! a normal Rust function.
96//!
97//! ```rust
98//! use cortex_a_rt::entry;
99//!
100//! #[entry]
101//! fn my_main() -> ! {
102//! loop { }
103//! }
104//! ```
105//!
106//! ### Undefined Handler
107//!
108//! The symbol `_undefined_handler` should be an `extern "C"` function. It is
109//! called in UND mode when an [Undefined Instruction Exception] occurs.
110//!
111//! [Undefined Instruction Exception]:
112//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Undefined-Instruction-exception?lang=en
113//!
114//! Our linker script PROVIDEs a default `_undefined_handler` symbol which is an
115//! alias for the `_default_handler` function. You can override it by defining
116//! your own `_undefined_handler` function, like:
117//!
118//! ```rust
119//! /// Does not return
120//! #[unsafe(no_mangle)]
121//! extern "C" fn _undefined_handler(addr: usize) -> ! {
122//! loop { }
123//! }
124//! ```
125//!
126//! or:
127//!
128//! ```rust
129//! /// Execution will continue from the returned address.
130//! ///
131//! /// Return `addr` to go back and execute the faulting instruction again.
132//! #[unsafe(no_mangle)]
133//! unsafe extern "C" fn _undefined_handler(addr: usize) -> usize {
134//! // do stuff here, then return to the address *after* the one
135//! // that failed
136//! addr + 4
137//! }
138//! ```
139//!
140//! You can create a `_undefined_handler` function by using the
141//! `#[exception(Undefined)]` attribute on a Rust function with the appropriate
142//! arguments and return type.
143//!
144//! ```rust
145//! use cortex_a_rt::exception;
146//!
147//! #[exception(Undefined)]
148//! fn my_handler(addr: usize) -> ! {
149//! loop { }
150//! }
151//! ```
152//!
153//! or:
154//!
155//! ```rust
156//! use cortex_a_rt::exception;
157//!
158//! #[exception(Undefined)]
159//! unsafe fn my_handler(addr: usize) -> usize {
160//! // do stuff here, then return the address to return to
161//! addr + 4
162//! }
163//! ```
164//!
165//! ### Supervisor Call Handler
166//!
167//! The symbol `_svc_handler` should be an `extern "C"` function. It is called
168//! in SVC mode when a [Supervisor Call Exception] occurs.
169//!
170//! [Supervisor Call Exception]:
171//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en
172//!
173//! Returning from this function will cause execution to resume at the function
174//! the triggered the exception, immediately after the SVC instruction. You
175//! cannot control where execution resumes. The function is passed the literal
176//! integer argument to the `svc` instruction, which is extracted from the
177//! machine code for you by the default assembly trampoline.
178//!
179//! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias
180//! for the `_default_handler` function. You can override it by defining your
181//! own `_svc_handler` function, like:
182//!
183//! ```rust
184//! #[unsafe(no_mangle)]
185//! extern "C" fn _svc_handler(svc: u32) {
186//! // do stuff here
187//! }
188//! ```
189//!
190//! You can also create a `_svc_handler` function by using the
191//! `#[exception(SupervisorCall)]` attribute on a normal Rust function.
192//!
193//! ```rust
194//! use cortex_a_rt::exception;
195//!
196//! #[exception(SupervisorCall)]
197//! fn my_svc_handler(arg: u32) {
198//! // do stuff here
199//! }
200//! ```
201//!
202//! ### Prefetch Abort Handler
203//!
204//! The symbol `_prefetch_abort_handler` should be an `extern "C"` function. It
205//! is called in ABT mode when a [Prefetch Abort Exception] occurs.
206//!
207//! [Prefetch Abort Exception]:
208//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Prefetch-Abort-exception?lang=en
209//!
210//! Our linker script PROVIDEs a default `_prefetch_abort_handler` symbol which
211//! is an alias for the `_default_handler` function. You can override it by
212//! defining your own `_undefined_handler` function.
213//!
214//! This function takes the address of faulting instruction, and can either not
215//! return:
216//!
217//! ```rust
218//! #[unsafe(no_mangle)]
219//! extern "C" fn _prefetch_abort_handler(addr: usize) -> ! {
220//! loop { }
221//! }
222//! ```
223//!
224//! Or it can return an address where execution should resume after the
225//! Exception handler is complete (which is unsafe):
226//!
227//! ```rust
228//! #[unsafe(no_mangle)]
229//! unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize {
230//! // do stuff, then go back to the instruction after the one that failed
231//! addr + 4
232//! }
233//! ```
234//!
235//! You can create a `_prefetch_abort_handler` function by using the
236//! `#[exception(PrefetchAbort)]` macro on a Rust function with the appropriate
237//! arguments and return type.
238//!
239//! ```rust
240//! use cortex_a_rt::exception;
241//!
242//! #[exception(PrefetchAbort)]
243//! fn my_handler(addr: usize) -> ! {
244//! loop { }
245//! }
246//! ```
247//!
248//! or:
249//!
250//! ```rust
251//! use cortex_a_rt::exception;
252//!
253//! #[exception(PrefetchAbort)]
254//! unsafe fn my_handler(addr: usize) -> usize {
255//! // do stuff, then go back to the instruction after the one that failed
256//! addr + 4
257//! }
258//! ```
259//!
260//! ### Data Abort Handler
261//!
262//! The symbol `_data_abort_handler` should be an `extern "C"` function. It is
263//! called in ABT mode when a [Data Abort Exception] occurs.
264//!
265//! [Data Abort Exception]:
266//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Data-Abort-exception?lang=en
267//!
268//! Our linker script PROVIDEs a default `_data_abort_handler` symbol which is
269//! an alias for the `_default_handler` function. You can override it by
270//! defining your own `_undefined_handler` function.
271//!
272//! This function takes the address of faulting instruction, and can either not
273//! return:
274//!
275//! ```rust
276//! #[unsafe(no_mangle)]
277//! extern "C" fn _data_abort_handler(addr: usize) -> ! {
278//! loop { }
279//! }
280//! ```
281//!
282//! Or it can return an address where execution should resume after the
283//! Exception handler is complete (which is unsafe):
284//!
285//! ```rust
286//! #[unsafe(no_mangle)]
287//! unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize {
288//! // do stuff, then go back to the instruction after the one that failed
289//! addr + 4
290//! }
291//! ```
292//!
293//! You can create a `_data_abort_handler` function by using the
294//! `#[exception(DataAbort)]` macro on a Rust function with the appropriate
295//! arguments and return type.
296//!
297//! ```rust
298//! use cortex_a_rt::exception;
299//!
300//! #[exception(DataAbort)]
301//! fn my_handler(addr: usize) -> ! {
302//! loop { }
303//! }
304//! ```
305//!
306//! or:
307//!
308//! ```rust
309//! use cortex_a_rt::exception;
310//!
311//! #[exception(DataAbort)]
312//! unsafe fn my_handler(addr: usize) -> usize {
313//! // do stuff, then go back to the instruction after the one that failed
314//! addr + 4
315//! }
316//! ```
317//!
318//! ### IRQ Handler
319//!
320//! The symbol `_irq_handler` should be an `extern "C"` function. It is called
321//! in SYS mode (not IRQ mode!) when an [Interrupt] occurs.
322//!
323//! [Interrupt]:
324//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/IRQ-exception?lang=en
325//!
326//! Returning from this function will cause execution to resume at wherever it
327//! was interrupted. You cannot control where execution resumes.
328//!
329//! This function is entered with interrupts masked, but you may unmask (i.e.
330//! enable) interrupts inside this function if desired. You will probably want
331//! to talk to your interrupt controller first, otherwise you'll just keep
332//! re-entering this interrupt handler recursively until you stack overflow.
333//!
334//! Our linker script PROVIDEs a default `_irq_handler` symbol which is an alias
335//! for `_default_handler`. You can override it by defining your own
336//! `_irq_handler` function.
337//!
338//! Expected prototype:
339//!
340//! ```rust
341//! #[unsafe(no_mangle)]
342//! extern "C" fn _irq_handler() {
343//! // 1. Talk to interrupt controller
344//! // 2. Handle interrupt
345//! // 3. Clear interrupt
346//! }
347//! ```
348//!
349//! You can also create a `_irq_handler` function by using the `#[irq]`
350//! attribute on a normal Rust function.
351//!
352//! ```rust
353//! use cortex_a_rt::irq;
354//!
355//! #[irq]
356//! fn my_irq_handler() {
357//! // 1. Talk to interrupt controller
358//! // 2. Handle interrupt
359//! // 3. Clear interrupt
360//! }
361//! ```
362//!
363//! ## ASM functions
364//!
365//! These are the naked 'raw' assembly functions the run-time requires:
366//!
367//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function
368//! at `_default_start` but you can override it. The provided default start
369//! function will initialise all global variables and then call `kmain` in SYS
370//! mode. Some SoCs require a chip specific startup for tasks like MMU
371//! initialization or chip specific initialization routines, so if our
372//! start-up routine doesn't work for you, supply your own `_start` function
373//! (but feel free to call our `_default_start` as part of it).
374//!
375//! * `_asm_undefined_handler` - a naked function to call when an Undefined
376//! Exception occurs. Our linker script PROVIDEs a default function at
377//! `_asm_default_undefined_handler` but you can override it. The provided
378//! default handler will call `_undefined_handler` in UND mode, saving state
379//! as required.
380//!
381//! * `_asm_svc_handler` - a naked function to call when an Supervisor Call
382//! (SVC) Exception occurs. Our linker script PROVIDEs a default function at
383//! `_asm_default_svc_handler` but you can override it. The provided default
384//! handler will call `_svc_handler` in SVC mode, saving state as required.
385//!
386//! * `_asm_prefetch_abort_handler` - a naked function to call when a Prefetch
387//! Abort Exception occurs. Our linker script PROVIDEs a default function at
388//! `_asm_default_prefetch_abort_handler` but you can override it. The
389//! provided default handler will call `_prefetch_abort_handler`, saving state
390//! as required. Note that Prefetch Abort Exceptions are handled in Abort Mode
391//! (ABT), Monitor Mode (MON) or Hyp Mode (HYP), depending on CPU
392//! configuration.
393//!
394//! * `_asm_data_abort_handler` - a naked function to call when a Data Abort
395//! Exception occurs. Our linker script PROVIDEs a default function at
396//! `_asm_default_data_abort_handler` but you can override it. The provided
397//! default handler will call `_data_abort_handler` in ABT mode, saving state
398//! as required.
399//!
400//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception
401//! occurs. Our linker script PROVIDEs a default function at
402//! `_asm_default_irq_handler` but you can override it. The provided default
403//! handler will call `_irq_handler` in SYS mode (not IRQ mode), saving state
404//! as required.
405//!
406//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt
407//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at
408//! `_asm_default_fiq_handler` but you can override it. The provided default
409//! just spins forever.
410//!
411//! ## Outputs
412//!
413//! This library produces global symbols called:
414//!
415//! * `_vector_table` - the start of the interrupt vector table
416//! * `_default_start` - the default Reset handler, that sets up some stacks and
417//! calls an `extern "C"` function called `kmain`.
418//! * `_asm_default_undefined_handler` - assembly language trampoline that calls
419//! `_undefined_handler`
420//! * `_asm_default_svc_handler` - assembly language trampoline that calls
421//! `_svc_handler`
422//! * `_asm_default_prefetch_abort_handler` - assembly language trampoline that
423//! calls `_prefetch_abort_handler`
424//! * `_asm_default_data_abort_handler` - assembly language trampoline that
425//! calls `_data_abort_handler`
426//! * `_asm_default_irq_handler` - assembly language trampoline that calls
427//! `_irq_handler`
428//! * `_asm_default_fiq_handler` - an FIQ handler that just spins
429//! * `_default_handler` - a C compatible function that spins forever.
430//!
431//! The assembly language trampolines are required because Armv7-A processors do
432//! not save a great deal of state on entry to an exception handler, unlike
433//! Armv7-M (and other M-Profile) processors. We must therefore save this state
434//! to the stack using assembly language, before transferring to an `extern "C"`
435//! function. We do not change modes before entering that `extern "C"` function
436//! \- that's for the handler to deal with as it wishes. Because FIQ is often
437//! performance-sensitive, we don't supply an FIQ trampoline; if you want to use
438//! FIQ, you have to write your own assembly routine, allowing you to preserve
439//! only whatever state is important to you.
440//!
441//! ## Examples
442//!
443//! You can find example code using QEMU inside the [project
444//! repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples)
445
446#![no_std]
447
448#[cfg(target_arch = "arm")]
449use cortex_ar::register::{cpsr::ProcessorMode, Cpsr};
450
451pub use cortex_ar_rt_macros::{entry, exception, irq};
452
453/// Our default exception handler.
454///
455/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
456/// file hasn't been over-ridden.
457#[no_mangle]
458pub extern "C" fn _default_handler() {
459 loop {
460 core::hint::spin_loop();
461 }
462}
463
464// The Interrupt Vector Table, and some default assembly-language handler.
465#[cfg(target_arch = "arm")]
466core::arch::global_asm!(
467 r#"
468 .section .vector_table,"ax",%progbits
469 .global _vector_table
470 .type _vector_table, %function
471 _vector_table:
472 ldr pc, =_start
473 ldr pc, =_asm_undefined_handler
474 ldr pc, =_asm_svc_handler
475 ldr pc, =_asm_prefetch_abort_handler
476 ldr pc, =_asm_data_abort_handler
477 nop
478 ldr pc, =_asm_irq_handler
479 ldr pc, =_asm_fiq_handler
480 .size _vector_table, . - _vector_table
481 "#
482);
483
484/// This macro expands to code for saving context on entry to an exception
485/// handler.
486///
487/// It should match `restore_context!`.
488///
489/// On entry to this block, we assume that we are in exception context.
490#[cfg(all(
491 target_arch = "arm",
492 not(any(target_abi = "eabihf", feature = "eabi-fpu"))
493))]
494macro_rules! save_context {
495 () => {
496 r#"
497 // save preserved registers (and gives us some working area)
498 push {{r0-r3}}
499 // align SP down to eight byte boundary
500 mov r0, sp
501 and r0, r0, 7
502 sub sp, r0
503 // push alignment amount, and final preserved register
504 push {{r0, r12}}
505 "#
506 };
507}
508
509/// This macro expands to code for restoring context on exit from an exception
510/// handler.
511///
512/// It should match `save_context!`.
513#[cfg(all(
514 target_arch = "arm",
515 not(any(target_abi = "eabihf", feature = "eabi-fpu"))
516))]
517macro_rules! restore_context {
518 () => {
519 r#"
520 // restore alignment amount, and preserved register
521 pop {{r0, r12}}
522 // restore pre-alignment SP
523 add sp, r0
524 // restore more preserved registers
525 pop {{r0-r3}}
526 "#
527 };
528}
529
530/// This macro expands to code for saving context on entry to an exception
531/// handler.
532///
533/// It should match `restore_context!`.
534#[cfg(all(
535 target_arch = "arm",
536 any(target_abi = "eabihf", feature = "eabi-fpu"),
537 not(feature = "vfp-dp")
538))]
539macro_rules! save_context {
540 () => {
541 r#"
542 // save preserved registers (and gives us some working area)
543 push {{r0-r3}}
544 // save FPU context
545 vpush {{d0-d7}}
546 vmrs r0, FPSCR
547 vmrs r1, FPEXC
548 push {{r0-r1}}
549 // align SP down to eight byte boundary
550 mov r0, sp
551 and r0, r0, 7
552 sub sp, r0
553 // push alignment amount, and final preserved register
554 push {{r0, r12}}
555 "#
556 };
557}
558
559/// This macro expands to code for restoring context on exit from an exception
560/// handler.
561///
562/// It should match `save_context!`.
563#[cfg(all(
564 target_arch = "arm",
565 any(target_abi = "eabihf", feature = "eabi-fpu"),
566 not(feature = "vfp-dp")
567))]
568macro_rules! restore_context {
569 () => {
570 r#"
571 // restore alignment amount, and preserved register
572 pop {{r0, r12}}
573 // restore pre-alignment SP
574 add sp, r0
575 // pop FPU state
576 pop {{r0-r1}}
577 vmsr FPEXC, r1
578 vmsr FPSCR, r0
579 vpop {{d0-d7}}
580 // restore more preserved registers
581 pop {{r0-r3}}
582 "#
583 };
584}
585
586/// This macro expands to code for saving context on entry to an exception
587/// handler.
588///
589/// It should match `restore_context!`.
590#[cfg(all(
591 target_arch = "arm",
592 any(target_abi = "eabihf", feature = "eabi-fpu"),
593 feature = "vfp-dp"
594))]
595macro_rules! save_context {
596 () => {
597 r#"
598 // save preserved registers (and gives us some working area)
599 push {{r0-r3}}
600 // save FPU context
601 vpush {{d0-d7}}
602 vpush {{d16-d31}}
603 vmrs r0, FPSCR
604 vmrs r1, FPEXC
605 push {{r0-r1}}
606 // align SP down to eight byte boundary
607 mov r0, sp
608 and r0, r0, 7
609 sub sp, r0
610 // push alignment amount, and final preserved register
611 push {{r0, r12}}
612 "#
613 };
614}
615
616/// This macro expands to code for restoring context on exit from an exception
617/// handler.
618///
619/// It should match `save_context!`.
620#[cfg(all(
621 target_arch = "arm",
622 any(target_abi = "eabihf", feature = "eabi-fpu"),
623 feature = "vfp-dp"
624))]
625macro_rules! restore_context {
626 () => {
627 r#"
628 // restore alignment amount, and preserved register
629 pop {{r0, r12}}
630 // restore pre-alignment SP
631 add sp, r0
632 // pop FPU state
633 pop {{r0-r1}}
634 vmsr FPEXC, r1
635 vmsr FPSCR, r0
636 vpop {{d16-d31}}
637 vpop {{d0-d7}}
638 // restore more preserved registers
639 pop {{r0-r3}}
640 "#
641 };
642}
643
644// Our assembly language exception handlers
645#[cfg(target_arch = "arm")]
646core::arch::global_asm!(
647 r#"
648
649 // Called from the vector table when we have an undefined exception.
650 // Saves state and calls a C-compatible handler like
651 // `extern "C" fn _undefined_handler(addr: usize) -> usize;`
652 // or
653 // `extern "C" fn _undefined_handler(addr: usize) -> !;`
654 .section .text._asm_default_undefined_handler
655 .global _asm_default_undefined_handler
656 .type _asm_default_undefined_handler, %function
657 _asm_default_undefined_handler:
658 // state save from compiled code
659 srsfd sp!, #{und_mode}
660 // to work out what mode we're in, we need R0
661 push {{r0}}
662 // First adjust LR for two purposes: Passing the faulting instruction to the C handler,
663 // and to return to the failing instruction after the C handler returns.
664 // Load processor status for the calling code
665 mrs r0, spsr
666 // Was the code that triggered the exception in Thumb state?
667 tst r0, {t_bit}
668 // Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
669 ite eq
670 subeq lr, lr, #4
671 subne lr, lr, #2
672 // now do our standard exception save (which saves the 'wrong' R0)
673 "#,
674 save_context!(),
675 r#"
676 // Pass the faulting instruction address to the handler.
677 mov r0, lr
678 // call C handler
679 bl _undefined_handler
680 // if we get back here, assume they returned a new LR in r0
681 mov lr, r0
682 // do our standard restore (with the 'wrong' R0)
683 "#,
684 restore_context!(),
685 r#"
686 // get the R0 we saved early
687 pop {{r0}}
688 // overwrite the saved LR with the one from the C handler
689 str lr, [sp]
690 // Return from the asm handler
691 rfefd sp!
692 .size _asm_default_undefined_handler, . - _asm_default_undefined_handler
693
694
695 .section .text._asm_default_svc_handler
696
697 // Called from the vector table when we have an software interrupt.
698 // Saves state and calls a C-compatible handler like
699 // `extern "C" fn _svc_handler(svc: u32);`
700 .global _asm_default_svc_handler
701 .type _asm_default_svc_handler, %function
702 _asm_default_svc_handler:
703 srsfd sp!, #{svc_mode}
704 "#,
705 save_context!(),
706 r#"
707 mrs r0, cpsr // Load processor status
708 tst r0, {t_bit} // Occurred in Thumb state?
709 ldrhne r0, [lr,#-2] // Yes: Load halfword and...
710 bicne r0, r0, #0xFF00 // ...extract comment field
711 ldreq r0, [lr,#-4] // No: Load word and...
712 biceq r0, r0, #0xFF000000 // ...extract comment field
713 // r0 now contains SVC number
714 bl _svc_handler
715 "#,
716 restore_context!(),
717 r#"
718 rfefd sp!
719 .size _asm_default_svc_handler, . - _asm_default_svc_handler
720
721
722 .section .text._asm_default_data_abort_handler
723
724 // Called from the vector table when we have an undefined exception.
725 // Saves state and calls a C-compatible handler like
726 // `extern "C" fn _data_abort_handler(addr: usize);`
727 .global _asm_default_data_abort_handler
728 .type _asm_default_data_abort_handler, %function
729 _asm_default_data_abort_handler:
730 // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
731 subs lr, lr, #8
732 // state save from compiled code
733 srsfd sp!, #{abt_mode}
734 "#,
735 save_context!(),
736 r#"
737 // Pass the faulting instruction address to the handler.
738 mov r0, lr
739 // call C handler
740 bl _data_abort_handler
741 // if we get back here, assume they returned a new LR in r0
742 mov lr, r0
743 "#,
744 restore_context!(),
745 r#"
746 // overwrite the saved LR with the one from the C handler
747 str lr, [sp]
748 // Return from the asm handler
749 rfefd sp!
750 .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler
751
752
753 .section .text._asm_default_prefetch_abort_handler
754
755 // Called from the vector table when we have a prefetch abort.
756 // Saves state and calls a C-compatible handler like
757 // `extern "C" fn _prefetch_abort_handler(addr: usize);`
758 .global _asm_default_prefetch_abort_handler
759 .type _asm_default_prefetch_abort_handler, %function
760 _asm_default_prefetch_abort_handler:
761 // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
762 subs lr, lr, #4
763 // state save from compiled code
764 srsfd sp!, #{abt_mode}
765 "#,
766 save_context!(),
767 r#"
768 // Pass the faulting instruction address to the handler.
769 mov r0, lr
770 // call C handler
771 bl _prefetch_abort_handler
772 // if we get back here, assume they returned a new LR in r0
773 mov lr, r0
774 "#,
775 restore_context!(),
776 r#"
777 // overwrite the saved LR with the one from the C handler
778 str lr, [sp]
779 // Return from the asm handler
780 rfefd sp!
781 .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler
782
783
784 .section .text._asm_default_irq_handler
785
786 // Called from the vector table when we have an interrupt.
787 // Saves state and calls a C-compatible handler like
788 // `extern "C" fn _irq_handler();`
789 .global _asm_default_irq_handler
790 .type _asm_default_irq_handler, %function
791 _asm_default_irq_handler:
792 // make sure we jump back to the right place
793 sub lr, lr, 4
794 // The hardware has copied CPSR to SPSR_irq and LR to LR_irq for us.
795 // Now push SPSR_irq and LR_irq to the SYS stack.
796 srsfd sp!, #{sys_mode}
797 // switch to system mode
798 cps #{sys_mode}
799 // we also need to save LR, so we can be re-entrant
800 push {{lr}}
801 // save state to the system stack (adjusting SP for alignment)
802 "#,
803 save_context!(),
804 r#"
805 // call C handler
806 bl _irq_handler
807 // restore from the system stack
808 "#,
809 restore_context!(),
810 r#"
811 // restore LR
812 pop {{lr}}
813 // pop CPSR and LR from the stack (which also restores the mode)
814 rfefd sp!
815 .size _asm_default_irq_handler, . - _asm_default_irq_handler
816
817
818 .section .text._asm_default_fiq_handler
819
820 // Our default FIQ handler
821 .global _asm_default_fiq_handler
822 .type _asm_default_fiq_handler, %function
823 _asm_default_fiq_handler:
824 b _asm_default_fiq_handler
825 .size _asm_default_fiq_handler, . - _asm_default_fiq_handler
826 "#,
827 svc_mode = const ProcessorMode::Svc as u8,
828 und_mode = const ProcessorMode::Und as u8,
829 abt_mode = const ProcessorMode::Abt as u8,
830 sys_mode = const ProcessorMode::Sys as u8,
831 t_bit = const {
832 Cpsr::new_with_raw_value(0)
833 .with_t(true)
834 .raw_value()
835 },
836);
837
838/// This macro expands to code to turn on the FPU
839#[cfg(all(target_arch = "arm", any(target_abi = "eabihf", feature = "eabi-fpu")))]
840macro_rules! fpu_enable {
841 () => {
842 r#"
843 // Allow VFP coprocessor access
844 mrc p15, 0, r0, c1, c0, 2
845 orr r0, r0, #0xF00000
846 mcr p15, 0, r0, c1, c0, 2
847 // Enable VFP
848 mov r0, #0x40000000
849 vmsr fpexc, r0
850 "#
851 };
852}
853
854/// This macro expands to code that does nothing because there is no FPU
855#[cfg(all(
856 target_arch = "arm",
857 not(any(target_abi = "eabihf", feature = "eabi-fpu"))
858))]
859macro_rules! fpu_enable {
860 () => {
861 r#"
862 // no FPU - do nothing
863 "#
864 };
865}
866
867// Default start-up code for Armv7-A
868//
869// We set up our stacks and `kmain` in system mode.
870#[cfg(target_arch = "arm")]
871core::arch::global_asm!(
872 r#"
873 .section .text.default_start
874 .align 0
875
876 .global _default_start
877 .type _default_start, %function
878 _default_start:
879 // Set up stacks.
880 ldr r0, =_stack_top
881 // Set stack pointer (right after) and mask interrupts for for UND mode (Mode 0x1B)
882 msr cpsr, {und_mode}
883 mov sp, r0
884 ldr r1, =_und_stack_size
885 sub r0, r0, r1
886 // Set stack pointer (right after) and mask interrupts for for SVC mode (Mode 0x13)
887 msr cpsr, {svc_mode}
888 mov sp, r0
889 ldr r1, =_svc_stack_size
890 sub r0, r0, r1
891 // Set stack pointer (right after) and mask interrupts for for ABT mode (Mode 0x17)
892 msr cpsr, {abt_mode}
893 mov sp, r0
894 ldr r1, =_abt_stack_size
895 sub r0, r0, r1
896 // Set stack pointer (right after) and mask interrupts for for IRQ mode (Mode 0x12)
897 msr cpsr, {irq_mode}
898 mov sp, r0
899 ldr r1, =_irq_stack_size
900 sub r0, r0, r1
901 // Set stack pointer (right after) and mask interrupts for for FIQ mode (Mode 0x11)
902 msr cpsr, {fiq_mode}
903 mov sp, r0
904 ldr r1, =_fiq_stack_size
905 sub r0, r0, r1
906 // Set stack pointer (right after) and mask interrupts for for System mode (Mode 0x1F)
907 msr cpsr, {sys_mode}
908 mov sp, r0
909 // Clear the Thumb Exception bit because we're in Arm mode
910 mrc p15, 0, r0, c1, c0, 0
911 bic r0, #{te_bit}
912 mcr p15, 0, r0, c1, c0, 0
913 "#,
914 fpu_enable!(),
915 r#"
916 // Initialise .bss
917 ldr r0, =__sbss
918 ldr r1, =__ebss
919 mov r2, 0
920 0:
921 cmp r1, r0
922 beq 1f
923 stm r0!, {{r2}}
924 b 0b
925 1:
926 // Initialise .data
927 ldr r0, =__sdata
928 ldr r1, =__edata
929 ldr r2, =__sidata
930 0:
931 cmp r1, r0
932 beq 1f
933 ldm r2!, {{r3}}
934 stm r0!, {{r3}}
935 b 0b
936 1:
937 // Zero all registers before calling kmain
938 mov r0, 0
939 mov r1, 0
940 mov r2, 0
941 mov r3, 0
942 mov r4, 0
943 mov r5, 0
944 mov r6, 0
945 mov r7, 0
946 mov r8, 0
947 mov r9, 0
948 mov r10, 0
949 mov r11, 0
950 mov r12, 0
951 // Jump to application
952 bl kmain
953 // In case the application returns, loop forever
954 b .
955 .size _default_start, . - _default_start
956 "#,
957 und_mode = const {
958 Cpsr::new_with_raw_value(0)
959 .with_mode(ProcessorMode::Und)
960 .with_i(true)
961 .with_f(true)
962 .raw_value()
963 },
964 svc_mode = const {
965 Cpsr::new_with_raw_value(0)
966 .with_mode(ProcessorMode::Svc)
967 .with_i(true)
968 .with_f(true)
969 .raw_value()
970 },
971 abt_mode = const {
972 Cpsr::new_with_raw_value(0)
973 .with_mode(ProcessorMode::Abt)
974 .with_i(true)
975 .with_f(true)
976 .raw_value()
977 },
978 fiq_mode = const {
979 Cpsr::new_with_raw_value(0)
980 .with_mode(ProcessorMode::Fiq)
981 .with_i(true)
982 .with_f(true)
983 .raw_value()
984 },
985 irq_mode = const {
986 Cpsr::new_with_raw_value(0)
987 .with_mode(ProcessorMode::Irq)
988 .with_i(true)
989 .with_f(true)
990 .raw_value()
991 },
992 sys_mode = const {
993 Cpsr::new_with_raw_value(0)
994 .with_mode(ProcessorMode::Sys)
995 .with_i(true)
996 .with_f(true)
997 .raw_value()
998 },
999 te_bit = const {
1000 cortex_ar::register::Sctlr::new_with_raw_value(0)
1001 .with_te(true)
1002 .raw_value()
1003 }
1004);