Skip to content

Commit

Permalink
Merge pull request #161 from rust-embedded/vectored-exceptions
Browse files Browse the repository at this point in the history
`riscv-rt`: Rework on exception and interrupt handling
  • Loading branch information
romancardenas authored Dec 13, 2023
2 parents c579937 + cd88bb8 commit 5c447b8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 62 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/riscv-rt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ 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
- Optional cargo feature `single-hart` for single CPU targets

### Changed

- Removed U-mode interrupts to align with latest RISC-V specification
- Changed `Vector` union. Now, it uses `Option<fn>`, 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
Expand Down
15 changes: 15 additions & 0 deletions riscv-rt/link.x.in
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
170 changes: 111 additions & 59 deletions riscv-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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<unsafe extern "C" fn(&TrapFrame)>; 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<unsafe extern "C" fn()>; 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]
Expand All @@ -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]
Expand Down

0 comments on commit 5c447b8

Please sign in to comment.