diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 0c20eb31..10a27b3b 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, riscv-rt-asm ] + branches: [ master ] pull_request: merge_group: @@ -39,6 +39,8 @@ jobs: 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 (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 (v-trap) + run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=v-trap - 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 diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index c7b2a792..14cfd361 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add `pre_init_trap` to detect early errors during the boot process. +- Add `v-trap` feature to enable interrupt handling in vectored mode. +- Add `interrupt` proc macro to help defining interrupt handlers. +If `v-trap` feature is enabled, this macro also generates its corresponding trap. ### Changed @@ -17,6 +20,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use `weak` symbols for functions such as `_mp_hook` or `_start_trap` - `abort` is now `weak`, so it is possible to link third-party libraries including this symbol. - Made `cfg` variable selection more robust for custom targets +- `_start_trap_rust` now only deals with exceptions. When an interrupt is detected, it now calls +to `_dispatch_interrupt`. ### Removed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 1c33bea1..319891f8 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -12,13 +12,14 @@ license = "ISC" edition = "2021" links = "riscv-rt" # Prevent multiple versions of riscv-rt being linked -[features] -s-mode = [] -single-hart = [] - [dependencies] riscv = {path = "../riscv", version = "0.11.1"} riscv-rt-macros = { path = "macros", version = "0.2.1" } [dev-dependencies] panic-halt = "0.2.0" + +[features] +s-mode = ["riscv-rt-macros/s-mode"] +single-hart = [] +v-trap = ["riscv-rt-macros/v-trap"] diff --git a/riscv-rt/examples/empty.rs b/riscv-rt/examples/empty.rs index 5e4d2384..b770cba0 100644 --- a/riscv-rt/examples/empty.rs +++ b/riscv-rt/examples/empty.rs @@ -4,10 +4,16 @@ extern crate panic_halt; extern crate riscv_rt; -use riscv_rt::entry; +use riscv_rt::{entry, interrupt}; #[entry] fn main() -> ! { // do something here loop {} } + +#[interrupt] +fn MachineSoft() { + // do something here + loop {} +} diff --git a/riscv-rt/link.x.in b/riscv-rt/link.x.in index 720d5719..fc73f8ac 100644 --- a/riscv-rt/link.x.in +++ b/riscv-rt/link.x.in @@ -28,6 +28,28 @@ PROVIDE(_max_hart_id = 0); PROVIDE(_hart_stack_size = 2K); PROVIDE(_heap_size = 0); +/** TRAP ENTRY POINTS **/ + +/* Default trap entry point. The riscv-rt crate provides a weak alias of this function, + which saves caller saved registers, calls _start_trap_rust, restores caller saved registers + and then returns. Users can override this alias by defining the symbol themselves */ +EXTERN(_start_trap); + +/* Default interrupt trap entry point. When vectored trap mode is enabled, + the riscv-rt crate provides an implementation of this function, which saves caller saved + registers, calls the the DefaultHandler ISR, restores caller saved registers and returns. */ +PROVIDE(_start_DefaultHandler_trap = _start_trap); + +/* When vectored trap mode is enabled, each interrupt source must implement its own + trap entry point. By default, all interrupts start in _start_trap. However, users can + override these alias by defining the symbol themselves */ +PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap); +PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap); +PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap); +PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap); + /** EXCEPTION HANDLERS **/ /* Default exception handler. The riscv-rt crate provides a weak alias of this function, @@ -44,7 +66,7 @@ PROVIDE(Breakpoint = ExceptionHandler); PROVIDE(LoadMisaligned = ExceptionHandler); PROVIDE(LoadFault = ExceptionHandler); PROVIDE(StoreMisaligned = ExceptionHandler); -PROVIDE(StoreFault = ExceptionHandler);; +PROVIDE(StoreFault = ExceptionHandler); PROVIDE(UserEnvCall = ExceptionHandler); PROVIDE(SupervisorEnvCall = ExceptionHandler); PROVIDE(MachineEnvCall = ExceptionHandler); diff --git a/riscv-rt/macros/Cargo.toml b/riscv-rt/macros/Cargo.toml index f300fa1b..17b8e9d1 100644 --- a/riscv-rt/macros/Cargo.toml +++ b/riscv-rt/macros/Cargo.toml @@ -22,3 +22,7 @@ proc-macro2 = "1.0" [dependencies.syn] version = "1.0" features = ["extra-traits", "full"] + +[features] +s-mode = [] +v-trap = [] diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 14021902..007016b8 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -312,3 +312,290 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream { let res = format!("core::arch::global_asm!(\n\"{}\"\n);", instructions); res.parse().unwrap() } + +#[derive(Clone, Copy)] +enum RiscvArch { + Rv32, + Rv64, +} + +/// Size of the trap frame (in number of registers) +const TRAP_SIZE: usize = 16; + +#[rustfmt::skip] +/// List of the register names to be stored in the trap frame +const TRAP_FRAME: [&str; TRAP_SIZE] = [ + "ra", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", +]; + +/// Generate the assembly instructions to store the trap frame. +/// +/// The `arch` parameter is used to determine the width of the registers. +/// +/// The `filter` function is used to filter which registers to store. +/// This is useful to optimize the binary size in vectored interrupt mode, which divides the trap +/// frame storage in two parts: the first part saves space in the stack and stores only the `a0` register, +/// while the second part stores the remaining registers. +fn store_trap bool>(arch: RiscvArch, mut filter: T) -> String { + let (width, store) = match arch { + RiscvArch::Rv32 => (4, "sw"), + RiscvArch::Rv64 => (8, "sd"), + }; + + TRAP_FRAME + .iter() + .enumerate() + .filter(|(_, ®)| filter(reg)) + .map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n") +} + +/// Generate the assembly instructions to load the trap frame. +/// The `arch` parameter is used to determine the width of the registers. +fn load_trap(arch: RiscvArch) -> String { + let (width, load) = match arch { + RiscvArch::Rv32 => (4, "lw"), + RiscvArch::Rv64 => (8, "ld"), + }; + TRAP_FRAME + .iter() + .enumerate() + .map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n") +} + +/// Generates weak `_start_trap` function in assembly for RISCV-32 targets. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. +#[proc_macro] +pub fn weak_start_trap_riscv32(_input: TokenStream) -> TokenStream { + weak_start_trap(RiscvArch::Rv32) +} + +/// Generates weak `_start_trap` function in assembly for RISCV-64 targets. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. +#[proc_macro] +pub fn weak_start_trap_riscv64(_input: TokenStream) -> TokenStream { + weak_start_trap(RiscvArch::Rv64) +} + +/// Generates weak `_start_trap` function in assembly. +/// +/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`. +/// The trap frame is allocated on the stack and deallocated after the call. +/// +/// The `arch` parameter is used to determine the width of the registers. +/// The macro also ensures that the trap frame size is 16-byte aligned. +fn weak_start_trap(arch: RiscvArch) -> TokenStream { + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + // ensure we do not break that sp is 16-byte aligned + if (TRAP_SIZE * width) % 16 != 0 { + return parse::Error::new(Span::call_site(), "Trap frame size must be 16-byte aligned") + .to_compile_error() + .into(); + } + let store = store_trap(arch, |_| true); + let load = load_trap(arch); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + format!( + r#" +core::arch::global_asm!( +".section .trap, \"ax\" +.align {width} +.weak _start_trap +_start_trap: + addi sp, sp, - {TRAP_SIZE} * {width} + {store} + add a0, sp, zero + jal ra, _start_trap_rust + {load} + addi sp, sp, {TRAP_SIZE} * {width} + {ret} +");"# + ) + .parse() + .unwrap() +} + +/// Generates vectored interrupt trap functions in assembly for RISCV-32 targets. +#[cfg(feature = "v-trap")] +#[proc_macro] +pub fn vectored_interrupt_trap_riscv32(_input: TokenStream) -> TokenStream { + vectored_interrupt_trap(RiscvArch::Rv32) +} + +/// Generates vectored interrupt trap functions in assembly for RISCV-64 targets. +#[cfg(feature = "v-trap")] +#[proc_macro] +pub fn vectored_interrupt_trap_riscv64(_input: TokenStream) -> TokenStream { + vectored_interrupt_trap(RiscvArch::Rv64) +} + +#[cfg(feature = "v-trap")] +/// Generates global '_start_DefaultHandler_trap' and '_continue_interrupt_trap' functions in assembly. +/// The '_start_DefaultHandler_trap' function stores the trap frame partially (only register a0) and +/// jumps to the interrupt handler. The '_continue_interrupt_trap' function stores the trap frame +/// partially (all registers except a0), jumps to the interrupt handler, and restores the trap frame. +fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream { + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + let store_start = store_trap(arch, |reg| reg == "a0"); + let store_continue = store_trap(arch, |reg| reg != "a0"); + let load = load_trap(arch); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + let instructions = format!( + r#" +core::arch::global_asm!( +".section .trap, \"ax\" + +.global _start_DefaultHandler_trap +_start_DefaultHandler_trap: + addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame + {store_start} // store trap partially (only register a0) + la a0, DefaultHandler // load interrupt handler address into a0 + +.global _continue_interrupt_trap +_continue_interrupt_trap: + {store_continue} // store trap partially (all registers except a0) + jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0) + {load} // restore trap frame + addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame + {ret} // return from interrupt +");"# + ); + + instructions.parse().unwrap() +} + +#[proc_macro_attribute] +/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. +pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream { + interrupt(args, input, RiscvArch::Rv32) +} + +#[proc_macro_attribute] +/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. +pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream { + interrupt(args, input, RiscvArch::Rv64) +} + +fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + // check the function arguments + if !f.sig.inputs.is_empty() { + return parse::Error::new( + f.sig.inputs.first().unwrap().span(), + "`#[interrupt]` function should not have arguments", + ) + .to_compile_error() + .into(); + } + + // check the function signature + let valid_signature = f.sig.constness.is_none() + && f.sig.asyncness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[interrupt]` 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(); + } + + // XXX should we blacklist other attributes? + let ident = &f.sig.ident; + let export_name = format!("{:#}", ident); + + #[cfg(not(feature = "v-trap"))] + let start_trap = proc_macro2::TokenStream::new(); + #[cfg(feature = "v-trap")] + let start_trap = start_interrupt_trap(ident, _arch); + + quote!( + #start_trap + #[export_name = #export_name] + #f + ) + .into() +} + +#[cfg(feature = "v-trap")] +fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream { + let interrupt = ident.to_string(); + let width = match arch { + RiscvArch::Rv32 => 4, + RiscvArch::Rv64 => 8, + }; + let store = store_trap(arch, |r| r == "a0"); + + let instructions = format!( + r#" +core::arch::global_asm!( + ".section .trap, \"ax\" + .align 2 + .global _start_{interrupt}_trap + _start_{interrupt}_trap: + addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame + {store} // store trap partially (only register a0) + la a0, {interrupt} // load interrupt handler address into a0 + j _continue_interrupt_trap // jump to common part of interrupt trap +");"# + ); + + instructions.parse().unwrap() +} diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index 1aa962b3..7883c446 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -245,11 +245,14 @@ _mp_hook: 2: li a0, 1 ret", // Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap`. - // Trap mode is set to `Direct` by default. // Users can override this function by defining their own `_setup_interrupts` ".weak _setup_interrupts -_setup_interrupts: - la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode +_setup_interrupts:", + #[cfg(not(feature = "v-trap"))] + "la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode + #[cfg(feature = "v-trap")] + "la t0, _vector_table + ori t0, t0, 0x1", // _vector_table is 16-byte aligned, so we must set the bit 0 to activate the Vectored trap mode #[cfg(feature = "s-mode")] "csrw stvec, t0", #[cfg(not(feature = "s-mode"))] @@ -274,64 +277,46 @@ _pre_init_trap: j _pre_init_trap", ); -/// Trap entry point (_start_trap). It saves caller saved registers, calls -/// _start_trap_rust, restores caller saved registers and then returns. -/// -/// # Usage -/// -/// The macro takes 5 arguments: -/// - `$STORE`: the instruction used to store a register in the stack (e.g. `sd` for riscv64) -/// - `$LOAD`: the instruction used to load a register from the stack (e.g. `ld` for riscv64) -/// - `$BYTES`: the number of bytes used to store a register (e.g. 8 for riscv64) -/// - `$TRAP_SIZE`: the number of registers to store in the stack (e.g. 32 for all the user registers) -/// - list of tuples of the form `($REG, $LOCATION)`, where: -/// - `$REG`: the register to store/load -/// - `$LOCATION`: the location in the stack where to store/load the register -#[rustfmt::skip] -macro_rules! trap_handler { - ($STORE:ident, $LOAD:ident, $BYTES:literal, $TRAP_SIZE:literal, [$(($REG:ident, $LOCATION:literal)),*]) => { - // ensure we do not break that sp is 16-byte aligned - const _: () = assert!(($TRAP_SIZE * $BYTES) % 16 == 0); - global_asm!( - " - .section .trap, \"ax\" - .weak _start_trap - _start_trap:", - // save space for trap handler in stack - concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)), - // save registers in the desired order - $(concat!(stringify!($STORE), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* - // call rust trap handler - "add a0, sp, zero - jal ra, _start_trap_rust", - // restore registers in the desired order - $(concat!(stringify!($LOAD), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* - // free stack - concat!("addi sp, sp, ", stringify!($TRAP_SIZE * $BYTES)), - ); - cfg_global_asm!( - // return from trap - #[cfg(feature = "s-mode")] - "sret", - #[cfg(not(feature = "s-mode"))] - "mret", - ); - }; -} - -#[rustfmt::skip] #[cfg(riscv32)] -trap_handler!( - sw, lw, 4, 16, - [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), - (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] -); -#[rustfmt::skip] +riscv_rt_macros::weak_start_trap_riscv32!(); #[cfg(riscv64)] -trap_handler!( - sd, ld, 8, 16, - [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), - (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] +riscv_rt_macros::weak_start_trap_riscv64!(); + +#[cfg(all(riscv32, feature = "v-trap"))] +riscv_rt_macros::vectored_interrupt_trap_riscv32!(); +#[cfg(all(riscv64, feature = "v-trap"))] +riscv_rt_macros::vectored_interrupt_trap_riscv64!(); + +#[cfg(feature = "v-trap")] +cfg_global_asm!( + // Set the vector mode to vectored. + r#".section .trap, "ax" + .weak _vector_table + .type _vector_table, @function + + .option push + .balign 0x4 // TODO check if this is the correct alignment + .option norelax + .option norvc + + _vector_table: + j _start_trap // Interrupt 0 is used for exceptions + j _start_SupervisorSoft_trap + j _start_DefaultHandler_trap // Interrupt 2 is reserved + j _start_MachineSoft_trap + j _start_DefaultHandler_trap // Interrupt 4 is reserved + j _start_SupervisorTimer_trap + j _start_DefaultHandler_trap // Interrupt 6 is reserved + j _start_MachineTimer_trap + j _start_DefaultHandler_trap // Interrupt 8 is reserved + j _start_SupervisorExternal_trap + j _start_DefaultHandler_trap // Interrupt 10 is reserved + j _start_MachineExternal_trap + + // default table does not include the remaining interrupts. + // Targets with extra interrupts should override this table. + + .option pop"#, ); #[rustfmt::skip] diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 832a9fb3..7d6a6d59 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -354,6 +354,18 @@ //! } //! ``` //! +//! You can also use the `#[interrupt]` macro to define interrupt handlers: +//! +//! ``` no_run +//! #[riscv_rt::interrupt] +//! fn MachineTimer() { +//! // ... +//! } +//! ``` +//! +//! In direct mode, this macro is equivalent to defining a function with the same name. +//! However, in vectored mode, this macro will generate a proper trap handler for the interrupt. +//! //! If interrupt handler is not explicitly defined, `DefaultHandler` is called. //! //! ### `DefaultHandler` @@ -413,6 +425,33 @@ //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M //! } //! ``` +//! +//! ## `v-trap` +//! +//! The vectored trap feature (`v-trap`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). +//! +//! For example: +//! ``` text +//! [dependencies] +//! riscv-rt = {features=["v-trap"]} +//! ``` +//! When the vectored trap feature is enabled, the trap vector is set to `_vector_table` in vectored mode. +//! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the interrupt. +//! +//! ### Defining interrupt handlers in vectored mode +//! +//! In vectored mode, each interrupt must also have a corresponding trap handler. +//! Therefore, using `export_name` or `no_mangle` is not enough to define an interrupt handler. +//! The [`interrupt`] macro will generate the trap handler for the interrupt: +//! +//! ``` no_run +//! #[riscv_rt::interrupt] +//! fn MachineTimer() { +//! // ... +//! } +//! ``` +//! +//! This will generate a function named `_start_MachineTimer_trap` that calls the interrupt handler `MachineTimer`. // NOTE: Adapted from cortex-m/src/lib.rs #![no_std] @@ -429,6 +468,12 @@ use riscv::register::mcause as xcause; pub use riscv_rt_macros::{entry, pre_init}; +#[cfg(riscv32)] +pub use riscv_rt_macros::interrupt_riscv32 as interrupt; + +#[cfg(riscv64)] +pub use riscv_rt_macros::interrupt_riscv64 as interrupt; + /// We export this static with an informative name so that if an application attempts to link /// two copies of riscv-rt together, linking will fail. We also declare a links key in /// Cargo.toml which is the more modern way to solve the same problem, but we have to keep @@ -475,7 +520,7 @@ pub struct TrapFrame { pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { extern "C" { fn ExceptionHandler(trap_frame: &TrapFrame); - fn DefaultHandler(); + fn _dispatch_interrupt(code: usize); } let cause = xcause::read(); @@ -493,15 +538,8 @@ pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { } else { ExceptionHandler(trap_frame); } - } else if code < __INTERRUPTS.len() { - let h = &__INTERRUPTS[code]; - if let Some(handler) = h { - handler(); - } else { - DefaultHandler(); - } } else { - DefaultHandler(); + _dispatch_interrupt(code); } } @@ -543,6 +581,24 @@ pub static __EXCEPTIONS: [Option; 16] = [ Some(StorePageFault), ]; +#[export_name = "_dispatch_interrupt"] +unsafe extern "C" fn dispatch_interrupt(code: usize) { + extern "C" { + fn DefaultHandler(); + } + + if code < __INTERRUPTS.len() { + let h = &__INTERRUPTS[code]; + if let Some(handler) = h { + handler(); + } else { + DefaultHandler(); + } + } else { + DefaultHandler(); + } +} + extern "C" { fn SupervisorSoft(); fn MachineSoft();