From 20a912a0c02e6b1f5bb556255bcf72dd8eecbfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 4 Dec 2023 17:35:43 +0100 Subject: [PATCH 1/2] vector-like exception handlers --- .github/workflows/riscv-rt.yaml | 8 +- riscv-rt/CHANGELOG.md | 3 + riscv-rt/link.x.in | 15 +++ riscv-rt/src/lib.rs | 170 +++++++++++++++++++++----------- 4 files changed, 133 insertions(+), 63 deletions(-) diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 9662a4c9..e2c5291d 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master ] + branches: [ master, vectored-exceptions ] pull_request: merge_group: @@ -35,11 +35,11 @@ jobs: targets: ${{ matrix.target }} - name: Build (no features) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} - - name : Build example (s-mode) + - name : Build (s-mode) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode - - name : Build example (single-hart) + - name : Build (single-hart) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=single-hart - - name: Build example (all features) + - name: Build (all features) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --all-features # Job to check that all the builds succeeded diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index f4bebca6..99175de4 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Static array for vectored-like handling of exceptions - New GitHub workflow for checking invalid labels in PRs - New GitHub workflow for checking modifications on CHANGELOG.md - New GitHub workflow for checking clippy lints in PRs @@ -16,6 +17,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Removed U-mode interrupts to align with latest RISC-V specification +- Changed `Vector` union. Now, it uses `Option`, which is more idiomatic in Rust - Removed riscv-target dependency for build - Upgrade rust-version to 1.60 - Cargo workspace for riscv and riscv-rt diff --git a/riscv-rt/link.x.in b/riscv-rt/link.x.in index 0df3f2da..75ee3987 100644 --- a/riscv-rt/link.x.in +++ b/riscv-rt/link.x.in @@ -28,6 +28,21 @@ PROVIDE(_max_hart_id = 0); PROVIDE(_hart_stack_size = 2K); PROVIDE(_heap_size = 0); +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler);; +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + PROVIDE(UserSoft = DefaultHandler); PROVIDE(SupervisorSoft = DefaultHandler); PROVIDE(MachineSoft = DefaultHandler); diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index fcc77f9f..6e4ba835 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -245,9 +245,47 @@ //! //! Default implementation of this function wakes hart 0 and busy-loops all the other harts. //! +//! +//! ### Core exception handlers +//! +//! This functions are called when corresponding exception occurs. +//! You can define an exception handler with one of the following names: +//! * `InstructionMisaligned` +//! * `InstructionFault` +//! * `IllegalInstruction` +//! * `Breakpoint` +//! * `LoadMisaligned` +//! * `LoadFault` +//! * `StoreMisaligned` +//! * `StoreFault` +//! * `UserEnvCall` +//! * `SupervisorEnvCall` +//! * `MachineEnvCall` +//! * `InstructionPageFault` +//! * `LoadPageFault` +//! * `StorePageFault` +//! +//! For example: +//! ``` no_run +//! #[export_name = "MachineEnvCall"] +//! fn custom_menv_call_handler(trap_frame: &riscv_rt::TrapFrame) { +//! // ... +//! } +//! ``` +//! or +//! ``` no_run +//! #[no_mangle] +//! fn MachineEnvCall(trap_frame: &riscv_rt::TrapFrame) -> ! { +//! // ... +//! } +//! ``` +//! +//! If exception handler is not explicitly defined, `ExceptionHandler` is called. +//! //! ### `ExceptionHandler` //! -//! This function is called when exception is occured. The exception reason can be decoded from the +//! This function is called when exception without defined exception handler is occured. +//! The exception reason can be decoded from the //! `mcause`/`scause` register. //! //! This function can be redefined in the following way: @@ -561,15 +599,27 @@ pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { } let cause = xcause::read(); + let code = cause.code(); if cause.is_exception() { - ExceptionHandler(&*trap_frame) - } else if cause.code() < __INTERRUPTS.len() { - let h = &__INTERRUPTS[cause.code()]; - if h.reserved == 0 { - DefaultHandler(); + let trap_frame = &*trap_frame; + if code < __EXCEPTIONS.len() { + let h = &__EXCEPTIONS[code]; + if let Some(handler) = h { + handler(trap_frame); + } else { + ExceptionHandler(trap_frame); + } } else { - (h.handler)(); + ExceptionHandler(trap_frame); + } + ExceptionHandler(trap_frame) + } else if code < __INTERRUPTS.len() { + let h = &__INTERRUPTS[code]; + if let Some(handler) = h { + handler(); + } else { + DefaultHandler(); } } else { DefaultHandler(); @@ -589,7 +639,7 @@ pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! { #[doc(hidden)] #[no_mangle] -#[allow(unused_variables, non_snake_case)] +#[allow(non_snake_case)] pub fn DefaultInterruptHandler() { loop { // Prevent this from turning into a UDF instruction @@ -598,76 +648,78 @@ pub fn DefaultInterruptHandler() { } } -/* Interrupts */ -#[doc(hidden)] -pub enum Interrupt { - UserSoft, - SupervisorSoft, - MachineSoft, - UserTimer, - SupervisorTimer, - MachineTimer, - UserExternal, - SupervisorExternal, - MachineExternal, +extern "C" { + fn InstructionMisaligned(trap_frame: &TrapFrame); + fn InstructionFault(trap_frame: &TrapFrame); + fn IllegalInstruction(trap_frame: &TrapFrame); + fn Breakpoint(trap_frame: &TrapFrame); + fn LoadMisaligned(trap_frame: &TrapFrame); + fn LoadFault(trap_frame: &TrapFrame); + fn StoreMisaligned(trap_frame: &TrapFrame); + fn StoreFault(trap_frame: &TrapFrame); + fn UserEnvCall(trap_frame: &TrapFrame); + fn SupervisorEnvCall(trap_frame: &TrapFrame); + fn MachineEnvCall(trap_frame: &TrapFrame); + fn InstructionPageFault(trap_frame: &TrapFrame); + fn LoadPageFault(trap_frame: &TrapFrame); + fn StorePageFault(trap_frame: &TrapFrame); } -pub use self::Interrupt as interrupt; +#[doc(hidden)] +#[no_mangle] +pub static __EXCEPTIONS: [Option; 16] = [ + Some(InstructionMisaligned), + Some(InstructionFault), + Some(IllegalInstruction), + Some(Breakpoint), + Some(LoadMisaligned), + Some(LoadFault), + Some(StoreMisaligned), + Some(StoreFault), + Some(UserEnvCall), + Some(SupervisorEnvCall), + None, + Some(MachineEnvCall), + Some(InstructionPageFault), + Some(LoadPageFault), + None, + Some(StorePageFault), +]; extern "C" { - fn UserSoft(); fn SupervisorSoft(); fn MachineSoft(); - fn UserTimer(); fn SupervisorTimer(); fn MachineTimer(); - fn UserExternal(); fn SupervisorExternal(); fn MachineExternal(); } -#[doc(hidden)] -pub union Vector { - pub handler: unsafe extern "C" fn(), - pub reserved: usize, -} - #[doc(hidden)] #[no_mangle] -pub static __INTERRUPTS: [Vector; 12] = [ - Vector { handler: UserSoft }, - Vector { - handler: SupervisorSoft, - }, - Vector { reserved: 0 }, - Vector { - handler: MachineSoft, - }, - Vector { handler: UserTimer }, - Vector { - handler: SupervisorTimer, - }, - Vector { reserved: 0 }, - Vector { - handler: MachineTimer, - }, - Vector { - handler: UserExternal, - }, - Vector { - handler: SupervisorExternal, - }, - Vector { reserved: 0 }, - Vector { - handler: MachineExternal, - }, +pub static __INTERRUPTS: [Option; 12] = [ + None, + Some(SupervisorSoft), + None, + Some(MachineSoft), + None, + Some(SupervisorTimer), + None, + Some(MachineTimer), + None, + Some(SupervisorExternal), + None, + Some(MachineExternal), ]; +/// Default implementation of `_pre_init` does nothing. +/// Users can override this function with the [`#[pre_init]`] macro. #[doc(hidden)] #[no_mangle] #[rustfmt::skip] -pub unsafe extern "Rust" fn default_pre_init() {} +pub extern "Rust" fn default_pre_init() {} +/// Default implementation of `_mp_hook` wakes hart 0 and busy-loops all the other harts. #[doc(hidden)] #[no_mangle] #[rustfmt::skip] @@ -681,7 +733,7 @@ pub extern "Rust" fn default_mp_hook(hartid: usize) -> bool { } } -/// Default implementation of `_setup_interrupts` that sets `mtvec`/`stvec` to a trap handler address. +/// Default implementation of `_setup_interrupts` sets `mtvec`/`stvec` to the address of `_start_trap`. #[doc(hidden)] #[no_mangle] #[rustfmt::skip] From cd88bb88f639cf79968e7661a0c274d45cf29380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 4 Dec 2023 17:52:32 +0100 Subject: [PATCH 2/2] ready for PR --- .github/workflows/riscv-rt.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index e2c5291d..8e716b52 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, vectored-exceptions ] + branches: [ master ] pull_request: merge_group: