diff --git a/cortex-a-rt/link.x b/cortex-a-rt/link.x index 0ba73fc..8ee325f 100644 --- a/cortex-a-rt/link.x +++ b/cortex-a-rt/link.x @@ -17,8 +17,6 @@ SECTIONS { .text : { /* The vector table must come first */ *(.vector_table) - /* Our exception handling routines */ - *(.text.handlers) /* Now the rest of the code */ *(.text .text*) } > CODE @@ -72,36 +70,38 @@ SECTIONS { } /* -We reserve some space at the top of the RAM for our stacks. We have an IRQ stack -and a FIQ stack, plus the remainder is our system stack. +We reserve some space at the top of the RAM for our exception stacks. The +remainder is our system mode stack. You must keep _stack_top and the stack sizes aligned to eight byte boundaries. */ PROVIDE(_stack_top = ORIGIN(DATA) + LENGTH(DATA)); -PROVIDE(_fiq_stack_size = 0x400); -PROVIDE(_irq_stack_size = 0x1000); -PROVIDE(_abt_stack_size = 0x400); PROVIDE(_und_stack_size = 0x400); -PROVIDE(_svc_stack_size = 0x1000); +PROVIDE(_svc_stack_size = 0x400); +PROVIDE(_abt_stack_size = 0x400); +PROVIDE(_irq_stack_size = 0x400); +PROVIDE(_fiq_stack_size = 0x400); -ASSERT(_stack_top % 8 == 0, "ERROR(cortex-a-rt): top of stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of IRQ stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of ABT stack is not 8-byte aligned"); -ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of UND stack is not 8-byte aligned"); -ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_stack_top % 8 == 0, "ERROR(cortex-r-rt): top of stack is not 8-byte aligned"); +ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned"); +ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); +ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); +ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); /* Weak aliases for ASM default handlers */ +PROVIDE(_start =_default_start); PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler); +PROVIDE(_asm_svc_handler =_asm_default_svc_handler); PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler); PROVIDE(_asm_abort_handler =_asm_default_abort_handler); +PROVIDE(_asm_irq_handler =_asm_default_irq_handler); PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler); /* Weak aliases for C default handlers */ PROVIDE(_undefined_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); +PROVIDE(_svc_handler =_default_handler); PROVIDE(_prefetch_handler =_default_handler); +PROVIDE(_abort_handler =_default_handler); PROVIDE(_irq_handler =_default_handler); -PROVIDE(_svc_handler =_default_handler); -PROVIDE(_start =_default_start); +/* There is no default C-language FIQ handler */ diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index ce8bae8..0d485a3 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -1,22 +1,25 @@ -//! # Run-time support for Arm Cortex-A +//! # Run-time support for Arm Cortex-R //! //! This library implements a simple Arm vector table, suitable for getting into -//! a Rust application running in System Mode. It also provides a reference start up method. -//! Usually, most Cortex-A based systems will require chip specific start-up code, so the -//! start-up method can over overriden. -//! -//! The default startup routine provided by this crate does not include any special handling -//! for multi-core support because this is oftentimes implementation defined and the exact -//! handling depends on the specific chip in use. Many implementations only -//! run the startup routine with one core and will keep other cores in reset until they are woken -//! up by an implementation specific mechanism. For other implementations where multi-core -//! specific startup adaptions are necessary, the startup routine can be overwritten by the user. +//! a Rust application running in System Mode. It also provides a reference +//! start up method. Most Cortex-A based systems will require chip specific +//! start-up code, so the start-up method can over overriden. +//! +//! The default startup routine provided by this crate does not include any +//! special handling for multi-core support because this is oftentimes +//! implementation defined and the exact handling depends on the specific chip +//! in use. Many implementations only run the startup routine with one core and +//! will keep other cores in reset until they are woken up by an implementation +//! specific mechanism. For other implementations where multi-core specific +//! startup adaptions are necessary, the startup routine can be overwritten by +//! the user. //! //! ## Features //! -//! - `vfp-dp`: Enables support for the double-precision VFP floating point support. If your target -//! CPU has this feature or support for NEON which also implies double-precision support, this -//! feature should be activated. +//! - `vfp-dp`: Enables support for the double-precision VFP floating point +//! support. If your target CPU has this feature or support for NEON which +//! also implies double-precision support, this feature should be activated. +//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target. //! //! ## Information about the Run-Time //! @@ -49,7 +52,11 @@ //! * `__sidata` - the start of the initialisation values for data, in read-only //! memory. Must be 4-byte aligned. //! -//! ### Functions +//! Using our default start-up function `_default_start`, the memory between +//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and +//! `__edata` is initialised with the data found at `__sidata`. +//! +//! ### C-Compatible Functions //! //! * `kmain` - the `extern "C"` entry point to your application. //! @@ -62,7 +69,9 @@ //! //! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception, immediately after the SVC instruction. //! //! Expected prototype: //! @@ -73,108 +82,155 @@ //! //! * `_irq_handler` - an `extern "C"` function to call when an Interrupt //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception. //! //! Expected prototype: //! //! ```rust +//! /// Upon return, the interrupt handler will end and execution +//! /// will continue at the interrupted instruction. //! #[unsafe(no_mangle)] //! extern "C" fn _irq_handler(); //! ``` //! -//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_undefined_handler` unless that function is overriden as well. +//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined +//! Exception occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_undefined_handler` is missing. //! -//! Expected prototype: +//! The expected prototype for `_undefined_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize); +//! extern "C" fn _undefined_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_abort_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_abort_handler` - an `extern "C"` function to call when an Data Abort +//! occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_abort_handler` is missing. +//! +//! The expected prototype for `_abort_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(addr: usize); +//! extern "C" fn _abort_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_prefetch_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _abort_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_prefetch_handler` - an `extern "C"` function to call when an Prefetch +//! Abort occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_prefetch_handler` is missing. +//! +//! The expected prototype for `_prefetch_handler` is either: +//! +//! ```rust +//! /// Does not return +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_handler(addr: usize) -> !; +//! ``` +//! +//! or: //! //! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(addr: usize); +//! extern "C" fn _prefetch_handler(addr: usize) -> usize; //! ``` //! //! ### ASM functions //! -//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function -//! at `_default_start` but you can override it. Most Cortex-A SoCs require -//! a chip specific startup for tasks like MMU initialization or chip specific -//! initialization routines. -//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt -//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_fiq_handler` but you can override it. +//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function +//! at `_default_start` but you can override it. Some SoCs require a chip +//! specific startup for tasks like MMU initialization or chip specific +//! initialization routines, so if our start-up routine doesn't work for you, +//! supply your own `_start` function (but feel free to call our +//! `_default_start` as part of it). //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_undefined_handler` but you can override it. +//! `_asm_default_undefined_handler` but you can override it. The provided +//! default handler will call `_undefined_handler`, saving state as required. +//! * `_asm_svc_handler` - a naked function to call when an SVC Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_svc_handler` but you can override it. The provided default +//! handler will call `_svc_handler`, saving state as required. //! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_prefetch_handler` but you can override it. The provided default -//! handler will perform an exception return to the faulting address. +//! `_asm_default_prefetch_handler` but you can override it. The provided +//! default handler will call `_prefetch_handler`, saving state as required. +//! Note that Prefetch Exceptions are handled in Abort Mode, Monitor Mode or +//! Hyp Mode, depending on CPU configuration. There is no Prefetch Abort mode, +//! so there is no Prefetch Abort Mode stack. //! * `_asm_abort_handler` - a naked function to call when an Abort Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_abort_handler` but you can override it. The provided default handler -//! will perform an exception return to the faulting address. +//! `_asm_default_abort_handler` but you can override it. The provided default +//! handler will call `_abort_handler`, saving state as required. +//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_irq_handler` but you can override it. The provided default +//! handler will call `_irq_handler`, saving state as required. +//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt +//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_fiq_handler` but you can override it. The provided default +//! just spins forever. //! -//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the -//! memory between `__sdata` and `__edata` is initialised with the data found at -//! `__sidata`. +//! ## Outputs //! //! This library produces global symbols called: //! //! * `_vector_table` - the start of the interrupt vector table //! * `_default_start` - the default Reset handler, that sets up some stacks and //! calls an `extern "C"` function called `kmain`. +//! * `_asm_default_undefined_handler` - assembly language trampoline that calls +//! `_undefined_handler` +//! * `_asm_default_svc_handler` - assembly language trampoline that calls +//! `_svc_handler` +//! * `_asm_default_prefetch_handler` - assembly language trampoline that calls +//! `_prefetch_handler` +//! * `_asm_default_abort_handler` - assembly language trampoline that calls +//! `_abort_handler` +//! * `_asm_default_irq_handler` - assembly language trampoline that calls +//! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins -//! * `_asm_default_handler` - an exception handler that just spins -//! * `_asm_svc_handler` - assembly language trampoline for SVC Exceptions that -//! calls `_svc_handler` -//! * `_asm_irq_handler` - assembly language trampoline for Interrupts that -//! calls `_irq_handler` -//! -//! The assembly language trampolines are required because Armv7-R (and Armv8-R) -//! processors do not save a great deal of state on entry to an exception -//! handler, unlike Armv7-M (and other M-Profile) processors. We must therefore -//! save this state to the stack using assembly language, before transferring to -//! an `extern "C"` function. We do not change modes before entering that -//! `extern "C"` function - that's for the handler to deal with as it wishes. We -//! supply a default handler that prints an error message to Semihosting so you -//! know if you hit an unexpected exception. Because FIQ is often +//! * `_default_handler` - a C compatible function that spins forever. +//! +//! The assembly language trampolines are required because Armv7-A processors do +//! not save a great deal of state on entry to an exception handler, unlike +//! Armv7-M (and other M-Profile) processors. We must therefore save this state +//! to the stack using assembly language, before transferring to an `extern "C"` +//! function. We do not change modes before entering that `extern "C"` function +//! - that's for the handler to deal with as it wishes. Because FIQ is often //! performance-sensitive, we don't supply an FIQ trampoline; if you want to use //! FIQ, you have to write your own assembly routine, allowing you to preserve //! only whatever state is important to you. //! -//! If our start-up routine doesn't work for you (e.g. if you have to initialise -//! your memory controller before you touch RAM), supply your own `_start` -//! function (but feel free to call our `_default_start` as part of it). -//! //! ## Examples //! -//! You can find example code using QEMU inside the -//! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) +//! You can find example code using QEMU inside the [project +//! repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) #![no_std] @@ -197,8 +253,7 @@ pub extern "C" fn _default_handler() { // The Interrupt Vector Table, and some default assembly-language handler. core::arch::global_asm!( r#" - .section .vector_table - .align 0 + .section .vector_table,"ax",%progbits .global _vector_table .type _vector_table, %function @@ -212,20 +267,6 @@ core::arch::global_asm!( ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler .size _vector_table, . - _vector_table - - .section .text.handlers - - .global _asm_default_fiq_handler - .type _asm_default_fiq_handler, %function - _asm_default_fiq_handler: - b _asm_default_fiq_handler - .size _asm_default_fiq_handler, . - _asm_default_fiq_handler - - .global _asm_default_handler - .type _asm_default_handler, %function - _asm_default_handler: - b _asm_default_handler - .size _asm_default_handler, . - _asm_default_handler "# ); @@ -376,57 +417,13 @@ macro_rules! restore_context { // Our assembly language exception handlers core::arch::global_asm!( r#" - .section .text.handlers - .align 0 - - // Called from the vector table when we have an software interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32);` - .global _asm_svc_handler - .type _asm_svc_handler, %function - _asm_svc_handler: - srsfd sp!, {svc_mode} - "#, - save_context!(), - r#" - mrs r0, cpsr // Load processor status - tst r0, {t_bit} // Occurred in Thumb state? - ldrhne r0, [lr,#-2] // Yes: Load halfword and... - bicne r0, r0, #0xFF00 // ...extract comment field - ldreq r0, [lr,#-4] // No: Load word and... - biceq r0, r0, #0xFF000000 // ...extract comment field - // r0 now contains SVC number - bl _svc_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_svc_handler, . - _asm_svc_handler - - - // Called from the vector table when we have an interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn irq_handler();` - .global _asm_irq_handler - .type _asm_irq_handler, %function - _asm_irq_handler: - sub lr, lr, 4 - srsfd sp!, {irq_mode} - "#, - save_context!(), - r#" - // call C handler - bl _irq_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_irq_handler, . - _asm_irq_handler - + .section .text._asm_default_undefined_handler // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler(addr: usize);` + // `extern "C" fn _undefined_handler(addr: usize) -> usize;` + // or + // `extern "C" fn _undefined_handler(addr: usize) -> !;` .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: @@ -454,6 +451,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _undefined_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 // do our standard restore "#, restore_context!(), @@ -468,6 +467,64 @@ core::arch::global_asm!( rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler + + .section .text._asm_default_svc_handler + + // Called from the vector table when we have an software interrupt. + // Saves state and calls a C-compatible handler like + // `extern "C" fn svc_handler(svc: u32);` + .global _asm_default_svc_handler + .type _asm_default_svc_handler, %function + _asm_default_svc_handler: + srsfd sp!, {svc_mode} + "#, + save_context!(), + r#" + mrs r0, cpsr // Load processor status + tst r0, {t_bit} // Occurred in Thumb state? + ldrhne r0, [lr,#-2] // Yes: Load halfword and... + bicne r0, r0, #0xFF00 // ...extract comment field + ldreq r0, [lr,#-4] // No: Load word and... + biceq r0, r0, #0xFF000000 // ...extract comment field + // r0 now contains SVC number + bl _svc_handler + "#, + restore_context!(), + r#" + rfefd sp! + .size _asm_default_svc_handler, . - _asm_default_svc_handler + + + .section .text._asm_default_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _abort_handler(addr: usize);` + .global _asm_default_abort_handler + .type _asm_default_abort_handler, %function + _asm_default_abort_handler: + // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. + subs lr, lr, #8 + // state save from compiled code + srsfd sp!, {abt_mode} + "#, + save_context!(), + r#" + // Pass the faulting instruction address to the handler. + mov r0, lr + // call C handler + bl _abort_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 + "#, + restore_context!(), + r#" + // Return to the failing instruction which is the recommended approach by ARM. + rfefd sp! + .size _asm_default_abort_handler, . - _asm_default_abort_handler + + + .section .text._asm_default_prefetch_handler // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like @@ -486,6 +543,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _prefetch_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 "#, restore_context!(), r#" @@ -494,29 +553,36 @@ core::arch::global_asm!( .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler - // Called from the vector table when we have an undefined exception. + .section .text._asm_default_irq_handler + + // Called from the vector table when we have an interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler(addr: usize);` - .global _asm_default_abort_handler - .type _asm_default_abort_handler, %function - _asm_default_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - srsfd sp!, {abt_mode} + // `extern "C" fn irq_handler();` + .global _asm_default_irq_handler + .type _asm_default_irq_handler, %function + _asm_default_irq_handler: + sub lr, lr, 4 + srsfd sp!, {irq_mode} "#, save_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr // call C handler - bl _abort_handler + bl _irq_handler "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_irq_handler, . - _asm_default_irq_handler + + + .section .text._asm_default_fiq_handler + + // Our default FIQ handler + .global _asm_default_fiq_handler + .type _asm_default_fiq_handler, %function + _asm_default_fiq_handler: + b _asm_default_fiq_handler + .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, irq_mode = const ProcessorMode::Irq as u8, @@ -560,7 +626,7 @@ macro_rules! fpu_enable { // We set up our stacks and `kmain` in system mode. core::arch::global_asm!( r#" - .section .text.startup + .section .text.default_start .align 0 .global _default_start @@ -600,7 +666,6 @@ core::arch::global_asm!( mrc p15, 0, r0, c1, c0, 0 bic r0, #{te_bit} mcr p15, 0, r0, c1, c0, 0 - "#, fpu_enable!(), r#" diff --git a/cortex-r-rt/link.x b/cortex-r-rt/link.x index a0296a2..309d1bf 100644 --- a/cortex-r-rt/link.x +++ b/cortex-r-rt/link.x @@ -17,8 +17,6 @@ SECTIONS { .text : { /* The vector table must come first */ *(.vector_table) - /* Our exception handling routines */ - *(.text.handlers) /* Now the rest of the code */ *(.text .text*) } > CODE @@ -72,36 +70,38 @@ SECTIONS { } /* -We reserve some space at the top of the RAM for our stacks. We have an IRQ stack -and a FIQ stack, plus the remainder is our system stack. +We reserve some space at the top of the RAM for our exception stacks. The +remainder is our system mode stack. You must keep _stack_top and the stack sizes aligned to eight byte boundaries. */ PROVIDE(_stack_top = ORIGIN(DATA) + LENGTH(DATA)); -PROVIDE(_fiq_stack_size = 0x400); -PROVIDE(_irq_stack_size = 0x1000); -PROVIDE(_abt_stack_size = 0x400); PROVIDE(_und_stack_size = 0x400); -PROVIDE(_svc_stack_size = 0x1000); +PROVIDE(_svc_stack_size = 0x400); +PROVIDE(_abt_stack_size = 0x400); +PROVIDE(_irq_stack_size = 0x400); +PROVIDE(_fiq_stack_size = 0x400); ASSERT(_stack_top % 8 == 0, "ERROR(cortex-r-rt): top of stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned"); ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); +ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); +ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); /* Weak aliases for ASM default handlers */ +PROVIDE(_start =_default_start); PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler); +PROVIDE(_asm_svc_handler =_asm_default_svc_handler); PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler); PROVIDE(_asm_abort_handler =_asm_default_abort_handler); +PROVIDE(_asm_irq_handler =_asm_default_irq_handler); PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler); /* Weak aliases for C default handlers */ PROVIDE(_undefined_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); +PROVIDE(_svc_handler =_default_handler); PROVIDE(_prefetch_handler =_default_handler); +PROVIDE(_abort_handler =_default_handler); PROVIDE(_irq_handler =_default_handler); -PROVIDE(_svc_handler =_default_handler); -PROVIDE(_start =_default_start); +/* There is no default C-language FIQ handler */ diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index f4f4a15..0220903 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -1,7 +1,24 @@ //! # Run-time support for Arm Cortex-R //! //! This library implements a simple Arm vector table, suitable for getting into -//! a Rust application running in System Mode. +//! a Rust application running in System Mode. It also provides a reference +//! start up method. Most Cortex-R based systems will require chip specific +//! start-up code, so the start-up method can over overriden. +//! +//! The default startup routine provided by this crate does not include any +//! special handling for multi-core support because this is oftentimes +//! implementation defined and the exact handling depends on the specific chip +//! in use. Many implementations only run the startup routine with one core and +//! will keep other cores in reset until they are woken up by an implementation +//! specific mechanism. For other implementations where multi-core specific +//! startup adaptions are necessary, the startup routine can be overwritten by +//! the user. +//! +//! ## Features +//! +//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target. +//! +//! ## Information about the Run-Time //! //! Transferring from System Mode to User Mode (i.e. implementing an RTOS) is //! not handled here. @@ -32,7 +49,11 @@ //! * `__sidata` - the start of the initialisation values for data, in read-only //! memory. Must be 4-byte aligned. //! -//! ### Functions +//! Using our default start-up function `_default_start`, the memory between +//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and +//! `__edata` is initialised with the data found at `__sidata`. +//! +//! ### C-Compatible Functions //! //! * `kmain` - the `extern "C"` entry point to your application. //! @@ -45,7 +66,9 @@ //! //! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception, immediately after the SVC instruction. //! //! Expected prototype: //! @@ -56,108 +79,155 @@ //! //! * `_irq_handler` - an `extern "C"` function to call when an Interrupt //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception. //! //! Expected prototype: //! //! ```rust +//! /// Upon return, the interrupt handler will end and execution +//! /// will continue at the interrupted instruction. //! #[unsafe(no_mangle)] //! extern "C" fn _irq_handler(); //! ``` //! -//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_undefined_handler` unless that function is overriden as well. +//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined +//! Exception occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_undefined_handler` is missing. //! -//! Expected prototype: +//! The expected prototype for `_undefined_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize); +//! extern "C" fn _undefined_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_abort_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_abort_handler` - an `extern "C"` function to call when an Data Abort +//! occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_abort_handler` is missing. +//! +//! The expected prototype for `_abort_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(addr: usize); +//! extern "C" fn _abort_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_prefetch_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _abort_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_prefetch_handler` - an `extern "C"` function to call when an Prefetch +//! Abort occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_prefetch_handler` is missing. +//! +//! The expected prototype for `_prefetch_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(addr: usize); +//! extern "C" fn _prefetch_handler(addr: usize) -> !; +//! ``` +//! +//! or: +//! +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_handler(addr: usize) -> usize; //! ``` //! //! ### ASM functions //! -//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function -//! at `_default_start` but you can override it. Most Cortex-A SoCs require -//! a chip specific startup for tasks like MMU initialization or chip specific -//! initialization routines. -//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt -//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_fiq_handler` but you can override it. +//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function +//! at `_default_start` but you can override it. Some SoCs require a chip +//! specific startup for tasks like MMU initialization or chip specific +//! initialization routines, so if our start-up routine doesn't work for you, +//! supply your own `_start` function (but feel free to call our +//! `_default_start` as part of it). //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_undefined_handler` but you can override it. +//! `_asm_default_undefined_handler` but you can override it. The provided +//! default handler will call `_undefined_handler`, saving state as required. +//! * `_asm_svc_handler` - a naked function to call when an SVC Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_svc_handler` but you can override it. The provided default +//! handler will call `_svc_handler`, saving state as required. //! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_prefetch_handler` but you can override it. The provided default -//! handler will perform an exception return to the faulting address. +//! `_asm_default_prefetch_handler` but you can override it. The provided +//! default handler will call `_prefetch_handler`, saving state as required. +//! Note that Prefetch Exceptions are handled in Abort Mode, Monitor Mode or +//! Hyp Mode, depending on CPU configuration. There is no Prefetch Abort mode, +//! so there is no Prefetch Abort Mode stack. //! * `_asm_abort_handler` - a naked function to call when an Abort Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_abort_handler` but you can override it. The provided default handler -//! will perform an exception return to the faulting address. +//! `_asm_default_abort_handler` but you can override it. The provided default +//! handler will call `_abort_handler`, saving state as required. +//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_irq_handler` but you can override it. The provided default +//! handler will call `_irq_handler`, saving state as required. +//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt +//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_fiq_handler` but you can override it. The provided default +//! just spins forever. //! -//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the -//! memory between `__sdata` and `__edata` is initialised with the data found at -//! `__sidata`. +//! ## Outputs //! //! This library produces global symbols called: //! //! * `_vector_table` - the start of the interrupt vector table //! * `_default_start` - the default Reset handler, that sets up some stacks and //! calls an `extern "C"` function called `kmain`. +//! * `_asm_default_undefined_handler` - assembly language trampoline that calls +//! `_undefined_handler` +//! * `_asm_default_svc_handler` - assembly language trampoline that calls +//! `_svc_handler` +//! * `_asm_default_prefetch_handler` - assembly language trampoline that calls +//! `_prefetch_handler` +//! * `_asm_default_abort_handler` - assembly language trampoline that calls +//! `_abort_handler` +//! * `_asm_default_irq_handler` - assembly language trampoline that calls +//! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins -//! * `_asm_default_handler` - an exception handler that just spins -//! * `_asm_svc_handler` - assembly language trampoline for SVC Exceptions that -//! calls `_svc_handler` -//! * `_asm_irq_handler` - assembly language trampoline for Interrupts that -//! calls `_irq_handler` +//! * `_default_handler` - a C compatible function that spins forever. //! //! The assembly language trampolines are required because Armv7-R (and Armv8-R) //! processors do not save a great deal of state on entry to an exception //! handler, unlike Armv7-M (and other M-Profile) processors. We must therefore //! save this state to the stack using assembly language, before transferring to //! an `extern "C"` function. We do not change modes before entering that -//! `extern "C"` function - that's for the handler to deal with as it wishes. We -//! supply a default handler that prints an error message to Semihosting so you -//! know if you hit an unexpected exception. Because FIQ is often -//! performance-sensitive, we don't supply an FIQ trampoline; if you want to use -//! FIQ, you have to write your own assembly routine, allowing you to preserve -//! only whatever state is important to you. -//! -//! If our start-up routine doesn't work for you (e.g. if you have to initialise -//! your memory controller before you touch RAM), supply your own `_start` -//! function (but feel free to call our `_default_start` as part of it). +//! `extern "C"` function - that's for the handler to deal with as it wishes. +//! Because FIQ is often performance-sensitive, we don't supply an FIQ +//! trampoline; if you want to use FIQ, you have to write your own assembly +//! routine, allowing you to preserve only whatever state is important to you. //! //! ## Examples //! -//! You can find example code using QEMU inside the -//! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) +//! You can find example code using QEMU inside the [project +//! repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) #![no_std] @@ -183,9 +253,7 @@ pub extern "C" fn _default_handler() { // The Interrupt Vector Table, and some default assembly-language handler. core::arch::global_asm!( r#" - .section .vector_table - .align 0 - + .section .vector_table,"ax",%progbits .global _vector_table .type _vector_table, %function _vector_table: @@ -198,20 +266,6 @@ core::arch::global_asm!( ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler .size _vector_table, . - _vector_table - - .section .text.handlers - - .global _asm_default_fiq_handler - .type _asm_default_fiq_handler, %function - _asm_default_fiq_handler: - b _asm_default_fiq_handler - .size _asm_default_fiq_handler, . - _asm_default_fiq_handler - - .global _asm_default_handler - .type _asm_default_handler, %function - _asm_default_handler: - b _asm_default_handler - .size _asm_default_handler, . - _asm_default_handler "# ); @@ -306,59 +360,15 @@ macro_rules! restore_context { // Our assembly language exception handlers core::arch::global_asm!( r#" - .section .text.handlers // Work around https://github.com/rust-lang/rust/issues/127269 .fpu vfp3-d16 - .align 0 - - // Called from the vector table when we have an software interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32);` - .global _asm_svc_handler - .type _asm_svc_handler, %function - _asm_svc_handler: - srsfd sp!, {svc_mode} - "#, - save_context!(), - r#" - mrs r0, cpsr // Load processor status - tst r0, {t_bit} // Occurred in Thumb state? - ldrhne r0, [lr,#-2] // Yes: Load halfword and... - bicne r0, r0, #0xFF00 // ...extract comment field - ldreq r0, [lr,#-4] // No: Load word and... - biceq r0, r0, #0xFF000000 // ...extract comment field - // r0 now contains SVC number - bl _svc_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_svc_handler, . - _asm_svc_handler - - - // Called from the vector table when we have an interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn irq_handler();` - .global _asm_irq_handler - .type _asm_irq_handler, %function - _asm_irq_handler: - sub lr, lr, 4 - srsfd sp!, {irq_mode} - "#, - save_context!(), - r#" - // call C handler - bl _irq_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_irq_handler, . - _asm_irq_handler - - + // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler(addr: usize);` + // `extern "C" fn _undefined_handler(addr: usize) -> usize;` + // or + // `extern "C" fn _undefined_handler(addr: usize) -> !;` + .section .text._asm_default_undefined_handler .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: @@ -386,6 +396,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _undefined_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 // do our standard restore "#, restore_context!(), @@ -401,6 +413,64 @@ core::arch::global_asm!( .size _asm_default_undefined_handler, . - _asm_default_undefined_handler + .section .text._asm_default_svc_handler + + // Called from the vector table when we have an software interrupt. + // Saves state and calls a C-compatible handler like + // `extern "C" fn svc_handler(svc: u32);` + .global _asm_default_svc_handler + .type _asm_default_svc_handler, %function + _asm_default_svc_handler: + srsfd sp!, {svc_mode} + "#, + save_context!(), + r#" + mrs r0, cpsr // Load processor status + tst r0, {t_bit} // Occurred in Thumb state? + ldrhne r0, [lr,#-2] // Yes: Load halfword and... + bicne r0, r0, #0xFF00 // ...extract comment field + ldreq r0, [lr,#-4] // No: Load word and... + biceq r0, r0, #0xFF000000 // ...extract comment field + // r0 now contains SVC number + bl _svc_handler + "#, + restore_context!(), + r#" + rfefd sp! + .size _asm_default_svc_handler, . - _asm_default_svc_handler + + + .section .text._asm_default_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _abort_handler(addr: usize);` + .global _asm_default_abort_handler + .type _asm_default_abort_handler, %function + _asm_default_abort_handler: + // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. + subs lr, lr, #8 + // state save from compiled code + srsfd sp!, {abt_mode} + "#, + save_context!(), + r#" + // Pass the faulting instruction address to the handler. + mov r0, lr + // call C handler + bl _abort_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 + "#, + restore_context!(), + r#" + // Return to the failing instruction which is the recommended approach by ARM. + rfefd sp! + .size _asm_default_abort_handler, . - _asm_default_abort_handler + + + .section .text._asm_default_prefetch_handler + // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like // `extern "C" fn _prefetch_handler(addr: usize);` @@ -418,6 +488,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _prefetch_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 "#, restore_context!(), r#" @@ -426,29 +498,36 @@ core::arch::global_asm!( .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler - // Called from the vector table when we have an undefined exception. + .section .text._asm_default_irq_handler + + // Called from the vector table when we have an interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler(addr: usize);` - .global _asm_default_abort_handler - .type _asm_default_abort_handler, %function - _asm_default_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - srsfd sp!, {abt_mode} + // `extern "C" fn irq_handler();` + .global _asm_default_irq_handler + .type _asm_default_irq_handler, %function + _asm_default_irq_handler: + sub lr, lr, 4 + srsfd sp!, {irq_mode} "#, save_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr // call C handler - bl _abort_handler + bl _irq_handler "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_irq_handler, . - _asm_default_irq_handler + + + .section .text._asm_default_fiq_handler + + // Our default FIQ handler + .global _asm_default_fiq_handler + .type _asm_default_fiq_handler, %function + _asm_default_fiq_handler: + b _asm_default_fiq_handler + .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, irq_mode = const ProcessorMode::Irq as u8, @@ -456,8 +535,8 @@ core::arch::global_asm!( abt_mode = const ProcessorMode::Abt as u8, t_bit = const { Cpsr::new_with_raw_value(0) - .with_t(true) - .raw_value() + .with_t(true) + .raw_value() }, ); @@ -492,11 +571,10 @@ macro_rules! fpu_enable { // We set up our stacks and `kmain` in system mode. core::arch::global_asm!( r#" - .section .text.startup - .align 0 // Work around https://github.com/rust-lang/rust/issues/127269 .fpu vfp3-d16 - + + .section .text.el1_start .type _el1_start, %function _el1_start: // Set up stacks. @@ -618,9 +696,10 @@ core::arch::global_asm!( #[cfg(arm_architecture = "v7-r")] core::arch::global_asm!( r#" - .section .text.startup - .align 0 + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3-d16 + .section .text.default_start .global _default_start .type _default_start, %function _default_start: @@ -638,8 +717,10 @@ core::arch::global_asm!( #[cfg(arm_architecture = "v8-r")] core::arch::global_asm!( r#" - .section .text.startup - .align 0 + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3-d16 + + .section .text.default_start .global _default_start .type _default_start, %function diff --git a/examples/mps3-an536/src/bin/abt-exception.rs b/examples/mps3-an536/src/bin/abt-exception.rs index 4a0955c..ddf599c 100644 --- a/examples/mps3-an536/src/bin/abt-exception.rs +++ b/examples/mps3-an536/src/bin/abt-exception.rs @@ -56,17 +56,17 @@ fn disable_alignment_check() { } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) { +unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) { +unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { panic!("unexpected prefetch exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: u32) { +unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("data abort occurred"); // If this is not disabled, reading DFAR will trigger an alignment fault on Armv8-R, leading // to a loop. @@ -82,4 +82,6 @@ unsafe extern "C" fn _abort_handler(_addr: u32) { if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { semihosting::process::exit(0); } + + addr } diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index ced3ecd..b561ce1 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -77,9 +77,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs index cc03404..12249cb 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -80,9 +80,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index ed9b2e7..ff672f6 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -72,9 +72,12 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + // otherwise go back to where we came from + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs index 75bd43d..1cd70fd 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/abt-exception.rs b/examples/versatileab/src/bin/abt-exception.rs index d22566b..ab03ecd 100644 --- a/examples/versatileab/src/bin/abt-exception.rs +++ b/examples/versatileab/src/bin/abt-exception.rs @@ -56,17 +56,17 @@ fn disable_alignment_check() { } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) { +unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) { +unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { panic!("unexpected prefetch exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: u32) { +unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("data abort occurred"); let dfsr = Dfsr::read(); println!("DFSR (Fault Status Register): {:?}", dfsr); @@ -82,4 +82,6 @@ unsafe extern "C" fn _abort_handler(_addr: u32) { if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { semihosting::process::exit(0); } + + addr } diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index b26ebea..d773dd3 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -77,9 +77,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index d8f640e..acea6c8 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -80,9 +80,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index b8833eb..6679d2d 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index 01ee741..c9869b4 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); }