1- //! Run-time support for Arm Cortex-A
1+ //! # Run-time support for Arm Cortex-A
22//!
33//! This library implements a simple Arm vector table, suitable for getting into
44//! a Rust application running in System Mode. It also provides a reference start up method.
2222//!
2323//! We assume the following global symbols exist:
2424//!
25- //! * `__start` - a Reset handler. Our linker script PROVIDEs a default function
26- //! at `_default_start` but you can override it. Most Cortex-A SoCs require
27- //! a chip specific startup for tasks like MMU initialization or chip specific
28- //! initialization routines.
25+ //! ### Constants
26+ //!
2927//! * `_stack_top` - the address of the top of some region of RAM that we can
3028//! use as stack space, with eight-byte alignment. Our linker script PROVIDEs
3129//! a default pointing at the top of RAM.
30+ //! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
31+ //! aligned.
32+ //! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
33+ //! aligned.
3234//! * `_fiq_stack_size` - the number of bytes to be reserved for stack space
3335//! when in FIQ mode; must be a multiple of 8.
3436//! * `_irq_stack_size` - the number of bytes to be reserved for stack space
3537//! when in FIQ mode; must be a multiple of 8.
3638//! * `_svc_stack_size` - the number of bytes to be reserved for stack space
37- //! when in SVC mode; must be a multiple of 8.F
39+ //! when in SVC mode; must be a multiple of 8.
40+ //! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
41+ //! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
42+ //! * `__sidata` - the start of the initialisation values for data, in read-only
43+ //! memory. Must be 4-byte aligned.
44+ //!
45+ //! ### Functions
46+ //!
47+ //! * `boot_core` - the `extern "C"` entry point to your application. The CPU ID
48+ //! will be passed as the first argument to this function.
49+ //!
50+ //! Expected prototype:
51+ //!
52+ //! ```rust
53+ //! #[unsafe(no_mangle)]
54+ //! extern "C" fn boot_core(cpu_id: u32) -> !;
55+ //! ```
56+ //!
3857//! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception
3958//! occurs. Our linker script PROVIDEs a default function at
4059//! `_default_handler` but you can override it.
60+ //!
61+ //! Expected prototype:
62+ //!
63+ //! ```rust
64+ //! #[unsafe(no_mangle)]
65+ //! extern "C" fn _svc_handler(svc: u32);
66+ //! ```
67+ //!
4168//! * `_irq_handler` - an `extern "C"` function to call when an Interrupt
4269//! occurs. Our linker script PROVIDEs a default function at
4370//! `_default_handler` but you can override it.
71+ //!
72+ //! Expected prototype:
73+ //!
74+ //! ```rust
75+ //! #[unsafe(no_mangle)]
76+ //! extern "C" fn _irq_handler();
77+ //! ```
78+ //!
79+ //! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception
80+ //! occurs. Our linker script PROVIDEs a default function at
81+ //! `_default_handler` but you can override it. It will be called by the
82+ //! `_asm_default_undefined_handler` unless that function is overriden as well.
83+ //!
84+ //! Expected prototype:
85+ //!
86+ //! ```rust
87+ //! #[unsafe(no_mangle)]
88+ //! extern "C" fn _undefined_handler(faulting_instruction: u32);
89+ //! ```
90+ //!
91+ //! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception
92+ //! occurs. Our linker script PROVIDEs a default function at
93+ //! `_default_handler` but you can override it. It will be called by the
94+ //! `_asm_default_abort_handler` unless that function is overriden as well.
95+ //!
96+ //! Expected prototype:
97+ //!
98+ //! ```rust
99+ //! #[unsafe(no_mangle)]
100+ //! extern "C" fn _abort_handler(faulting_instruction: u32);
101+ //! ```
102+ //!
103+ //! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception
104+ //! occurs. Our linker script PROVIDEs a default function at
105+ //! `_default_handler` but you can override it. It will be called by the
106+ //! `_asm_default_prefetch_handler` unless that function is overriden as well.
107+ //!
108+ //! Expected prototype:
109+ //!
110+ //! ```rust
111+ //! #[unsafe(no_mangle)]
112+ //! extern "C" fn _prefetch_handler(faulting_instruction: u32);
113+ //! ```
114+ //!
115+ //! ### ASM functions
116+ //!
117+ //! * `__start` - a Reset handler. Our linker script PROVIDEs a default function
118+ //! at `_default_start` but you can override it. Most Cortex-A SoCs require
119+ //! a chip specific startup for tasks like MMU initialization or chip specific
120+ //! initialization routines.
44121//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt
45122//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at
46123//! `_asm_default_fiq_handler` but you can override it.
47124//! * `_asm_undefined_handler` - a naked function to call when an Undefined
48125//! Exception occurs. Our linker script PROVIDEs a default function at
49- //! `_asm_default_handler ` but you can override it.
126+ //! `_asm_defeault_undefined_handler ` but you can override it.
50127//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch
51128//! Exception occurs. Our linker script PROVIDEs a default function at
52- //! `_asm_default_handler` but you can override it.
129+ //! `_asm_default_prefetch_handler` but you can override it. The provided default
130+ //! handler will perform an exception return to the faulting address.
53131//! * `_asm_abort_handler` - a naked function to call when an Abort Exception
54132//! occurs. Our linker script PROVIDEs a default function at
55- //! `_asm_default_handler` but you can override it.
56- //! * `boot_core` - the `extern "C"` entry point to your application. The CPU ID
57- //! will be passed as the first argument to this fumction.
58- //! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
59- //! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
60- //! * `__sidata` - the start of the initialisation values for data, in read-only
61- //! memory. Must be 4-byte aligned.
62- //! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
63- //! aligned.
64- //! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
65- //! aligned.
133+ //! `_asm_default_abort_handler` but you can override it. The provided default handler
134+ //! will perform an exception return to the faulting address.
66135//!
67136//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the
68137//! memory between `__sdata` and `__edata` is initialised with the data found at
95164//! If our start-up routine doesn't work for you (e.g. if you have to initialise
96165//! your memory controller before you touch RAM), supply your own `_start`
97166//! function (but feel free to call our `_default_start` as part of it).
167+ //!
168+ //! ## Examples
169+ //!
170+ //! You can find example code using QEMU inside the
171+ //! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples)
98172
99173#![ no_std]
100174
101- use cortex_ar:: register:: { cpsr:: ProcessorMode , Cpsr } ;
175+ use cortex_ar:: {
176+ asm:: nop,
177+ register:: { cpsr:: ProcessorMode , Cpsr } ,
178+ } ;
102179
103180/// Our default exception handler.
104181///
105182/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
106183/// file hasn't been over-ridden.
107184#[ no_mangle]
108185pub extern "C" fn _default_handler ( ) {
109- semihosting:: eprintln!( "Unhandled exception!" ) ;
110- semihosting:: process:: abort ( ) ;
186+ loop {
187+ nop ( ) ;
188+ }
111189}
112190
113191// The Interrupt Vector Table, and some default assembly-language handler.
@@ -146,13 +224,12 @@ core::arch::global_asm!(
146224) ;
147225
148226/// This macro expands to code for saving context on entry to an exception
149- /// handler.
227+ /// handler, ignoring FPU registers .
150228///
151229/// It should match `restore_context!`.
152230///
153231/// On entry to this block, we assume that we are in exception context.
154- #[ cfg( not( any( target_abi = "eabihf" , feature = "eabi-fpu" ) ) ) ]
155- macro_rules! save_context {
232+ macro_rules! save_context_ignore_fpu {
156233 ( ) => {
157234 r#"
158235 // save preserved registers (and gives us some working area)
@@ -168,11 +245,10 @@ macro_rules! save_context {
168245}
169246
170247/// This macro expands to code for restoring context on exit from an exception
171- /// handler.
248+ /// handler, ignoring FPU registers .
172249///
173250/// It should match `save_context!`.
174- #[ cfg( not( any( target_abi = "eabihf" , feature = "eabi-fpu" ) ) ) ]
175- macro_rules! restore_context {
251+ macro_rules! restore_context_ignore_fpu {
176252 ( ) => {
177253 r#"
178254 // restore alignment amount, and preserved register
@@ -185,6 +261,30 @@ macro_rules! restore_context {
185261 } ;
186262}
187263
264+ /// This macro expands to code for saving context on entry to an exception
265+ /// handler.
266+ ///
267+ /// It should match `restore_context!`.
268+ ///
269+ /// On entry to this block, we assume that we are in exception context.
270+ #[ cfg( not( any( target_abi = "eabihf" , feature = "eabi-fpu" ) ) ) ]
271+ macro_rules! save_context {
272+ ( ) => {
273+ save_context_ignore_fpu!( )
274+ } ;
275+ }
276+
277+ /// This macro expands to code for restoring context on exit from an exception
278+ /// handler.
279+ ///
280+ /// It should match `save_context!`.
281+ #[ cfg( not( any( target_abi = "eabihf" , feature = "eabi-fpu" ) ) ) ]
282+ macro_rules! restore_context {
283+ ( ) => {
284+ restore_context_ignore_fpu!( )
285+ } ;
286+ }
287+
188288/// This macro expands to code for saving context on entry to an exception
189289/// handler.
190290///
@@ -319,6 +419,7 @@ core::arch::global_asm!(
319419 rfefd sp!
320420 .size _asm_svc_handler, . - _asm_svc_handler
321421
422+
322423 // Called from the vector table when we have an interrupt.
323424 // Saves state and calls a C-compatible handler like
324425 // `extern "C" fn irq_handler();`
@@ -337,9 +438,98 @@ core::arch::global_asm!(
337438 r#"
338439 rfefd sp!
339440 .size _asm_irq_handler, . - _asm_irq_handler
441+
442+
443+ // Called from the vector table when we have an undefined exception.
444+ // Saves state and calls a C-compatible handler like
445+ // `extern "C" fn _undefined_handler();`
446+ .global _asm_default_undefined_handler
447+ .type _asm_default_undefined_handler, %function
448+ _asm_default_undefined_handler:
449+ // First adjust LR for two purposes: Passing the faulting instruction to the C handler,
450+ // and to return to the failing instruction after the C handler returns.
451+ // Load processor status
452+ mrs r4, cpsr
453+ // Occurred in Thumb state?
454+ tst r4, {t_bit}
455+ // If not in Thumb mode, branch to not_thumb
456+ beq not_thumb
457+ subs lr, lr, #2
458+ b done
459+ not_thumb:
460+ // Subtract 4 from LR (ARM mode)
461+ subs lr, lr, #4
462+ done:
463+ // state save from compiled code
464+ srsfd sp!, {und_mode}
465+ "# ,
466+ save_context_ignore_fpu!( ) ,
467+ r#"
468+ // Pass the faulting instruction address to the handler.
469+ mov r0, lr
470+ // call C handler
471+ bl _undefined_handler
472+ "# ,
473+ restore_context_ignore_fpu!( ) ,
474+ r#"
475+ // Return to the failing instruction which is the recommended approach by ARM.
476+ rfefd sp!
477+ .size _asm_default_undefined_handler, . - _asm_default_undefined_handler
478+
479+
480+ // Called from the vector table when we have an undefined exception.
481+ // Saves state and calls a C-compatible handler like
482+ // `extern "C" fn _prefetch_handler();`
483+ .global _asm_default_prefetch_handler
484+ .type _asm_default_prefetch_handler, %function
485+ _asm_default_prefetch_handler:
486+ // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
487+ subs lr, lr, #4
488+ // state save from compiled code
489+ srsfd sp!, {abt_mode}
490+ "# ,
491+ save_context_ignore_fpu!( ) ,
492+ r#"
493+ // Pass the faulting instruction address to the handler.
494+ mov r0, lr
495+ // call C handler
496+ bl _prefetch_handler
497+ "# ,
498+ restore_context_ignore_fpu!( ) ,
499+ r#"
500+ // Return to the failing instruction which is the recommended approach by ARM.
501+ rfefd sp!
502+ .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler
503+
504+
505+ // Called from the vector table when we have an undefined exception.
506+ // Saves state and calls a C-compatible handler like
507+ // `extern "C" fn _abort_handler();`
508+ .global _asm_default_abort_handler
509+ .type _asm_default_abort_handler, %function
510+ _asm_default_abort_handler:
511+ // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
512+ subs lr, lr, #8
513+ // state save from compiled code
514+ srsfd sp!, {abt_mode}
515+ "# ,
516+ save_context_ignore_fpu!( ) ,
517+ r#"
518+ // Pass the faulting instruction address to the handler.
519+ mov r0, lr
520+ // call C handler
521+ bl _abort_handler
522+ "# ,
523+ restore_context_ignore_fpu!( ) ,
524+ r#"
525+ // Return to the failing instruction which is the recommended approach by ARM.
526+ rfefd sp!
527+ .size _asm_default_abort_handler, . - _asm_default_abort_handler
340528 "# ,
341529 svc_mode = const ProcessorMode :: Svc as u8 ,
342530 irq_mode = const ProcessorMode :: Irq as u8 ,
531+ und_mode = const ProcessorMode :: Und as u8 ,
532+ abt_mode = const ProcessorMode :: Abt as u8 ,
343533 t_bit = const {
344534 Cpsr :: new_with_raw_value( 0 )
345535 . with_t( true )
0 commit comments