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_default_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.
@@ -319,6 +397,7 @@ core::arch::global_asm!(
319397 rfefd sp!
320398 .size _asm_svc_handler, . - _asm_svc_handler
321399
400+
322401 // Called from the vector table when we have an interrupt.
323402 // Saves state and calls a C-compatible handler like
324403 // `extern "C" fn irq_handler();`
@@ -337,9 +416,98 @@ core::arch::global_asm!(
337416 r#"
338417 rfefd sp!
339418 .size _asm_irq_handler, . - _asm_irq_handler
419+
420+
421+ // Called from the vector table when we have an undefined exception.
422+ // Saves state and calls a C-compatible handler like
423+ // `extern "C" fn _undefined_handler();`
424+ .global _asm_default_undefined_handler
425+ .type _asm_default_undefined_handler, %function
426+ _asm_default_undefined_handler:
427+ // First adjust LR for two purposes: Passing the faulting instruction to the C handler,
428+ // and to return to the failing instruction after the C handler returns.
429+ // Load processor status
430+ mrs r4, cpsr
431+ // Occurred in Thumb state?
432+ tst r4, {t_bit}
433+ // If not in Thumb mode, branch to not_thumb
434+ beq not_thumb
435+ subs lr, lr, #2
436+ b done
437+ not_thumb:
438+ // Subtract 4 from LR (ARM mode)
439+ subs lr, lr, #4
440+ done:
441+ // state save from compiled code
442+ srsfd sp!, {und_mode}
443+ "# ,
444+ save_context!( ) ,
445+ r#"
446+ // Pass the faulting instruction address to the handler.
447+ mov r0, lr
448+ // call C handler
449+ bl _undefined_handler
450+ "# ,
451+ restore_context!( ) ,
452+ r#"
453+ // Return to the failing instruction which is the recommended approach by ARM.
454+ rfefd sp!
455+ .size _asm_default_undefined_handler, . - _asm_default_undefined_handler
456+
457+
458+ // Called from the vector table when we have an undefined exception.
459+ // Saves state and calls a C-compatible handler like
460+ // `extern "C" fn _prefetch_handler();`
461+ .global _asm_default_prefetch_handler
462+ .type _asm_default_prefetch_handler, %function
463+ _asm_default_prefetch_handler:
464+ // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
465+ subs lr, lr, #4
466+ // state save from compiled code
467+ srsfd sp!, {abt_mode}
468+ "# ,
469+ save_context!( ) ,
470+ r#"
471+ // Pass the faulting instruction address to the handler.
472+ mov r0, lr
473+ // call C handler
474+ bl _prefetch_handler
475+ "# ,
476+ restore_context!( ) ,
477+ r#"
478+ // Return to the failing instruction which is the recommended approach by ARM.
479+ rfefd sp!
480+ .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler
481+
482+
483+ // Called from the vector table when we have an undefined exception.
484+ // Saves state and calls a C-compatible handler like
485+ // `extern "C" fn _abort_handler();`
486+ .global _asm_default_abort_handler
487+ .type _asm_default_abort_handler, %function
488+ _asm_default_abort_handler:
489+ // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
490+ subs lr, lr, #8
491+ // state save from compiled code
492+ srsfd sp!, {abt_mode}
493+ "# ,
494+ save_context!( ) ,
495+ r#"
496+ // Pass the faulting instruction address to the handler.
497+ mov r0, lr
498+ // call C handler
499+ bl _abort_handler
500+ "# ,
501+ restore_context!( ) ,
502+ r#"
503+ // Return to the failing instruction which is the recommended approach by ARM.
504+ rfefd sp!
505+ .size _asm_default_abort_handler, . - _asm_default_abort_handler
340506 "# ,
341507 svc_mode = const ProcessorMode :: Svc as u8 ,
342508 irq_mode = const ProcessorMode :: Irq as u8 ,
509+ und_mode = const ProcessorMode :: Und as u8 ,
510+ abt_mode = const ProcessorMode :: Abt as u8 ,
343511 t_bit = const {
344512 Cpsr :: new_with_raw_value( 0 )
345513 . with_t( true )
0 commit comments