Browse Source

Implement interrupt and exception handling

Vadim Kaushan 5 years ago
parent
commit
f233381982
2 changed files with 203 additions and 13 deletions
  1. 12 1
      riscv-rt/link.x
  2. 191 12
      riscv-rt/src/lib.rs

+ 12 - 1
riscv-rt/link.x

@@ -4,7 +4,18 @@ PROVIDE(_max_hart_id = 0);
 PROVIDE(_hart_stack_size = 2K);
 PROVIDE(_heap_size = 0);
 
-PROVIDE(trap_handler = default_trap_handler);
+PROVIDE(UserSoft = DefaultHandler);
+PROVIDE(SupervisorSoft = DefaultHandler);
+PROVIDE(MachineSoft = DefaultHandler);
+PROVIDE(UserTimer = DefaultHandler);
+PROVIDE(SupervisorTimer = DefaultHandler);
+PROVIDE(MachineTimer = DefaultHandler);
+PROVIDE(UserExternal = DefaultHandler);
+PROVIDE(SupervisorExternal = DefaultHandler);
+PROVIDE(MachineExternal = DefaultHandler);
+
+PROVIDE(DefaultHandler = DefaultInterruptHandler);
+PROVIDE(ExceptionHandler = DefaultExceptionHandler);
 
 /* # Pre-initialization function */
 /* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,

+ 191 - 12
riscv-rt/src/lib.rs

@@ -249,6 +249,84 @@
 //! ```
 //!
 //! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
+//!
+//! ### `ExceptionHandler`
+//!
+//! This function is called when exception is occured. The exception reason can be decoded from the
+//! `mcause` register.
+//!
+//! This function can be redefined in the following way:
+//!
+//! ``` no_run
+//! #[export_name = "ExceptionHandler"]
+//! fn custom_exception_handler(trap_frame: &riscv_rt::TrapFrame) -> ! {
+//!     // ...
+//! }
+//! ```
+//! or
+//! ``` no_run
+//! #[no_mangle]
+//! fn ExceptionHandler(trap_frame: &riscv_rt::TrapFrame) -> ! {
+//!     // ...
+//! }
+//! ```
+//!
+//! Default implementation of this function stucks in a busy-loop.
+//!
+//!
+//! ### Core interrupt handlers
+//!
+//! This functions are called when corresponding interrupt is occured.
+//! You can define an interrupt handler with one of the following names:
+//! * `UserSoft`
+//! * `SupervisorSoft`
+//! * `MachineSoft`
+//! * `UserTimer`
+//! * `SupervisorTimer`
+//! * `MachineTimer`
+//! * `UserExternal`
+//! * `SupervisorExternal`
+//! * `MachineExternal`
+//!
+//! For example:
+//! ``` no_run
+//! #[export_name = "MachineTimer"]
+//! fn custom_timer_handler() {
+//!     // ...
+//! }
+//! ```
+//! or
+//! ``` no_run
+//! #[no_mangle]
+//! fn MachineTimer() {
+//!     // ...
+//! }
+//! ```
+//!
+//! If interrupt handler is not explicitly defined, `DefaultHandler` is called.
+//!
+//! ### `DefaultHandler`
+//!
+//! This function is called when interrupt without defined interrupt handler is occured.
+//! The interrupt reason can be decoded from the `mcause` register.
+//!
+//! This function can be redefined in the following way:
+//!
+//! ``` no_run
+//! #[export_name = "DefaultHandler"]
+//! fn custom_interrupt_handler() {
+//!     // ...
+//! }
+//! ```
+//! or
+//! ``` no_run
+//! #[no_mangle]
+//! fn DefaultHandler() {
+//!     // ...
+//! }
+//! ```
+//!
+//! Default implementation of this function stucks in a busy-loop.
 
 // NOTE: Adapted from cortex-m/src/lib.rs
 #![no_std]
@@ -261,7 +339,7 @@ extern crate r0;
 
 pub use macros::{entry, pre_init};
 
-use riscv::register::mstatus;
+use riscv::register::mcause;
 
 #[export_name = "error: riscv-rt appears more than once in the dependency graph"]
 #[doc(hidden)]
@@ -310,33 +388,134 @@ pub unsafe extern "C" fn start_rust() -> ! {
     main();
 }
 
+/// Registers saved in trap handler
+#[allow(missing_docs)]
+#[repr(C)]
+pub struct TrapFrame {
+    pub ra: usize,
+    pub t0: usize,
+    pub t1: usize,
+    pub t2: usize,
+    pub t3: usize,
+    pub t4: usize,
+    pub t5: usize,
+    pub t6: usize,
+    pub a0: usize,
+    pub a1: usize,
+    pub a2: usize,
+    pub a3: usize,
+    pub a4: usize,
+    pub a5: usize,
+    pub a6: usize,
+    pub a7: usize,
+}
+
 
 /// Trap entry point rust (_start_trap_rust)
 ///
-/// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
-/// if it's an interrupt or an exception. The result is converted to an element
-/// of the Interrupt or Exception enum and passed to handle_interrupt or
-/// handle_exception.
+/// `mcause` is read to determine the cause of the trap. XLEN-1 bit indicates
+/// if it's an interrupt or an exception. The result is examined and ExceptionHandler
+/// or one of the core interrupt handlers is called.
 #[link_section = ".trap.rust"]
 #[export_name = "_start_trap_rust"]
-pub extern "C" fn start_trap_rust() {
+pub extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
     extern "C" {
-        fn trap_handler();
+        fn ExceptionHandler(trap_frame: &TrapFrame) -> !;
+        fn DefaultHandler();
     }
 
     unsafe {
-        // dispatch trap to handler
-        trap_handler();
+        let cause = mcause::read();
+        if cause.is_exception() {
+            ExceptionHandler(&*trap_frame)
+        } else {
+            let code = cause.code();
+            if code < __INTERRUPTS.len() {
+                let h = &__INTERRUPTS[code];
+                if h.reserved == 0 {
+                    DefaultHandler();
+                } else {
+                    (h.handler)();
+                }
+            } else {
+                DefaultHandler();
+            }
+        }
+    }
+}
 
-        // mstatus, remain in M-mode after mret
-        mstatus::set_mpp(mstatus::MPP::Machine);
+#[doc(hidden)]
+#[no_mangle]
+#[allow(unused_variables, non_snake_case)]
+pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! {
+    loop {
+        // Prevent this from turning into a UDF instruction
+        // see rust-lang/rust#28728 for details
+        continue;
     }
 }
 
+#[doc(hidden)]
+#[no_mangle]
+#[allow(unused_variables, non_snake_case)]
+pub fn DefaultInterruptHandler() {
+    loop {
+        // Prevent this from turning into a UDF instruction
+        // see rust-lang/rust#28728 for details
+        continue;
+    }
+}
+
+/* Interrupts */
+#[doc(hidden)]
+pub enum Interrupt {
+    UserSoft,
+    SupervisorSoft,
+    MachineSoft,
+    UserTimer,
+    SupervisorTimer,
+    MachineTimer,
+    UserExternal,
+    SupervisorExternal,
+    MachineExternal,
+}
+
+pub use self::Interrupt as interrupt;
+
+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 {
+    handler: unsafe extern "C" fn(),
+    reserved: usize,
+}
 
 #[doc(hidden)]
 #[no_mangle]
-pub fn default_trap_handler() {}
+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 },
+];
 
 #[doc(hidden)]
 #[no_mangle]