Skip to content

Commit

Permalink
More intelligent exception and interrupt handling. Catch and describe…
Browse files Browse the repository at this point in the history
… fatal kernel errors
  • Loading branch information
diodesign committed Oct 22, 2018
1 parent a33dc64 commit 7df7d18
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 80 deletions.
45 changes: 36 additions & 9 deletions src/kernel/irq.rs
Expand Up @@ -5,20 +5,47 @@
* See LICENSE for usage and copying.
*/

/* platform-specific code must implement all this */
use platform;
use platform::common::IRQType;
use platform::common::IRQContext;
use platform::common::PrivilegeMode;
use platform::common::IRQ;

/* entry point for software exceptions. call down into platform-specific handlers */
/* kernel_irq_handler
entry point for hardware interrupts and software exceptions, collectively known as IRQs.
call down into platform-specific handlers
=> context = platform-specific context of the IRQ
*/
#[no_mangle]
pub extern "C" fn kernel_exception_handler()
pub extern "C" fn kernel_irq_handler(context: IRQContext)
{
klog!("Exception received");
platform::exception_handler();
let irq = platform::common::irq::dispatch(context);

match irq.irq_type
{
IRQType::Exception => exception(irq),
IRQType::Interrupt => interrupt(irq),
};
}

/* entry point for hardware interrupts. call down into platform-specific handlers */
#[no_mangle]
pub extern "C" fn kernel_interrupt_handler()
/* handle software exception */
fn exception(irq: IRQ)
{
match (irq.fatal, irq.privilege_mode)
{
(true, PrivilegeMode::Kernel) =>
{
kalert!("Fatal exception in kernel: {}", irq.debug_cause());
loop {}
},
(_, _) => () /* ignore everything else */
}
}

/* handle hardware interrupt */
fn interrupt(_irq: platform::common::IRQ)
{
klog!("Interrupt received");
platform::interrupt_handler();
kalert!("Hardware interrupt");
loop {}
}
58 changes: 25 additions & 33 deletions src/platform/riscv32/common/asm/irq.s
Expand Up @@ -32,8 +32,8 @@ irq_early_init:
lw x\reg, (\reg * 4)(sp)
.endm

# during interrupts and exceptions, stack 31 of 32 registers (skip x0)
.equ IRQ_REGISTER_FRAME_SIZE, (31 * 4)
# during interrupts and exceptions, reserve space for 32 registers
.equ IRQ_REGISTER_FRAME_SIZE, (32 * 4)

.align 4
# Entry point for machine-level handler of interrupts and exceptions
Expand All @@ -42,51 +42,43 @@ irq_machine_handler:
# get exception handler stack from mscratch by swapping it for current sp
csrrw sp, mscratch, sp

# preserve all 32 registers bar two: x0 (zero) and x2 (sp)
# in the IRQ handler stack. Pointless stacking zero,
# and x2 is held mscratch
# save space to preserve all 32 GP registers
addi sp, sp, -(IRQ_REGISTER_FRAME_SIZE)

# skip x0 (zero), save x1, skip and x2 (sp) as it's in mscratch right now,
# then stack the remaining 29 registers (x3 to x31)
PUSH_REG 1
.set reg, 3
.rept 29
# skip x0 (zero), stack all 31 other registers
.set reg, 1
.rept 31
PUSH_REG %reg
.set reg, reg + 1
.endr

# time to call the platform's higher-level IRQ handler. gather up
# the cause and location in memory of the exception or interrupt
addi a0, sp, IRQ_REGISTER_FRAME_SIZE
csrrs a1, mcause, x0
csrrs a2, mepc, x0

# if the top bit is set then a1 (mcause) is negative and this means
# we're dealing with a hardware interrupt
blt a1, x0, handle_hardware_interrupt
# if not, this is an exception
call kernel_exception_handler

irq_machine_handler_outro:
# restore stacked 29 registers (x31 to x3) then x1
# gather up the cause and location in memory of the exception or interrupt,
# and store on the IRQ handler's stack
addi sp, sp, -12
csrrs t0, mcause, x0
csrrs t1, mepc, x0
csrrs t2, mtval, x0
sw t0, 0(sp)
sw t1, 4(sp)
sw t2, 8(sp)

# pass current sp to exception/hw handler as a pointer. this'll allow
# the higher-level kernel access the context of the IRQ
add a0, sp, x0
call kernel_irq_handler

# fix up the stack from the cause and epc pushes
# then restore all 31 stacked registers, skipping zero (x0)
addi sp, sp, 12
.set reg, 31
.rept 29
.rept 31
PULL_REG %reg
.set reg, reg - 1
.endr
PULL_REG 1

# fix up exception handler sp
addi sp, sp, IRQ_REGISTER_FRAME_SIZE

# swap exception sp for original sp, and return
csrrw sp, mscratch, sp
mret

handle_hardware_interrupt:
# get rid of the top bit in a1
sll a1, a1, 1
srl a1, a1, 1
call kernel_interrupt_handler
j irq_machine_handler_outro
66 changes: 52 additions & 14 deletions src/platform/riscv32/common/src/irq.rs
Expand Up @@ -5,21 +5,59 @@
* See LICENSE for usage and copying.
*/

/* structure representing 30 registers stacked by low-level IRQ handler */
struct IRQRegisters
/* dispatch
Handle incoming IRQs: software exceptions and hardware interrupts
for the high-level kernel.
=> context = context from the low-level code that picked up the IRQ
<= return high-level description of the IRQ for the portable kernel
*/
pub fn dispatch(context: ::IRQContext) -> ::IRQ
{
ra: u32, gp: u32, tp: u32, t0: u32, t1: u32, t2: u32, fp: u32, s1: u32,
a0: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32, a7: u32,
s2: u32, s3: u32, s4: u32, s5: u32, s6: u32, s7: u32, s8: u32, s9: u32,
s10: u32, s11: u32, t3: u32, t4: u32, t5: u32, t6: u32
}
/* convert RISC-V cause codes into generic codes for the kernel.
the top bit of the cause code is set for interrupts and clear for execeptions */
let cause_type = match context.cause >> 31
{
0 => ::IRQType::Exception,
_ => ::IRQType::Interrupt
};
let cause_mask = (1 << 31) - 1;
let (fatal, cause) = match (cause_type, context.cause & cause_mask)
{
/* exceptions - some are labeled fatal */
(::IRQType::Exception, 0) => (true, ::IRQCause::InstructionAlignment),
(::IRQType::Exception, 1) => (true, ::IRQCause::InstructionAccess),
(::IRQType::Exception, 2) => (true, ::IRQCause::IllegalInstruction),
(::IRQType::Exception, 3) => (false, ::IRQCause::Breakpoint),
(::IRQType::Exception, 4) => (true, ::IRQCause::LoadAlignment),
(::IRQType::Exception, 5) => (true, ::IRQCause::LoadAccess),
(::IRQType::Exception, 6) => (true, ::IRQCause::StoreAlignment),
(::IRQType::Exception, 7) => (true, ::IRQCause::StoreAccess),
(::IRQType::Exception, 8) => (false, ::IRQCause::UserEnvironmentCall),
(::IRQType::Exception, 9) => (false, ::IRQCause::SupervisorEnvironmentCall),
(::IRQType::Exception, 11) => (false, ::IRQCause::KernelEnvironmentCall),
(::IRQType::Exception, 12) => (false, ::IRQCause::InstructionPageFault),
(::IRQType::Exception, 13) => (false, ::IRQCause::LoadPageFault),
(::IRQType::Exception, 15) => (false, ::IRQCause::StorePageFault),

/* Handle synchronous exception triggered by programming error */
pub fn exception_handler()
{
}
/* interrupts - none are fatal */
(::IRQType::Interrupt, 0) => (false, ::IRQCause::UserSWI),
(::IRQType::Interrupt, 1) => (false, ::IRQCause::SupervisorSWI),
(::IRQType::Interrupt, 3) => (false, ::IRQCause::KernelSWI),
(::IRQType::Interrupt, 4) => (false, ::IRQCause::UserTimer),
(::IRQType::Interrupt, 5) => (false, ::IRQCause::SupervisorTimer),
(::IRQType::Interrupt, 7) => (false, ::IRQCause::KernelTimer),
(::IRQType::Interrupt, 8) => (false, ::IRQCause::UserInterrupt),
(::IRQType::Interrupt, 9) => (false, ::IRQCause::SupervisorInterrupt),
(::IRQType::Interrupt, 11) => (false, ::IRQCause::KernelInterrupt),
(_, _) => (false, ::IRQCause::Unknown)
};

/* Handle async or synchronous interrupt triggered during program execution */
pub fn interrupt_handler()
{
/* return structure describing this exception to the high-level kernel */
::IRQ
{
fatal: fatal,
irq_type: cause_type,
cause: cause,
privilege_mode: ::PrivilegeMode::Kernel,
}
}
94 changes: 94 additions & 0 deletions src/platform/riscv32/common/src/lib.rs
Expand Up @@ -10,3 +10,97 @@
/* expose architecture common code to platform-specific code */
pub mod devicetree;
pub mod irq;

/* define common structures */

/* levels of privilege accepted by the kernel */
#[derive(Copy, Clone)]
pub enum PrivilegeMode
{
Kernel, /* machine-mode kernel */
Supervisor, /* supervisor aka guest kernel */
User /* usermode */
}

/* describe the type of interruption */
#[derive(Copy, Clone)]
pub enum IRQType
{
Exception, /* software-generated interrupt */
Interrupt /* hardware-generated interrupt */
}

pub enum IRQCause
{
/* software interrupt generated from user, supervisor or kernel mode */
UserSWI, SupervisorSWI, KernelSWI,
/* hardware timer generated for user, supervisor or kernel mode */
UserTimer, SupervisorTimer, KernelTimer,
/* external hw interrupt generated for user, supervisor or kernel mode */
UserInterrupt, SupervisorInterrupt, KernelInterrupt,

/* common CPU faults */
InstructionAlignment, InstructionAccess, IllegalInstruction, InstructionPageFault,
LoadAlignment, LoadAccess, LoadPageFault, StoreAlignment, StoreAccess, StorePageFault,
Breakpoint,

/* other ways to call down from user to supervisor, etc */
UserEnvironmentCall, SupervisorEnvironmentCall, KernelEnvironmentCall,

Unknown /* unknown, undefined, or reserved type */
}

/* describe IRQ in high-level, portable terms */
pub struct IRQ
{
pub fatal: bool, /* true if this IRQ means current environment must stop */
pub privilege_mode: PrivilegeMode, /* privilege level of the running environment */
pub irq_type: IRQType, /* type of the IRQ - sw or hw generated */
pub cause: IRQCause /* cause of this interruption */
}

impl IRQ
{
/* return a string debugging this IRQ's cause */
pub fn debug_cause(&self) -> &str
{
match self.cause
{
IRQCause::UserSWI => "Usermode SWI",
IRQCause::SupervisorSWI => "Supervisor SWI",
IRQCause::KernelSWI => "Kernel SWI",
IRQCause::UserTimer => "Usermode timer",
IRQCause::SupervisorTimer => "Supervisor timer",
IRQCause::KernelTimer => "Kernel timer",
IRQCause::UserInterrupt => "Usermode external interrupt",
IRQCause::SupervisorInterrupt => "Supervisor external interrupt",
IRQCause::KernelInterrupt => "Kernel external interrupt",
IRQCause::InstructionAlignment => "Bad instruction alignment",
IRQCause::InstructionAccess => "Bad instruction access",
IRQCause::IllegalInstruction => "Illegal instruction",
IRQCause::InstructionPageFault => "Page fault by instruction fetch",
IRQCause::LoadAlignment => "Bad memory read alignment",
IRQCause::LoadAccess => "Bad memory read",
IRQCause::LoadPageFault => "Page fault by memory read",
IRQCause::StoreAlignment => "Bad memory write alignment",
IRQCause::StoreAccess => "Bad memory write",
IRQCause::StorePageFault => "Page fault by memory write",
IRQCause::Breakpoint => "Breakpoint",
IRQCause::UserEnvironmentCall => "Usermode environment call",
IRQCause::SupervisorEnvironmentCall => "Supervisor environment call",
IRQCause::KernelEnvironmentCall => "Kernel environment call",
_ => "Unknown IRQ"
}
}
}

/* Hardware-specific data from low-level IRQ handler.
Note: register x2 is normally sp but in this case contains the
top of the IRQ handler stack. Read the interrupted sp from
mscratch if needed... */
pub struct IRQContext
{
cause: u32, epc: u32, /* cause code and PC when IRQ fired */
mtval: u32, /* IRQ specific information */
registers: [u32; 32] /* all 32 registers stacked */
}
12 changes: 0 additions & 12 deletions src/platform/riscv32/qemu32_virt/src/lib.rs
Expand Up @@ -17,15 +17,3 @@ pub fn get_ram_size(device_tree_buf: &u8) -> Option<u64>
{
common::devicetree::get_ram_size(device_tree_buf)
}

/* Handle synchronous exception triggered by programming error */
pub fn exception_handler()
{
common::irq::exception_handler();
}

/* Handle async or synchronous interrupt triggered during program execution */
pub fn interrupt_handler()
{
common::irq::interrupt_handler();
}
12 changes: 0 additions & 12 deletions src/platform/riscv32/sifive_u34/src/lib.rs
Expand Up @@ -17,15 +17,3 @@ pub fn get_ram_size(device_tree_buf: &u8) -> Option<u64>
{
common::devicetree::get_ram_size(device_tree_buf)
}

/* Handle synchronous exception triggered by programming error */
pub fn exception_handler()
{
common::irq::exception_handler();
}

/* Handle async or synchronous interrupt triggered during program execution */
pub fn interrupt_handler()
{
common::irq::interrupt_handler();
}

0 comments on commit 7df7d18

Please sign in to comment.