diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..987b9f9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + // override the default setting (`cargo check --all-targets`) which produces the following error + // "can't find crate for `test`" when the default compilation target is a no_std target + // with these changes RA will call `cargo check --bins` on save + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.extraArgs": ["--bins"], + "rust-analyzer.checkOnSave": true, +} diff --git a/Cargo.toml b/Cargo.toml index 8a17aa7..71e6a1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,5 +8,6 @@ members = [ "cortex-ar", "cortex-r-rt", "cortex-a-rt", + "cortex-ar-rt-macros", ] resolver = "2" diff --git a/cortex-a-rt/Cargo.toml b/cortex-a-rt/Cargo.toml index bc68060..a4cd705 100644 --- a/cortex-a-rt/Cargo.toml +++ b/cortex-a-rt/Cargo.toml @@ -16,6 +16,7 @@ version = "0.1.0" [dependencies] cortex-ar = {version = "0.1.0", path = "../cortex-ar"} +cortex-ar-rt-macros = { path = "../cortex-ar-rt-macros", version = "=0.1.0" } [features] # Enable the FPU on start-up, even on a soft-float EABI target diff --git a/cortex-a-rt/link.x b/cortex-a-rt/link.x index 8ee325f..28d119a 100644 --- a/cortex-a-rt/link.x +++ b/cortex-a-rt/link.x @@ -90,18 +90,18 @@ ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8 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); +PROVIDE(_start = _default_start); +PROVIDE(_asm_undefined_handler = _asm_default_undefined_handler); +PROVIDE(_asm_svc_handler = _asm_default_svc_handler); +PROVIDE(_asm_prefetch_abort_handler = _asm_default_prefetch_abort_handler); +PROVIDE(_asm_data_abort_handler = _asm_default_data_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(_svc_handler =_default_handler); -PROVIDE(_prefetch_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); -PROVIDE(_irq_handler =_default_handler); +PROVIDE(_undefined_handler = _default_handler); +PROVIDE(_svc_handler = _default_handler); +PROVIDE(_prefetch_abort_handler = _default_handler); +PROVIDE(_data_abort_handler = _default_handler); +PROVIDE(_irq_handler = _default_handler); /* 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 304ccbf..4b76cde 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -17,8 +17,8 @@ //! ## 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. +//! 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 @@ -30,9 +30,11 @@ //! System mode. If you wish to write a hypervisor, you will need to replace //! this library with something more advanced. //! -//! We assume the following global symbols exist: +//! We assume that a set of symbols exist, either for constants or for C +//! compatible functions or for naked raw-assembly functions. They are described +//! in the next three sections. //! -//! ### Constants +//! ## Constants //! //! * `_stack_top` - the address of the top of some region of RAM that we can //! use as stack space, with eight-byte alignment. Our linker script PROVIDEs @@ -74,142 +76,333 @@ //! +------------------+ //! ``` //! -//! ### C-Compatible Functions +//! ## C-Compatible Functions //! -//! * `kmain` - the `extern "C"` entry point to your application. +//! ### Main Function //! -//! Expected prototype: +//! The symbol `kmain` should be an `extern "C"` function. It is called in SYS +//! mode after all the global variables have been initialised. There is no +//! default - this function is mandatory. //! -//! ```rust -//! #[unsafe(no_mangle)] -//! extern "C" fn kmain() -> !; -//! ``` +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn kmain() -> ! { +//! loop { } +//! } +//! ``` //! -//! * `_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. Returning from this function -//! will cause execution to resume from the function the triggered the -//! exception, immediately after the SVC instruction. +//! You can also create a 'kmain' function by using the `#[entry]` attribute on +//! a normal Rust function. //! -//! Expected prototype: +//! ```rust +//! use cortex_a_rt::entry; //! -//! ```rust -//! #[unsafe(no_mangle)] -//! extern "C" fn _svc_handler(svc: u32); -//! ``` +//! #[entry] +//! fn my_main() -> ! { +//! loop { } +//! } +//! ``` //! -//! * `_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. 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 implementation at -//! `_default_handler` which is used if `_undefined_handler` is missing. -//! -//! The expected prototype for `_undefined_handler` is either: -//! -//! ```rust -//! /// Does not return -//! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize) -> !; -//! ``` +//! ### Undefined Handler +//! +//! The symbol `_undefined_handler` should be an `extern "C"` function. It is +//! called in UND mode when an [Undefined Instruction Exception] occurs. +//! +//! [Undefined Instruction Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Undefined-Instruction-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_undefined_handler` symbol which is an +//! alias for the `_default_handler` function. You can override it by defining +//! your own `_undefined_handler` function, like: +//! +//! ```rust +//! /// Does not return +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! 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 _undefined_handler(addr: usize) -> usize; -//! ``` -//! -//! * `_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) -> !; -//! ``` +//! or: +//! +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +//! // do stuff here, then return to the address *after* the one +//! // that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_undefined_handler` function by using the +//! `#[exception(Undefined)]` attribute on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(Undefined)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! 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 _abort_handler(addr: usize) -> usize; -//! ``` -//! -//! * `_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 +//! use cortex_a_rt::exception; +//! +//! #[exception(Undefined)] +//! unsafe fn my_handler(addr: usize) -> usize { +//! // do stuff here, then return the address to return to +//! addr + 4 +//! } +//! ``` +//! +//! ### Supervisor Call Handler +//! +//! The symbol `_svc_handler` should be an `extern "C"` function. It is called +//! in SVC mode when a [Supervisor Call Exception] occurs. +//! +//! [Supervisor Call Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en +//! +//! Returning from this function will cause execution to resume at the function +//! the triggered the exception, immediately after the SVC instruction. You +//! cannot control where execution resumes. The function is passed the literal +//! integer argument to the `svc` instruction, which is extracted from the +//! machine code for you by the default assembly trampoline. +//! +//! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias +//! for the `_default_handler` function. You can override it by defining your +//! own `_svc_handler` function, like: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _svc_handler(svc: u32) { +//! // do stuff here +//! } +//! ``` +//! +//! You can also create a `_svc_handler` function by using the +//! `#[exception(SupervisorCall)]` attribute on a normal Rust function. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(SupervisorCall)] +//! fn my_svc_handler(arg: u32) { +//! // do stuff here +//! } +//! ``` +//! +//! ### Prefetch Abort Handler +//! +//! The symbol `_prefetch_abort_handler` should be an `extern "C"` function. It +//! is called in ABT mode when a [Prefetch Abort Exception] occurs. +//! +//! [Prefetch Abort Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Prefetch-Abort-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_prefetch_abort_handler` symbol which +//! is an alias for the `_default_handler` function. You can override it by +//! defining your own `_undefined_handler` function. +//! +//! This function takes the address of faulting instruction, and can either not +//! return: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_abort_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! Or it can return an address where execution should resume after the +//! Exception handler is complete (which is unsafe): +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_prefetch_abort_handler` function by using the +//! `#[exception(PrefetchAbort)]` macro on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(PrefetchAbort)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! or: +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(PrefetchAbort)] +//! unsafe fn my_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! ### Data Abort Handler +//! +//! The symbol `_data_abort_handler` should be an `extern "C"` function. It is +//! called in ABT mode when a [Data Abort Exception] occurs. +//! +//! [Data Abort Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Data-Abort-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_data_abort_handler` symbol which is +//! an alias for the `_default_handler` function. You can override it by +//! defining your own `_undefined_handler` function. +//! +//! This function takes the address of faulting instruction, and can either not +//! return: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _data_abort_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! or: +//! Or it can return an address where execution should resume after the +//! Exception handler is complete (which is unsafe): +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_data_abort_handler` function by using the +//! `#[exception(DataAbort)]` macro on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(DataAbort)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! or: +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(DataAbort)] +//! unsafe fn my_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! ### IRQ Handler +//! +//! The symbol `_irq_handler` should be an `extern "C"` function. It is called +//! in SYS mode (not IRQ mode!) when an [Interrupt] occurs. +//! +//! [Interrupt]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/IRQ-exception?lang=en //! -//! ```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; -//! ``` +//! Returning from this function will cause execution to resume at wherever it +//! was interrupted. You cannot control where execution resumes. //! -//! ### ASM functions +//! This function is entered with interrupts masked, but you may unmask (i.e. +//! enable) interrupts inside this function if desired. You will probably want +//! to talk to your interrupt controller first, otherwise you'll just keep +//! re-entering this interrupt handler recursively until you stack overflow. +//! +//! Our linker script PROVIDEs a default `_irq_handler` symbol which is an alias +//! for `_default_handler`. You can override it by defining your own +//! `_irq_handler` function. +//! +//! Expected prototype: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _irq_handler() { +//! // 1. Talk to interrupt controller +//! // 2. Handle interrupt +//! // 3. Clear interrupt +//! } +//! ``` +//! +//! You can also create a `_irq_handler` function by using the `#[irq]` +//! attribute on a normal Rust function. +//! +//! ```rust +//! use cortex_a_rt::irq; +//! +//! #[irq] +//! fn my_irq_handler() { +//! // 1. Talk to interrupt controller +//! // 2. Handle interrupt +//! // 3. Clear interrupt +//! } +//! ``` +//! +//! ## ASM functions +//! +//! These are the naked 'raw' assembly functions the run-time requires: //! //! * `_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). +//! at `_default_start` but you can override it. The provided default start +//! function will initialise all global variables and then call `kmain` in SYS +//! mode. 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. 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 +//! default handler will call `_undefined_handler` in UND mode, saving state +//! as required. +//! +//! * `_asm_svc_handler` - a naked function to call when an Supervisor Call +//! (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 +//! handler will call `_svc_handler` in SVC mode, saving state as required. +//! +//! * `_asm_prefetch_abort_handler` - a naked function to call when a Prefetch +//! Abort Exception occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_prefetch_abort_handler` but you can override it. The +//! provided default handler will call `_prefetch_abort_handler`, saving state +//! as required. Note that Prefetch Abort Exceptions are handled in Abort Mode +//! (ABT), Monitor Mode (MON) or Hyp Mode (HYP), depending on CPU +//! configuration. +//! +//! * `_asm_data_abort_handler` - a naked function to call when a Data Abort //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_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 call `_abort_handler`, saving state as required. +//! `_asm_default_data_abort_handler` but you can override it. The provided +//! default handler will call `_data_abort_handler` in ABT mode, 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. +//! handler will call `_irq_handler` in SYS mode (not IRQ mode), 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 @@ -226,10 +419,10 @@ //! `_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_prefetch_abort_handler` - assembly language trampoline that +//! calls `_prefetch_abort_handler` +//! * `_asm_default_data_abort_handler` - assembly language trampoline that +//! calls `_data_abort_handler` //! * `_asm_default_irq_handler` - assembly language trampoline that calls //! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins @@ -240,7 +433,7 @@ //! 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 +//! \- 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. @@ -252,10 +445,10 @@ #![no_std] -use cortex_ar::{ - asm::nop, - register::{cpsr::ProcessorMode, Cpsr}, -}; +#[cfg(target_arch = "arm")] +use cortex_ar::register::{cpsr::ProcessorMode, Cpsr}; + +pub use cortex_ar_rt_macros::{entry, exception, irq}; /// Our default exception handler. /// @@ -264,23 +457,23 @@ use cortex_ar::{ #[no_mangle] pub extern "C" fn _default_handler() { loop { - nop(); + core::hint::spin_loop(); } } // The Interrupt Vector Table, and some default assembly-language handler. +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" .section .vector_table,"ax",%progbits - .global _vector_table .type _vector_table, %function _vector_table: ldr pc, =_start ldr pc, =_asm_undefined_handler ldr pc, =_asm_svc_handler - ldr pc, =_asm_prefetch_handler - ldr pc, =_asm_abort_handler + ldr pc, =_asm_prefetch_abort_handler + ldr pc, =_asm_data_abort_handler nop ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler @@ -294,7 +487,10 @@ core::arch::global_asm!( /// It should match `restore_context!`. /// /// On entry to this block, we assume that we are in exception context. -#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] +#[cfg(all( + target_arch = "arm", + not(any(target_abi = "eabihf", feature = "eabi-fpu")) +))] macro_rules! save_context { () => { r#" @@ -314,7 +510,10 @@ macro_rules! save_context { /// handler. /// /// It should match `save_context!`. -#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] +#[cfg(all( + target_arch = "arm", + not(any(target_abi = "eabihf", feature = "eabi-fpu")) +))] macro_rules! restore_context { () => { r#" @@ -333,6 +532,7 @@ macro_rules! restore_context { /// /// It should match `restore_context!`. #[cfg(all( + target_arch = "arm", any(target_abi = "eabihf", feature = "eabi-fpu"), not(feature = "vfp-dp") ))] @@ -361,6 +561,7 @@ macro_rules! save_context { /// /// It should match `save_context!`. #[cfg(all( + target_arch = "arm", any(target_abi = "eabihf", feature = "eabi-fpu"), not(feature = "vfp-dp") ))] @@ -386,7 +587,11 @@ macro_rules! restore_context { /// handler. /// /// It should match `restore_context!`. -#[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "vfp-dp"))] +#[cfg(all( + target_arch = "arm", + any(target_abi = "eabihf", feature = "eabi-fpu"), + feature = "vfp-dp" +))] macro_rules! save_context { () => { r#" @@ -412,7 +617,11 @@ macro_rules! save_context { /// handler. /// /// It should match `save_context!`. -#[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "vfp-dp"))] +#[cfg(all( + target_arch = "arm", + any(target_abi = "eabihf", feature = "eabi-fpu"), + feature = "vfp-dp" +))] macro_rules! restore_context { () => { r#" @@ -433,21 +642,22 @@ macro_rules! restore_context { } // Our assembly language exception handlers +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" - .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) -> 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: // state save from compiled code - srsfd sp!, {und_mode} - // to work out what mode we're in, we need R0, so save it + srsfd sp!, #{und_mode} + // to work out what mode we're in, we need R0 push {{r0}} // First adjust LR for two purposes: Passing the faulting instruction to the C handler, // and to return to the failing instruction after the C handler returns. @@ -481,16 +691,16 @@ 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);` + // `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} + srsfd sp!, #{svc_mode} "#, save_context!(), r#" @@ -509,25 +719,25 @@ core::arch::global_asm!( .size _asm_default_svc_handler, . - _asm_default_svc_handler - .section .text._asm_default_abort_handler + .section .text._asm_default_data_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: + // `extern "C" fn _data_abort_handler(addr: usize);` + .global _asm_default_data_abort_handler + .type _asm_default_data_abort_handler, %function + _asm_default_data_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} + srsfd sp!, #{abt_mode} "#, save_context!(), r#" // Pass the faulting instruction address to the handler. mov r0, lr // call C handler - bl _abort_handler + bl _data_abort_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 "#, @@ -537,28 +747,28 @@ core::arch::global_asm!( str lr, [sp] // Return from the asm handler rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler - .section .text._asm_default_prefetch_handler + .section .text._asm_default_prefetch_abort_handler - // Called from the vector table when we have a prefetch exception. + // Called from the vector table when we have a prefetch abort. // Saves state and calls a C-compatible handler like - // `extern "C" fn _prefetch_handler(addr: usize);` - .global _asm_default_prefetch_handler - .type _asm_default_prefetch_handler, %function - _asm_default_prefetch_handler: + // `extern "C" fn _prefetch_abort_handler(addr: usize);` + .global _asm_default_prefetch_abort_handler + .type _asm_default_prefetch_abort_handler, %function + _asm_default_prefetch_abort_handler: // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual. subs lr, lr, #4 // state save from compiled code - srsfd sp!, {abt_mode} + srsfd sp!, #{abt_mode} "#, save_context!(), r#" // Pass the faulting instruction address to the handler. mov r0, lr // call C handler - bl _prefetch_handler + bl _prefetch_abort_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 "#, @@ -568,27 +778,39 @@ core::arch::global_asm!( str lr, [sp] // Return from the asm handler rfefd sp! - .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler + .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler .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 irq_handler();` + // `extern "C" fn _irq_handler();` .global _asm_default_irq_handler .type _asm_default_irq_handler, %function _asm_default_irq_handler: + // make sure we jump back to the right place sub lr, lr, 4 - srsfd sp!, {irq_mode} + // The hardware has copied CPSR to SPSR_irq and LR to LR_irq for us. + // Now push SPSR_irq and LR_irq to the SYS stack. + srsfd sp!, #{sys_mode} + // switch to system mode + cps #{sys_mode} + // we also need to save LR, so we can be re-entrant + push {{lr}} + // save state to the system stack (adjusting SP for alignment) "#, - save_context!(), + save_context!(), r#" // call C handler bl _irq_handler + // restore from the system stack "#, - restore_context!(), + restore_context!(), r#" + // restore LR + pop {{lr}} + // pop CPSR and LR from the stack (which also restores the mode) rfefd sp! .size _asm_default_irq_handler, . - _asm_default_irq_handler @@ -603,9 +825,9 @@ core::arch::global_asm!( .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, - irq_mode = const ProcessorMode::Irq as u8, und_mode = const ProcessorMode::Und as u8, abt_mode = const ProcessorMode::Abt as u8, + sys_mode = const ProcessorMode::Sys as u8, t_bit = const { Cpsr::new_with_raw_value(0) .with_t(true) @@ -614,7 +836,7 @@ core::arch::global_asm!( ); /// This macro expands to code to turn on the FPU -#[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))] +#[cfg(all(target_arch = "arm", any(target_abi = "eabihf", feature = "eabi-fpu")))] macro_rules! fpu_enable { () => { r#" @@ -630,7 +852,10 @@ macro_rules! fpu_enable { } /// This macro expands to code that does nothing because there is no FPU -#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] +#[cfg(all( + target_arch = "arm", + not(any(target_abi = "eabihf", feature = "eabi-fpu")) +))] macro_rules! fpu_enable { () => { r#" @@ -642,6 +867,7 @@ macro_rules! fpu_enable { // Default start-up code for Armv7-A // // We set up our stacks and `kmain` in system mode. +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" .section .text.default_start diff --git a/cortex-ar-rt-macros/Cargo.toml b/cortex-ar-rt-macros/Cargo.toml new file mode 100644 index 0000000..1846fdb --- /dev/null +++ b/cortex-ar-rt-macros/Cargo.toml @@ -0,0 +1,26 @@ +[package] +authors = [ + "Robin Mueller ", + "Jonathan Pallant ", + "The Cortex-R Team " +] +description = "Run-Time macros for Arm Cortex-A and Cortex-R" +edition = "2021" +license = "MIT OR Apache-2.0" +name = "cortex-ar-rt-macros" +readme = "README.md" +repository = "https://github.com/rust-embedded/cortex-ar.git" +homepage = "https://github.com/rust-embedded/cortex-ar.git" +rust-version = "1.82" +version = "0.1.0" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" + +[dependencies.syn] +features = ["extra-traits", "full"] +version = "2.0" diff --git a/cortex-ar-rt-macros/src/lib.rs b/cortex-ar-rt-macros/src/lib.rs new file mode 100644 index 0000000..a56a573 --- /dev/null +++ b/cortex-ar-rt-macros/src/lib.rs @@ -0,0 +1,501 @@ +//! Macros for the cortex-a-rt and cortex-r-rt libraries +//! +//! Provides `#[entry]`, `#[exception(...)]` and `#[irq]` attribute macros. +//! +//! Do not use this crate directly. +//! +//! Based on . + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree}; +use proc_macro2::Span; +use quote::quote; +use syn::{ + parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, Ident, ItemFn, ReturnType, + Type, Visibility, +}; + +/// Creates an `unsafe` program entry point (i.e. a `kmain` function). +/// +/// It's `unsafe` because you are not supposed to call it - it should only be +/// called from the start-up code once initialisation is complete. +/// +/// When placed on a function like: +/// +/// ```rust ignore +/// #[entry] +/// fn foo() -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// You get something like: +/// +/// ```rust +/// #[doc(hidden)] +/// #[export_name = "kmain"] +/// pub unsafe extern "C" fn __cortex_ar_rt_kmain() -> ! { +/// foo() +/// } +/// +/// fn foo() -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// The symbol `kmain` is what the assembly code in both the cortex-r-rt and +/// cortex-a-rt start-up code will jump to, and the `extern "C"` makes it sound +/// to call from assembly. +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + // check the function signature. + // + // it should be `fn foo() -> !` or `unsafe fn foo() -> !` + let valid_signature = f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[entry]` function must have signature `[unsafe] fn() -> !`", + ) + .to_compile_error() + .into(); + } + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + let tramp_ident = Ident::new("__cortex_ar_rt_kmain", Span::call_site()); + let ident = &f.sig.ident; + + if let Err(error) = check_attr_whitelist(&f.attrs, Kind::Entry) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "kmain"] + pub unsafe extern "C" fn #tramp_ident() -> ! { + #ident() + } + + #f + ) + .into() +} + +/// The set of exceptions we can handle. +#[derive(Debug, PartialEq)] +enum Exception { + Undefined, + SupervisorCall, + PrefetchAbort, + DataAbort, + Irq, +} + +impl std::fmt::Display for Exception { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Exception::Undefined => write!(f, "Undefined"), + Exception::SupervisorCall => write!(f, "SupervisorCall"), + Exception::PrefetchAbort => write!(f, "PrefetchAbort"), + Exception::DataAbort => write!(f, "DataAbort"), + Exception::Irq => write!(f, "Irq"), + } + } +} + +/// Creates an `unsafe` exception handler. +/// +/// It's `unsafe` because you are not supposed to call it - it should only be +/// called from assembly routines registered in the interrupt vector table. +/// +/// When placed on a function like: +/// +/// ```rust ignore +/// #[exception(Undefined)] +/// fn foo(addr: usize) -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// You get something like: +/// +/// ```rust +/// #[doc(hidden)] +/// #[export_name = "_undefined_handler"] +/// pub unsafe extern "C" fn __cortex_ar_rt_undefined_handler(addr: usize) -> ! { +/// foo(addr) +/// } +/// +/// fn foo(addr: usize) -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// The supported arguments are: +/// +/// * Undefined (creates `_undefined_handler`) +/// * SupervisorCall (creates `_svc_handler`) +/// * PrefetchAbort (creates `_prefetch_abort_handler`) +/// * DataAbort (creates `_data_abort_handler`) +/// * Irq (creates `_irq_handler`) - although people should prefer `#[irq]`. +#[proc_macro_attribute] +pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { + handle_exception_interrupt(args, input, Kind::Exception) +} + +/// Creates an `unsafe` interrupt handler. +/// +/// It's `unsafe` because you are not supposed to call it - it should only be +/// called from assembly routines registered in the interrupt vector table. +/// +/// When placed on a function like: +/// +/// ```rust ignore +/// #[irq] +/// fn foo(addr: usize) -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// You get something like: +/// +/// ```rust +/// #[doc(hidden)] +/// #[export_name = "_irq_handler"] +/// pub unsafe extern "C" fn __cortex_ar_rt_irq_handler(addr: usize) -> ! { +/// foo(addr) +/// } +/// +/// fn foo(addr: usize) -> ! { +/// panic!("On no") +/// } +/// ``` +/// +/// This is preferred over `#[exception(Irq)` because most people +/// probably won't consider interrupts to be a form of exception. +#[proc_macro_attribute] +pub fn irq(args: TokenStream, input: TokenStream) -> TokenStream { + handle_exception_interrupt(args, input, Kind::Interrupt) +} + +/// Note if we got `#[entry]`, `#[exception(...)]` or `#[irq]` +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +enum Kind { + /// Corresponds to `#[entry]` + Entry, + /// Corresponds to `#[exception(...)]` + Exception, + /// Corresponds to `#[irq]` + Interrupt, +} + +/// A common routine for handling exception or interrupt functions +fn handle_exception_interrupt(args: TokenStream, input: TokenStream, kind: Kind) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + if let Err(error) = check_attr_whitelist(&f.attrs, kind) { + return error; + } + + let returns_never = match f.sig.output { + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), + _ => false, + }; + + let exception = match kind { + Kind::Entry => { + panic!("Don't handle #[entry] with `handle_exception_interrupt`!"); + } + Kind::Exception => { + let mut args_iter = args.into_iter(); + let Some(TokenTree::Ident(exception_name)) = args_iter.next() else { + return parse::Error::new( + Span::call_site(), + "This attribute requires the name of the exception as the first argument", + ) + .to_compile_error() + .into(); + }; + if args_iter.next().is_some() { + return parse::Error::new( + Span::call_site(), + "This attribute accepts only one argument", + ) + .to_compile_error() + .into(); + } + match exception_name.to_string().as_str() { + "Undefined" => { + if !returns_never && f.sig.unsafety.is_none() { + return parse::Error::new( + exception_name.span().into(), + "Undefined handlers that don't return ! must be unsafe", + ) + .to_compile_error() + .into(); + } + Exception::Undefined + } + "SupervisorCall" => Exception::SupervisorCall, + "PrefetchAbort" => { + if !returns_never && f.sig.unsafety.is_none() { + return parse::Error::new( + exception_name.span().into(), + "PrefetchAbort handlers that don't return ! must be unsafe", + ) + .to_compile_error() + .into(); + } + Exception::PrefetchAbort + } + "DataAbort" => { + if !returns_never && f.sig.unsafety.is_none() { + return parse::Error::new( + exception_name.span().into(), + "DataAbort handlers that don't return ! must be unsafe", + ) + .to_compile_error() + .into(); + } + Exception::DataAbort + } + "Irq" => Exception::Irq, + _ => { + return parse::Error::new( + exception_name.span().into(), + "This is not a valid exception name", + ) + .to_compile_error() + .into(); + } + } + } + Kind::Interrupt => Exception::Irq, + }; + + let ident = &f.sig.ident; + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + + let handler = match exception { + // extern "C" fn _undefined_handler(addr: usize) -> !; + // unsafe extern "C" fn _undefined_handler(addr: usize) -> usize; + Exception::Undefined => { + let tramp_ident = Ident::new("__cortex_ar_rt_undefined_handler", Span::call_site()); + if returns_never { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_undefined_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! { + #ident(addr) + } + + #f + ) + } else { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_undefined_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize { + unsafe { + #ident(addr) + } + } + + #f + ) + } + } + // extern "C" fn _prefetch_abort_handler(addr: usize) -> !; + // unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize; + Exception::PrefetchAbort => { + let tramp_ident = + Ident::new("__cortex_ar_rt_prefetch_abort_handler", Span::call_site()); + if returns_never { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_prefetch_abort_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! { + #ident(addr) + } + + #f + ) + } else { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_prefetch_abort_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize { + unsafe { + #ident(addr) + } + } + + #f + ) + } + } + // extern "C" fn _data_abort_handler(addr: usize) -> !; + // unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize; + Exception::DataAbort => { + let tramp_ident = Ident::new("__cortex_ar_rt_data_abort_handler", Span::call_site()); + if returns_never { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_data_abort_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! { + #ident(addr) + } + + #f + ) + } else { + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_data_abort_handler"] + pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize { + unsafe { + #ident(addr) + } + } + + #f + ) + } + } + // extern "C" fn _svc_handler(addr: usize); + Exception::SupervisorCall => { + let tramp_ident = Ident::new("__cortex_ar_rt_svc_handler", Span::call_site()); + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_svc_handler"] + pub unsafe extern "C" fn #tramp_ident(arg: u32) { + #ident(arg) + } + + #f + ) + } + // extern "C" fn _irq_handler(addr: usize); + Exception::Irq => { + let tramp_ident = Ident::new("__cortex_ar_rt_irq_handler", Span::call_site()); + quote!( + #(#cfgs)* + #(#attrs)* + #[doc(hidden)] + #[export_name = "_irq_handler"] + pub unsafe extern "C" fn #tramp_ident() { + #ident() + } + + #f + ) + } + }; + + quote!( + #handler + ) + .into() +} + +/// Given a list of attributes, split them into `cfg` and non-`cfg`. +/// +/// Returns `(cfgs, non_cfgs)`. +fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { + let mut cfgs = vec![]; + let mut not_cfgs = vec![]; + + for attr in attrs { + if eq(&attr, "cfg") { + cfgs.push(attr); + } else { + not_cfgs.push(attr); + } + } + + (cfgs, not_cfgs) +} + +/// Check whether any disallowed attributes have been applied to our entry/exception function. +fn check_attr_whitelist(attrs: &[Attribute], caller: Kind) -> Result<(), TokenStream> { + let whitelist = &[ + "doc", + "link_section", + "cfg", + "allow", + "warn", + "deny", + "forbid", + "cold", + "naked", + "expect", + ]; + + 'o: for attr in attrs { + for val in whitelist { + if eq(attr, val) { + continue 'o; + } + } + + let err_str = match caller { + Kind::Entry => { + "this attribute is not allowed on a cortex-r-rt/cortex-a-rt entry point" + } + Kind::Exception => { + "this attribute is not allowed on an exception handler controlled by cortex-r-rt/cortex-a-rt" + } + Kind::Interrupt => { + "this attribute is not allowed on an interrupt handler controlled by cortex-r-rt/cortex-a-rt" + } + }; + + return Err(parse::Error::new(attr.span(), err_str) + .to_compile_error() + .into()); + } + + Ok(()) +} + +/// Returns `true` if `attr.path` matches `name` +fn eq(attr: &Attribute, name: &str) -> bool { + attr.style == AttrStyle::Outer && attr.path().is_ident(name) +} diff --git a/cortex-ar/src/asm.rs b/cortex-ar/src/asm.rs index 58184ce..2695b41 100644 --- a/cortex-ar/src/asm.rs +++ b/cortex-ar/src/asm.rs @@ -66,5 +66,5 @@ pub fn core_id() -> u32 { unsafe { core::arch::asm!("MRC p15, 0, {}, c0, c0, 5", out(reg) r, options(nomem, nostack, preserves_flags)); } - return r & 0x00FF_FFFF; + r & 0x00FF_FFFF } diff --git a/cortex-ar/src/lib.rs b/cortex-ar/src/lib.rs index ed25e57..283fde1 100644 --- a/cortex-ar/src/lib.rs +++ b/cortex-ar/src/lib.rs @@ -4,7 +4,9 @@ mod critical_section; +#[cfg(target_arch = "arm")] pub mod asm; + pub mod interrupt; pub mod mmu; pub mod register; @@ -20,7 +22,7 @@ pub mod pmsav8; /// Generate an SVC call with the given argument. /// -/// Safe to call even in Supervisor (Svc) mode, as long as your Svc handler +/// Safe to call even in Supervisor (SupervisorCall) mode, as long as your Svc handler /// saves and restores SPSR_svc correctly. #[macro_export] macro_rules! svc { diff --git a/cortex-r-rt/Cargo.toml b/cortex-r-rt/Cargo.toml index 50f9c17..340f002 100644 --- a/cortex-r-rt/Cargo.toml +++ b/cortex-r-rt/Cargo.toml @@ -26,6 +26,7 @@ version = "0.1.0" [dependencies] cortex-ar = {version = "0.1.0", path = "../cortex-ar"} semihosting = {version = "0.1.18", features = ["stdio"]} +cortex-ar-rt-macros = { path = "../cortex-ar-rt-macros", version = "=0.1.0" } [features] # Enable the FPU on start-up, even on a soft-float EABI target diff --git a/cortex-r-rt/link.x b/cortex-r-rt/link.x index e2da1f0..380b857 100644 --- a/cortex-r-rt/link.x +++ b/cortex-r-rt/link.x @@ -96,18 +96,18 @@ ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8 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); +PROVIDE(_start = _default_start); +PROVIDE(_asm_undefined_handler = _asm_default_undefined_handler); +PROVIDE(_asm_svc_handler = _asm_default_svc_handler); +PROVIDE(_asm_prefetch_abort_handler = _asm_default_prefetch_abort_handler); +PROVIDE(_asm_data_abort_handler = _asm_default_data_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(_svc_handler =_default_handler); -PROVIDE(_prefetch_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); -PROVIDE(_irq_handler =_default_handler); +PROVIDE(_undefined_handler = _default_handler); +PROVIDE(_svc_handler = _default_handler); +PROVIDE(_prefetch_abort_handler = _default_handler); +PROVIDE(_data_abort_handler = _default_handler); +PROVIDE(_irq_handler = _default_handler); /* 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 052412f..71d38f9 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -27,9 +27,11 @@ //! System mode. If you wish to write a hypervisor, you will need to replace //! this library with something more advanced. //! -//! We assume the following global symbols exist: +//! We assume that a set of symbols exist, either for constants or for C +//! compatible functions or for naked raw-assembly functions. They are described +//! in the next three sections. //! -//! ### Constants +//! ## Constants //! //! * `_stack_top` - the address of the top of some region of RAM that we can //! use as stack space, with eight-byte alignment. Our linker script PROVIDEs @@ -73,142 +75,333 @@ //! +------------------+ //! ``` //! -//! ### C-Compatible Functions +//! ## C-Compatible Functions //! -//! * `kmain` - the `extern "C"` entry point to your application. +//! ### Main Function //! -//! Expected prototype: +//! The symbol `kmain` should be an `extern "C"` function. It is called in SYS +//! mode after all the global variables have been initialised. There is no +//! default - this function is mandatory. //! -//! ```rust -//! #[unsafe(no_mangle)] -//! extern "C" fn kmain() -> !; -//! ``` +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn kmain() -> ! { +//! loop { } +//! } +//! ``` //! -//! * `_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. Returning from this function -//! will cause execution to resume from the function the triggered the -//! exception, immediately after the SVC instruction. +//! You can also create a 'kmain' function by using the `#[entry]` attribute on +//! a normal Rust function. +//! +//! ```rust +//! use cortex_a_rt::entry; //! -//! Expected prototype: +//! #[entry] +//! fn my_main() -> ! { +//! loop { } +//! } +//! ``` //! -//! ```rust -//! #[unsafe(no_mangle)] -//! extern "C" fn _svc_handler(svc: u32); -//! ``` +//! ### Undefined Handler //! -//! * `_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. 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 implementation at -//! `_default_handler` which is used if `_undefined_handler` is missing. -//! -//! The expected prototype for `_undefined_handler` is either: -//! -//! ```rust -//! /// Does not return -//! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize) -> !; -//! ``` +//! The symbol `_undefined_handler` should be an `extern "C"` function. It is +//! called in UND mode when an [Undefined Instruction Exception] occurs. +//! +//! [Undefined Instruction Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Undefined-Instruction-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_undefined_handler` symbol which is an +//! alias for the `_default_handler` function. You can override it by defining +//! your own `_undefined_handler` function, like: +//! +//! ```rust +//! /// Does not return +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! 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 _undefined_handler(addr: usize) -> usize; -//! ``` -//! -//! * `_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) -> !; -//! ``` +//! or: +//! +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +//! // do stuff here, then return to the address *after* the one +//! // that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_undefined_handler` function by using the +//! `#[exception(Undefined)]` attribute on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(Undefined)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! 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 _abort_handler(addr: usize) -> usize; -//! ``` -//! -//! * `_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 +//! use cortex_a_rt::exception; +//! +//! #[exception(Undefined)] +//! unsafe fn my_handler(addr: usize) -> usize { +//! // do stuff here, then return the address to return to +//! addr + 4 +//! } +//! ``` +//! +//! ### Supervisor Call Handler +//! +//! The symbol `_svc_handler` should be an `extern "C"` function. It is called +//! in SVC mode when an [Supervisor Call Exception] occurs. +//! +//! [Supervisor CalL Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en +//! +//! Returning from this function will cause execution to resume at the function +//! the triggered the exception, immediately after the SVC instruction. You +//! cannot control where execution resumes. The function is passed the literal +//! integer argument to the `svc` instruction, which is extracted from the +//! machine code for you by the default assembly trampoline. +//! +//! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias +//! for the `_default_handler` function. You can override it by defining your +//! own `_svc_handler` function, like: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _svc_handler(svc: u32) { +//! // do stuff here +//! } +//! ``` +//! +//! You can also create a `_svc_handler` function by using the +//! `#[exception(SupervisorCall)]` attribute on a normal Rust function. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(SupervisorCall)] +//! fn my_svc_handler(arg: u32) { +//! // do stuff here +//! } +//! ``` +//! +//! ### Prefetch Abort Handler +//! +//! The symbol `_prefetch_abort_handler` should be an `extern "C"` function. It +//! is called in ABT mode when a [Prefetch Abort Exception] occurs. +//! +//! [Prefetch Abort Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Prefetch-Abort-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_prefetch_abort_handler` symbol which +//! is an alias for the `_default_handler` function. You can override it by +//! defining your own `_undefined_handler` function. +//! +//! This function takes the address of faulting instruction, and can either not +//! return: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_abort_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! Or it can return an address where execution should resume after the +//! Exception handler is complete (which is unsafe): +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _prefetch_abort_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_prefetch_abort_handler` function by using the +//! `#[exception(PrefetchAbort)]` macro on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(PrefetchAbort)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! or: +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(PrefetchAbort)] +//! fn my_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! ### Data Abort Handler +//! +//! The symbol `_data_abort_handler` should be an `extern "C"` function. It is +//! called in ABT mode when a Data Abort Exception occurs. +//! +//! [Data Abort Exception]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Data-Abort-exception?lang=en +//! +//! Our linker script PROVIDEs a default `_data_abort_handler` symbol which is +//! an alias for the `_default_handler` function. You can override it by +//! defining your own `_undefined_handler` function. +//! +//! This function takes the address of faulting instruction, and can either not +//! return: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _data_abort_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` //! -//! or: +//! Or it can return an address where execution should resume after the +//! Exception handler is complete (which is unsafe): +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! unsafe extern "C" fn _data_abort_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! You can create a `_data_abort_handler` function by using the +//! `#[exception(DataAbort)]` macro on a Rust function with the appropriate +//! arguments and return type. +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(DataAbort)] +//! fn my_handler(addr: usize) -> ! { +//! loop { } +//! } +//! ``` +//! +//! or: +//! +//! ```rust +//! use cortex_a_rt::exception; +//! +//! #[exception(DataAbort)] +//! unsafe fn my_handler(addr: usize) -> usize { +//! // do stuff, then go back to the instruction after the one that failed +//! addr + 4 +//! } +//! ``` +//! +//! ### IRQ Handler +//! +//! The symbol `_irq_handler` should be an `extern "C"` function. It is called +//! in SYS mode (not IRQ mode!) when an [Interrupt] occurs. +//! +//! [Interrupt]: +//! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/IRQ-exception?lang=en +//! +//! Returning from this function will cause execution to resume at wherever it +//! was interrupted. You cannot control where execution resumes. //! -//! ```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; -//! ``` +//! This function is entered with interrupts masked, but you may unmask (i.e. +//! enable) interrupts inside this function if desired. You will probably want +//! to talk to your interrupt controller first, otherwise you'll just keep +//! re-entering this interrupt handler recursively until you stack overflow. //! -//! ### ASM functions +//! Our linker script PROVIDEs a default `_irq_handler` symbol which is an alias +//! for `_default_handler`. You can override it by defining your own +//! `_irq_handler` function. +//! +//! Expected prototype: +//! +//! ```rust +//! #[unsafe(no_mangle)] +//! extern "C" fn _irq_handler() { +//! // 1. Talk to interrupt controller +//! // 2. Handle interrupt +//! // 3. Clear interrupt +//! } +//! ``` +//! +//! You can also create a `_irq_handler` function by using the `#[irq]` +//! attribute on a normal Rust function. +//! +//! ```rust +//! use cortex_a_rt::irq; +//! +//! #[irq] +//! fn my_irq_handler() { +//! // 1. Talk to interrupt controller +//! // 2. Handle interrupt +//! // 3. Clear interrupt +//! } +//! ``` +//! +//! ## ASM functions +//! +//! These are the naked 'raw' assembly functions the run-time requires: //! //! * `_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). +//! at `_default_start` but you can override it. The provided default start +//! function will initialise all global variables and then call `kmain` in SYS +//! mode. Some SoCs require a chip specific startup for tasks like MPU +//! 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. 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 +//! default handler will call `_undefined_handler` in UND mode, saving state +//! as required. +//! +//! * `_asm_svc_handler` - a naked function to call when an Supervisor Call +//! (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 +//! handler will call `_svc_handler` in SVC mode, saving state as required. +//! +//! * `_asm_prefetch_abort_handler` - a naked function to call when a Prefetch +//! Abort Exception occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_prefetch_abort_handler` but you can override it. The +//! provided default handler will call `_prefetch_abort_handler`, saving state +//! as required. Note that Prefetch Abort Exceptions are handled in Abort Mode +//! (ABT), Monitor Mode (MON) or Hyp Mode (HYP), depending on CPU +//! configuration. +//! +//! * `_asm_data_abort_handler` - a naked function to call when a Data Abort //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_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 call `_abort_handler`, saving state as required. +//! `_asm_default_data_abort_handler` but you can override it. The provided +//! default handler will call `_data_abort_handler` in ABT mode, 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. +//! handler will call `_irq_handler` in SYS mode (not IRQ mode), 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 @@ -225,10 +418,10 @@ //! `_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_prefetch_abort_handler` - assembly language trampoline that +//! calls `_prefetch_abort_handler` +//! * `_asm_default_data_abort_handler` - assembly language trampoline that +//! calls `_data_abort_handler` //! * `_asm_default_irq_handler` - assembly language trampoline that calls //! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins @@ -254,14 +447,14 @@ #![no_std] -use cortex_ar::{ - asm::nop, - register::{cpsr::ProcessorMode, Cpsr}, -}; +#[cfg(target_arch = "arm")] +use cortex_ar::register::{cpsr::ProcessorMode, Cpsr}; #[cfg(arm_architecture = "v8-r")] use cortex_ar::register::Hactlr; +pub use cortex_ar_rt_macros::{entry, exception, irq}; + /// Our default exception handler. /// /// We end up here if an exception fires and the weak 'PROVIDE' in the link.x @@ -269,11 +462,12 @@ use cortex_ar::register::Hactlr; #[no_mangle] pub extern "C" fn _default_handler() { loop { - nop(); + core::hint::spin_loop(); } } // The Interrupt Vector Table, and some default assembly-language handler. +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" .section .vector_table,"ax",%progbits @@ -283,8 +477,8 @@ core::arch::global_asm!( ldr pc, =_start ldr pc, =_asm_undefined_handler ldr pc, =_asm_svc_handler - ldr pc, =_asm_prefetch_handler - ldr pc, =_asm_abort_handler + ldr pc, =_asm_prefetch_abort_handler + ldr pc, =_asm_data_abort_handler nop ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler @@ -381,6 +575,7 @@ macro_rules! restore_context { } // Our assembly language exception handlers +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" // Work around https://github.com/rust-lang/rust/issues/127269 @@ -396,7 +591,7 @@ core::arch::global_asm!( .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: // state save from compiled code - srsfd sp!, {und_mode} + srsfd sp!, #{und_mode} // to work out what mode we're in, we need R0 push {{r0}} // First adjust LR for two purposes: Passing the faulting instruction to the C handler, @@ -436,11 +631,11 @@ core::arch::global_asm!( // 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);` + // `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} + srsfd sp!, #{svc_mode} "#, save_context!(), r#" @@ -459,25 +654,25 @@ core::arch::global_asm!( .size _asm_default_svc_handler, . - _asm_default_svc_handler - .section .text._asm_default_abort_handler + .section .text._asm_default_data_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: + // `extern "C" fn _data_abort_handler(addr: usize);` + .global _asm_default_data_abort_handler + .type _asm_default_data_abort_handler, %function + _asm_default_data_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} + srsfd sp!, #{abt_mode} "#, save_context!(), r#" // Pass the faulting instruction address to the handler. mov r0, lr // call C handler - bl _abort_handler + bl _data_abort_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 "#, @@ -487,28 +682,28 @@ core::arch::global_asm!( str lr, [sp] // Return from the asm handler rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler - .section .text._asm_default_prefetch_handler + .section .text._asm_default_prefetch_abort_handler - // Called from the vector table when we have a prefetch exception. + // Called from the vector table when we have a prefetch abort. // Saves state and calls a C-compatible handler like - // `extern "C" fn _prefetch_handler(addr: usize);` - .global _asm_default_prefetch_handler - .type _asm_default_prefetch_handler, %function - _asm_default_prefetch_handler: + // `extern "C" fn _prefetch_abort_handler(addr: usize);` + .global _asm_default_prefetch_abort_handler + .type _asm_default_prefetch_abort_handler, %function + _asm_default_prefetch_abort_handler: // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual. subs lr, lr, #4 // state save from compiled code - srsfd sp!, {abt_mode} + srsfd sp!, #{abt_mode} "#, save_context!(), r#" // Pass the faulting instruction address to the handler. mov r0, lr // call C handler - bl _prefetch_handler + bl _prefetch_abort_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 "#, @@ -518,27 +713,39 @@ core::arch::global_asm!( str lr, [sp] // Return from the asm handler rfefd sp! - .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler + .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler .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 irq_handler();` + // `extern "C" fn _irq_handler();` .global _asm_default_irq_handler .type _asm_default_irq_handler, %function _asm_default_irq_handler: + // make sure we jump back to the right place sub lr, lr, 4 - srsfd sp!, {irq_mode} + // The hardware has copied CPSR to SPSR_irq and LR to LR_irq for us. + // Now push SPSR_irq and LR_irq to the SYS stack. + srsfd sp!, #{sys_mode} + // switch to system mode + cps #{sys_mode} + // we also need to save LR, so we can be re-entrant + push {{lr}} + // save state to the system stack (adjusting SP for alignment) "#, - save_context!(), + save_context!(), r#" // call C handler bl _irq_handler + // restore from the system stack "#, - restore_context!(), + restore_context!(), r#" + // restore LR + pop {{lr}} + // pop CPSR and LR from the stack (which also restores the mode) rfefd sp! .size _asm_default_irq_handler, . - _asm_default_irq_handler @@ -553,9 +760,9 @@ core::arch::global_asm!( .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, - irq_mode = const ProcessorMode::Irq as u8, und_mode = const ProcessorMode::Und as u8, abt_mode = const ProcessorMode::Abt as u8, + sys_mode = const ProcessorMode::Sys as u8, t_bit = const { Cpsr::new_with_raw_value(0) .with_t(true) @@ -564,7 +771,10 @@ core::arch::global_asm!( ); /// This macro expands to code to turn on the FPU -#[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))] +#[cfg(all( + any(arm_architecture = "v7-r", arm_architecture = "v8-r"), + any(target_abi = "eabihf", feature = "eabi-fpu") +))] macro_rules! fpu_enable { () => { r#" @@ -580,7 +790,10 @@ macro_rules! fpu_enable { } /// This macro expands to code that does nothing because there is no FPU -#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] +#[cfg(all( + any(arm_architecture = "v7-r", arm_architecture = "v8-r"), + not(any(target_abi = "eabihf", feature = "eabi-fpu")) +))] macro_rules! fpu_enable { () => { r#" @@ -592,6 +805,7 @@ macro_rules! fpu_enable { // Start-up code for Armv7-R (and Armv8-R once we've left EL2) // // We set up our stacks and `kmain` in system mode. +#[cfg(target_arch = "arm")] core::arch::global_asm!( r#" // Work around https://github.com/rust-lang/rust/issues/127269 diff --git a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out new file mode 100644 index 0000000..b12baf8 --- /dev/null +++ b/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/mps3-an536/reference/gic-armv8r-none-eabihf.out b/examples/mps3-an536/reference/gic-armv8r-none-eabihf.out index 56b80a0..ab68c14 100644 --- a/examples/mps3-an536/reference/gic-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/gic-armv8r-none-eabihf.out @@ -1,12 +1,19 @@ Found PERIPHBASE 0xf0000000 Creating GIC driver @ 0xf0000000 / 0xf0100000 Calling git.setup(0) -Configure SGI... +Configure low-prio SGI... +Configure high-prio SGI... gic.enable_interrupt() Enabling interrupts... CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=1 F=1 T=0 MODE=Ok(Sys) } CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=0 F=1 T=0 MODE=Ok(Sys) } -Send SGI +Send lo-prio SGI > IRQ -- IRQ handle SGI 3 +- IRQ Handling SGI 3 +- IRQ got SGI 3, sending hi-prio SGI 4 +> IRQ +- IRQ Handling SGI 4 +< IRQ +- IRQ finished sending hi-prio! < IRQ +IRQ test completed OK diff --git a/examples/mps3-an536/reference/hello-armv8r-none-eabihf.out b/examples/mps3-an536/reference/hello-armv8r-none-eabihf.out index c3d6c35..76d6f99 100644 --- a/examples/mps3-an536/reference/hello-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/hello-armv8r-none-eabihf.out @@ -3,7 +3,7 @@ PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/hello.rs", - line: 26, + line: 22, col: 5, }, can_unwind: true, diff --git a/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out index a2be48f..0443a50 100644 --- a/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out index 75f99bd..94da556 100644 --- a/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/mps3-an536/reference/svc-armv8r-none-eabihf.out b/examples/mps3-an536/reference/svc-armv8r-none-eabihf.out index fee2bd6..aa2b2b6 100644 --- a/examples/mps3-an536/reference/svc-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/svc-armv8r-none-eabihf.out @@ -1,12 +1,12 @@ x = 1, y = 2, z = 3.000 -In _svc_handler, with arg=0xabcdef -In _svc_handler, with arg=0x456789 +In svc_handler, with arg=0xabcdef +In svc_handler, with arg=0x456789 x = 1, y = 2, z = 3.000 PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/svc.rs", - line: 29, + line: 25, col: 5, }, can_unwind: true, diff --git a/examples/mps3-an536/src/bin/abt-exception-a32.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs index 3432e65..2056b03 100644 --- a/examples/mps3-an536/src/bin/abt-exception-a32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -6,7 +6,11 @@ use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; + // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; use semihosting::println; @@ -17,14 +21,7 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] -#[allow(unreachable_code)] +#[entry] fn main() -> ! { // Enable alignment check for Armv7-R. Was not required // on Cortex-A for some reason, even though the bit was not set. @@ -73,18 +70,18 @@ fn disable_alignment_check() { Sctlr::write(sctrl); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { - panic!("unexpected prefetch exception"); +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(addr: usize) -> usize { +#[exception(DataAbort)] +unsafe fn data_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. diff --git a/examples/mps3-an536/src/bin/abt-exception-t32.rs b/examples/mps3-an536/src/bin/abt-exception-t32.rs index 4048e42..033ba3f 100644 --- a/examples/mps3-an536/src/bin/abt-exception-t32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -6,7 +6,11 @@ use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; + // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; use semihosting::println; @@ -17,14 +21,7 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] -#[allow(unreachable_code)] +#[entry] fn main() -> ! { // Enable alignment check for Armv7-R. Was not required // on Cortex-A for some reason, even though the bit was not set. @@ -73,18 +70,18 @@ fn disable_alignment_check() { Sctlr::write(sctrl); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { - panic!("unexpected prefetch exception"); +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(addr: usize) -> usize { +#[exception(DataAbort)] +unsafe fn data_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. diff --git a/examples/mps3-an536/src/bin/generic_timer.rs b/examples/mps3-an536/src/bin/generic_timer.rs index 00ca739..2e53e08 100644 --- a/examples/mps3-an536/src/bin/generic_timer.rs +++ b/examples/mps3-an536/src/bin/generic_timer.rs @@ -4,21 +4,18 @@ #![no_main] // pull in our start-up code +use cortex_r_rt::entry; + +// pull in our library use mps3_an536 as _; use semihosting::println; /// The entry-point to the Rust application. /// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { - main(); - semihosting::process::exit(0); -} - -/// Let's test some timers! -fn main() { +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { use cortex_ar::generic_timer::{El1PhysicalTimer, El1VirtualTimer, GenericTimer}; let cntfrq = cortex_ar::register::Cntfrq::read().0; println!("cntfrq = {:.03} MHz", cntfrq as f32 / 1_000_000.0); @@ -59,4 +56,6 @@ fn main() { } println!("{} countdown hit zero!", name,); } + + semihosting::process::exit(0); } diff --git a/examples/mps3-an536/src/bin/gic.rs b/examples/mps3-an536/src/bin/gic.rs index ef8b331..05c834e 100644 --- a/examples/mps3-an536/src/bin/gic.rs +++ b/examples/mps3-an536/src/bin/gic.rs @@ -4,6 +4,9 @@ #![no_main] // pull in our start-up code +use cortex_r_rt::{entry, irq}; + +// pull in our library use mps3_an536 as _; use arm_gic::{ @@ -20,17 +23,13 @@ const GICD_BASE_OFFSET: usize = 0x0000_0000usize; /// Offset from PERIPHBASE for the first GIC Redistributor const GICR_BASE_OFFSET: usize = 0x0010_0000usize; -/// The entry-point to the Rust application. -/// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { - main(); -} +const SGI_INTID_LO: IntId = IntId::sgi(3); +const SGI_INTID_HI: IntId = IntId::sgi(4); -/// The main function of our Rust application. +/// The entry-point to the Rust application. /// -/// Called by [`kmain`]. +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] fn main() -> ! { // Get the GIC address by reading CBAR let periphbase = cortex_ar::register::ImpCbar::read().periphbase(); @@ -50,13 +49,17 @@ fn main() -> ! { SingleCoreGic::set_priority_mask(0x80); // Configure a Software Generated Interrupt for Core 0 - println!("Configure SGI..."); - let sgi_intid = IntId::sgi(3); - gic.set_interrupt_priority(sgi_intid, Some(0), 0x31); - gic.set_group(sgi_intid, Some(0), Group::Group1NS); + println!("Configure low-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_LO, Some(0), 0x31); + gic.set_group(SGI_INTID_LO, Some(0), Group::Group1NS); + + println!("Configure high-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_HI, Some(0), 0x10); + gic.set_group(SGI_INTID_HI, Some(0), Group::Group1NS); println!("gic.enable_interrupt()"); - gic.enable_interrupt(sgi_intid, Some(0), true); + gic.enable_interrupt(SGI_INTID_LO, Some(0), true); + gic.enable_interrupt(SGI_INTID_HI, Some(0), true); println!("Enabling interrupts..."); dump_cpsr(); @@ -66,9 +69,9 @@ fn main() -> ! { dump_cpsr(); // Send it - println!("Send SGI"); + println!("Send lo-prio SGI"); SingleCoreGic::send_sgi( - sgi_intid, + SGI_INTID_LO, SgiTarget::List { affinity3: 0, affinity2: 0, @@ -81,6 +84,8 @@ fn main() -> ! { cortex_ar::asm::nop(); } + println!("IRQ test completed OK"); + semihosting::process::exit(0); } @@ -89,11 +94,33 @@ fn dump_cpsr() { println!("CPSR: {:?}", cpsr); } -#[no_mangle] -unsafe extern "C" fn _irq_handler() { +#[irq] +fn irq_handler() { println!("> IRQ"); while let Some(int_id) = SingleCoreGic::get_and_acknowledge_interrupt() { - println!("- IRQ handle {:?}", int_id); + // let's go re-entrant + unsafe { + cortex_ar::interrupt::enable(); + } + println!("- IRQ Handling {:?}", int_id); + if int_id == SGI_INTID_LO { + println!( + "- IRQ got {:?}, sending hi-prio {:?}", + SGI_INTID_LO, SGI_INTID_HI + ); + SingleCoreGic::send_sgi( + SGI_INTID_HI, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b1, + }, + ); + println!("- IRQ finished sending hi-prio!"); + } + // turn interrupts off again + cortex_ar::interrupt::disable(); SingleCoreGic::end_interrupt(int_id); } println!("< IRQ"); diff --git a/examples/mps3-an536/src/bin/hello.rs b/examples/mps3-an536/src/bin/hello.rs index 52b27d4..4608feb 100644 --- a/examples/mps3-an536/src/bin/hello.rs +++ b/examples/mps3-an536/src/bin/hello.rs @@ -4,21 +4,17 @@ #![no_main] // pull in our start-up code +use cortex_r_rt::entry; + +// pull in our library use mps3_an536 as _; use semihosting::println; /// The entry-point to the Rust application. /// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { - main(); -} - -/// The main function of our Rust application. -/// -/// Called by [`kmain`]. +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] fn main() -> ! { let x = 1.0f64; let y = x * 2.0; diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index 58c404a..cb331b7 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -1,4 +1,4 @@ -//! Example triggering a prefetch exception. +//! Example triggering a prefetch abort exception. #![no_std] #![no_main] @@ -8,6 +8,9 @@ use cortex_ar::register::{Ifar, Ifsr}; use semihosting::println; // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; static COUNTER: AtomicU32 = AtomicU32::new(0); @@ -15,14 +18,14 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - println!("Hello, this is a prefetch exception example"); +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual unsafe { - // trigger an prefetch exception, from A32 (Arm) mode + // trigger an prefetch abort exception, from A32 (Arm) mode bkpt_from_a32(); } @@ -49,13 +52,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -87,12 +90,12 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { } _ => { // we've faulted thrice - time to quit - panic!("_prefetch_handler called too often"); + panic!("prefetch_handler called too often"); } } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data 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 6eb02a6..6b90c4f 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -1,4 +1,4 @@ -//! Example triggering a prefetch exception. +//! Example triggering a prefetch abort exception. #![no_std] #![no_main] @@ -8,6 +8,9 @@ use cortex_ar::register::{Ifar, Ifsr}; use semihosting::println; // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; static COUNTER: AtomicU32 = AtomicU32::new(0); @@ -15,14 +18,14 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - println!("Hello, this is a prefetch exception example"); +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual unsafe { - // trigger an prefetch exception, from T32 (Thumb) mode + // trigger an prefetch abort exception, from T32 (Thumb) mode bkpt_from_t32(); } @@ -49,13 +52,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -90,12 +93,12 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { } _ => { // we've faulted thrice - time to quit - panic!("_prefetch_handler called too often"); + panic!("prefetch_handler called too often"); } } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/mps3-an536/src/bin/registers.rs b/examples/mps3-an536/src/bin/registers.rs index 97ae37c..f22453d 100644 --- a/examples/mps3-an536/src/bin/registers.rs +++ b/examples/mps3-an536/src/bin/registers.rs @@ -4,22 +4,18 @@ #![no_main] // pull in our start-up code +use cortex_r_rt::entry; + +// pull in our library use mps3_an536 as _; use semihosting::println; /// The entry-point to the Rust application. /// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { - main(); -} - -/// The entry-point to the Rust application. -/// -/// Called by [`kmain`]. -pub fn main() -> ! { +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { chip_info(); #[cfg(arm_architecture = "v7-r")] mpu_pmsa_v7(); diff --git a/examples/mps3-an536/src/bin/smp_test.rs b/examples/mps3-an536/src/bin/smp_test.rs index 6155787..ebdaaa1 100644 --- a/examples/mps3-an536/src/bin/smp_test.rs +++ b/examples/mps3-an536/src/bin/smp_test.rs @@ -14,6 +14,9 @@ use core::cell::{RefCell, UnsafeCell}; use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; // pull in our start-up code +use cortex_r_rt::entry; + +// pull in our library use mps3_an536 as _; use semihosting::println; @@ -58,9 +61,9 @@ const CS_MUTEX_LOOPS: u32 = 1000; /// The entry-point to the Rust application. /// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { let fpga_led = 0xE020_2000 as *mut u32; extern "C" { static mut _core1_stack_pointer: usize; diff --git a/examples/mps3-an536/src/bin/svc.rs b/examples/mps3-an536/src/bin/svc.rs index 2483692..9877d0c 100644 --- a/examples/mps3-an536/src/bin/svc.rs +++ b/examples/mps3-an536/src/bin/svc.rs @@ -4,22 +4,18 @@ #![no_main] // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; use semihosting::println; /// The entry-point to the Rust application. /// -/// It is called by the start-up code in `cortex-m-rt`. -#[no_mangle] -pub extern "C" fn kmain() { - main(); -} - -/// The main function of our Rust application. -/// -/// Called by [`kmain`]. -pub fn main() -> ! { +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { let x = 1; let y = x + 1; let z = (y as f64) * 1.5; @@ -30,9 +26,9 @@ pub fn main() -> ! { } /// This is our SVC exception handler -#[no_mangle] -unsafe extern "C" fn _svc_handler(arg: u32) { - println!("In _svc_handler, with arg={:#06x}", arg); +#[exception(SupervisorCall)] +fn svc_handler(arg: u32) { + println!("In svc_handler, with arg={:#06x}", arg); if arg == 0xABCDEF { // test nested SVC calls cortex_ar::svc!(0x456789); diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index 5862559..4a5be80 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -7,6 +7,9 @@ use core::sync::atomic::{AtomicU32, Ordering}; use semihosting::println; // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; static COUNTER: AtomicU32 = AtomicU32::new(0); @@ -14,8 +17,8 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { +#[entry] +fn main() -> ! { println!("Hello, this is a undef exception example"); unsafe { @@ -46,13 +49,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -84,7 +87,7 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data 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 fe6e24f..e879290 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -7,6 +7,9 @@ use core::sync::atomic::{AtomicU32, Ordering}; use semihosting::println; // pull in our start-up code +use cortex_r_rt::{entry, exception}; + +// pull in our library use mps3_an536 as _; static COUNTER: AtomicU32 = AtomicU32::new(0); @@ -14,8 +17,8 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { +#[entry] +fn main() -> ! { println!("Hello, this is a undef exception example"); unsafe { @@ -46,13 +49,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -87,7 +90,7 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/mps3-an536/src/lib.rs b/examples/mps3-an536/src/lib.rs index ebe6185..289b1c7 100644 --- a/examples/mps3-an536/src/lib.rs +++ b/examples/mps3-an536/src/lib.rs @@ -2,9 +2,6 @@ #![no_std] -// Need this to bring in the start-up function -use cortex_r_rt as _; - #[cfg(not(arm_architecture = "v8-r"))] compile_error!("This example is only compatible to the ARMv8-R architecture"); diff --git a/examples/versatileab/reference/abt-exception-armv7a-none-eabi.out b/examples/versatileab/reference/abt-exception-armv7a-none-eabi.out new file mode 100644 index 0000000..703128d --- /dev/null +++ b/examples/versatileab/reference/abt-exception-armv7a-none-eabi.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out new file mode 100644 index 0000000..703128d --- /dev/null +++ b/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out b/examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out new file mode 100644 index 0000000..703128d --- /dev/null +++ b/examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/versatileab/reference/hello-armv7a-none-eabi.out b/examples/versatileab/reference/hello-armv7a-none-eabi.out index fdb05bb..04a1a27 100644 --- a/examples/versatileab/reference/hello-armv7a-none-eabi.out +++ b/examples/versatileab/reference/hello-armv7a-none-eabi.out @@ -3,7 +3,7 @@ PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/hello.rs", - line: 25, + line: 19, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/reference/hello-armv7r-none-eabi.out b/examples/versatileab/reference/hello-armv7r-none-eabi.out index fdb05bb..04a1a27 100644 --- a/examples/versatileab/reference/hello-armv7r-none-eabi.out +++ b/examples/versatileab/reference/hello-armv7r-none-eabi.out @@ -3,7 +3,7 @@ PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/hello.rs", - line: 25, + line: 19, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/reference/hello-armv7r-none-eabihf.out b/examples/versatileab/reference/hello-armv7r-none-eabihf.out index fdb05bb..04a1a27 100644 --- a/examples/versatileab/reference/hello-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/hello-armv7r-none-eabihf.out @@ -3,7 +3,7 @@ PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/hello.rs", - line: 25, + line: 19, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out index e013c87..1929eee 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out index e013c87..1929eee 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out index e013c87..1929eee 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out index fd0da4c..2b5664f 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out index fd0da4c..2b5664f 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out index fd0da4c..2b5664f 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out @@ -1,4 +1,4 @@ -Hello, this is a prefetch exception example +Hello, this is a prefetch abort exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) diff --git a/examples/versatileab/reference/svc-armv7a-none-eabi.out b/examples/versatileab/reference/svc-armv7a-none-eabi.out index 79353f6..688d0d7 100644 --- a/examples/versatileab/reference/svc-armv7a-none-eabi.out +++ b/examples/versatileab/reference/svc-armv7a-none-eabi.out @@ -1,12 +1,12 @@ x = 1, y = 2, z = 3.000 -In _svc_handler, with arg=0xabcdef -In _svc_handler, with arg=0x456789 +In svc_handler, with arg=0xabcdef +In svc_handler, with arg=0x456789 x = 1, y = 2, z = 3.000 PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/svc.rs", - line: 28, + line: 22, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/reference/svc-armv7r-none-eabi.out b/examples/versatileab/reference/svc-armv7r-none-eabi.out index 79353f6..688d0d7 100644 --- a/examples/versatileab/reference/svc-armv7r-none-eabi.out +++ b/examples/versatileab/reference/svc-armv7r-none-eabi.out @@ -1,12 +1,12 @@ x = 1, y = 2, z = 3.000 -In _svc_handler, with arg=0xabcdef -In _svc_handler, with arg=0x456789 +In svc_handler, with arg=0xabcdef +In svc_handler, with arg=0x456789 x = 1, y = 2, z = 3.000 PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/svc.rs", - line: 28, + line: 22, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/reference/svc-armv7r-none-eabihf.out b/examples/versatileab/reference/svc-armv7r-none-eabihf.out index 79353f6..688d0d7 100644 --- a/examples/versatileab/reference/svc-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/svc-armv7r-none-eabihf.out @@ -1,12 +1,12 @@ x = 1, y = 2, z = 3.000 -In _svc_handler, with arg=0xabcdef -In _svc_handler, with arg=0x456789 +In svc_handler, with arg=0xabcdef +In svc_handler, with arg=0x456789 x = 1, y = 2, z = 3.000 PANIC: PanicInfo { message: I am an example panic, location: Location { file: "src/bin/svc.rs", - line: 28, + line: 22, col: 5, }, can_unwind: true, diff --git a/examples/versatileab/src/bin/abt-exception-a32.rs b/examples/versatileab/src/bin/abt-exception-a32.rs index 6a31e9c..d953971 100644 --- a/examples/versatileab/src/bin/abt-exception-a32.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -6,8 +6,9 @@ use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; + // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; use semihosting::println; @@ -17,14 +18,7 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] -#[allow(unreachable_code)] +#[entry] fn main() -> ! { // Enable alignment check for Armv7-R. Was not required // on Cortex-A for some reason, even though the bit was not set. @@ -73,18 +67,18 @@ fn disable_alignment_check() { Sctlr::write(sctrl); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { - panic!("unexpected prefetch exception"); +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(addr: usize) -> usize { +#[exception(DataAbort)] +unsafe fn data_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. diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs index cf5dce3..fde37a1 100644 --- a/examples/versatileab/src/bin/abt-exception-t32.rs +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; use semihosting::println; @@ -17,14 +17,7 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] -#[allow(unreachable_code)] +#[entry] fn main() -> ! { // Enable alignment check for Armv7-R. Was not required // on Cortex-A for some reason, even though the bit was not set. @@ -73,18 +66,18 @@ fn disable_alignment_check() { Sctlr::write(sctrl); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { - panic!("unexpected prefetch exception"); +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(addr: usize) -> usize { +#[exception(DataAbort)] +unsafe fn data_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. diff --git a/examples/versatileab/src/bin/hello.rs b/examples/versatileab/src/bin/hello.rs index 074cdc0..7de4da4 100644 --- a/examples/versatileab/src/bin/hello.rs +++ b/examples/versatileab/src/bin/hello.rs @@ -4,21 +4,15 @@ #![no_main] // pull in our start-up code -use versatileab as _; +use versatileab; use semihosting::println; /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] -fn main() -> ! { +#[versatileab::rt::entry] +fn my_main() -> ! { let x = 1.0f64; let y = x * 2.0; println!("Hello, this is semihosting! x = {:0.3}, y = {:0.3}", x, y); diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index 7865d5c..bd80537 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -1,4 +1,4 @@ -//! Example triggering a prefetch exception. +//! Example triggering a prefetch abort exception. #![no_std] #![no_main] @@ -8,21 +8,21 @@ use cortex_ar::register::{Ifar, Ifsr}; use semihosting::println; // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - println!("Hello, this is a prefetch exception example"); +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual unsafe { - // trigger an prefetch exception, from A32 (Arm) mode + // trigger an prefetch abort exception, from A32 (Arm) mode bkpt_from_a32(); } @@ -49,13 +49,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -87,12 +87,12 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { } _ => { // we've faulted thrice - time to quit - panic!("_prefetch_handler called too often"); + panic!("_prefetch_abort_handler called too often"); } } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index e4303a7..c772567 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -1,4 +1,4 @@ -//! Example triggering a prefetch exception. +//! Example triggering a prefetch abort exception. #![no_std] #![no_main] @@ -8,21 +8,21 @@ use cortex_ar::register::{Ifar, Ifsr}; use semihosting::println; // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - println!("Hello, this is a prefetch exception example"); +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual unsafe { - // trigger an prefetch exception, from T32 (Thumb) mode + // trigger an prefetch abort exception, from T32 (Thumb) mode bkpt_from_t32(); } @@ -49,13 +49,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -90,12 +90,12 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { } _ => { // we've faulted thrice - time to quit - panic!("_prefetch_handler called too often"); + panic!("prefetch_handler called too often"); } } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/versatileab/src/bin/registers.rs b/examples/versatileab/src/bin/registers.rs index f897bb4..327c1d8 100644 --- a/examples/versatileab/src/bin/registers.rs +++ b/examples/versatileab/src/bin/registers.rs @@ -4,22 +4,14 @@ #![no_main] // pull in our start-up code -use versatileab as _; +use versatileab::rt::entry; use semihosting::println; /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up code in `cortex-m-rt`. -#[export_name = "main"] +#[entry] fn main() -> ! { chip_info(); test_changing_sctlr(); diff --git a/examples/versatileab/src/bin/svc.rs b/examples/versatileab/src/bin/svc.rs index 83b33da..6cb6e2b 100644 --- a/examples/versatileab/src/bin/svc.rs +++ b/examples/versatileab/src/bin/svc.rs @@ -4,20 +4,14 @@ #![no_main] // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; use semihosting::println; /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -/// The main function of our Rust application. -#[export_name = "main"] +#[entry] fn main() -> ! { let x = 1; let y = x + 1; @@ -29,9 +23,9 @@ fn main() -> ! { } /// This is our SVC exception handler -#[no_mangle] -unsafe extern "C" fn _svc_handler(arg: u32) { - println!("In _svc_handler, with arg={:#06x}", arg); +#[exception(SupervisorCall)] +fn svc_handler(arg: u32) { + println!("In svc_handler, with arg={:#06x}", arg); if arg == 0xABCDEF { // test nested SVC calls cortex_ar::svc!(0x456789); diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index dc90e31..821f4dd 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -7,15 +7,15 @@ use core::sync::atomic::{AtomicU32, Ordering}; use semihosting::println; // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { +#[entry] +fn main() -> ! { println!("Hello, this is a undef exception example"); unsafe { @@ -46,13 +46,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -84,7 +84,7 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index 8c8ac68..4de5e58 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -7,15 +7,15 @@ use core::sync::atomic::{AtomicU32, Ordering}; use semihosting::println; // pull in our start-up code -use versatileab as _; +use versatileab::rt::{entry, exception}; static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. /// /// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { +#[entry] +fn main() -> ! { println!("Hello, this is a undef exception example"); unsafe { @@ -46,13 +46,13 @@ core::arch::global_asm!( "# ); -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -87,7 +87,7 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { - panic!("unexpected abort exception"); +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); } diff --git a/examples/versatileab/src/lib.rs b/examples/versatileab/src/lib.rs index d931296..dc8e177 100644 --- a/examples/versatileab/src/lib.rs +++ b/examples/versatileab/src/lib.rs @@ -4,9 +4,10 @@ // Need this to bring in the start-up function #[cfg(arm_profile = "a")] -use cortex_a_rt as _; +pub use cortex_a_rt as rt; + #[cfg(arm_profile = "r")] -use cortex_r_rt as _; +pub use cortex_r_rt as rt; #[cfg(arm_architecture = "v8-r")] compile_error!("This example/board is not compatible with the ARMv8-R architecture");