Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv-rt: Rework on exception and interrupt handling #161

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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