赞
踩
发生异常时,首先需要执行 trap 流程:
并设置相关 CSR 的值:
这里不过多分析异常处理过程中硬件寄存器的变化,需要注意的是,在执行异常处理程序时,会进行上下文环境的切换和保存,在执行完异常处理程序后,会通过【m/s】ret指令来退出异常处理程序,接下来会进行恢复异常前程序流的相关操作,最终会跳转到【m/s】pec中保存的地址执行。
在默认的情况下,无论在什么模式下发生异常,都会将控制权交到M模式的异常处理程序,但是Linux系统多数异常都在S模式下进行系统调用。此时,会将M 模式的异常处理程序可以将异常重新导向 S 模式,但这些额外的操作会减慢大多数异常的处理速度。因此,RISC-V 提供了一种异常委托机制。通过该机制可以选择性地将异常交给 S 模式处理,而完全绕过 M 模式。
这种委托机制的实现主要通过:medeleg(Machine Exception Delegation,机器同步异常委托)和 mideleg(Machine Interrupt Delegation,机器中断委托)分别控制将哪些同步异常和中断委托给 S 模式,mret 指令则将 trap 交给其它特权模式处理。
委托给 S 模式的任何异常都可以被 S 模式屏蔽。sie(Supervisor Interrupt Enable,监管者中断使能)和 sip(Supervisor Interrupt Pending,监管者中断待处理)是 S 模式的控制状态寄存器,它们是 mie 和 mip 的子集。它们有着和 M 模式下相同的布局,但在 sie 和 sip 中只有由 mideleg 委托的中断对应的位才能读写,那些没有被委派的中断对应的位始终为 0。
注:发生异常时控制权都不会移交给权限更低的模式。在 M 模式下发生的异常总是在 M 模式下处理。在 S 模式下发生的异常,根据具体的委派设置,可能由 M 模式或 S 模式处理,但永远不会由 U 模式处理。
这里需要特殊强调的是异常处理构建的相关内容:
这里会将a4寄存器中的值存储到CSR_MTVEC这个状态寄存器,也就是异常处理程序的的入口;如果遇到异常、中断时,硬件会自动找到_trap_handler
/* Setup trap handler */ la a4, _trap_handler #if __riscv_xlen == 32 csrr a5, CSR_MISA srli a5, a5, ('H' - 'A') andi a5, a5, 0x1 beq a5, zero, _skip_trap_handler_rv32_hyp la a4, _trap_handler_rv32_hyp #endif csrw CSR_MTVEC, a4 .section .entry, "ax", %progbits .align 3 .globl _trap_handler _trap_handler: TRAP_SAVE_AND_SETUP_SP_T0 TRAP_SAVE_MEPC_MSTATUS 0 TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 TRAP_CALL_C_ROUTINE TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 TRAP_RESTORE_MEPC_MSTATUS 0 TRAP_RESTORE_SP_T0 mret
建立excption_stack空间,如所示M模式下的异常,则从SP指针开始构建;若不是M模式进入异常,则需要从TP指针开始构建,TP的值为MSCRARCH(这个寄存器会在非M模式下记录M模式下栈帧地址)
.macro TRAP_SAVE_AND_SETUP_SP_T0 /* Swap TP and MSCRATCH */ csrrw tp, CSR_MSCRATCH, tp /* Save T0 in scratch space */ REG_S t0, SBI_SCRATCH_TMP0_OFFSET(tp) /* * Set T0 to appropriate exception stack * * Came_From_M_Mode = ((MSTATUS.MPP < PRV_M) ? 1 : 0) - 1; * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) * * Came_From_M_Mode = 0 ==> Exception_Stack = TP * Came_From_M_Mode = -1 ==> Exception_Stack = SP */ csrr t0, CSR_MSTATUS srl t0, t0, MSTATUS_MPP_SHIFT and t0, t0, PRV_M slti t0, t0, PRV_M add t0, t0, -1 xor sp, sp, tp and t0, t0, sp xor sp, sp, tp xor t0, tp, t0 /* Save original SP on exception stack */ REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_REGS_SIZE)(t0) /* Set SP to exception stack and make room for trap registers */ add sp, t0, -(SBI_TRAP_REGS_SIZE) /* Restore T0 from scratch space */ REG_L t0, SBI_SCRATCH_TMP0_OFFSET(tp) /* Save T0 on stack */ REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) /* Swap TP and MSCRATCH */ csrrw tp, CSR_MSCRATCH, tp .endm
TRAP_CALL_C_ROUTINE前面的宏流程用于状态的保存,TRAP_CALL_C_ROUTINE则会调用到C阶段,进入真正的异常处理程序:
.macro TRAP_CALL_C_ROUTINE
/* Call C routine */
add a0, sp, zero
call sbi_trap_handler
.endm
下面将调用到sbi_trap_handler进行真正的异常处理函数。
当Linux中发起ecall调用后,OpenSBI相关服务出发过程如下,主要分为以下几个阶段
//lib/sbi_trap.c /** * Handle trap/interrupt * * This function is called by firmware linked to OpenSBI * library for handling trap/interrupt. It expects the * following: * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART * 2. The 'mcause' CSR is having exception/interrupt cause * 3. The 'mtval' CSR is having additional trap information * 4. The 'mtval2' CSR is having additional trap information * 5. The 'mtinst' CSR is having decoded trap instruction * 6. Stack pointer (SP) is setup for current HART * 7. Interrupts are disabled in MSTATUS CSR * * @param regs pointer to register state */ void sbi_trap_handler(struct sbi_trap_regs *regs) { int rc = SBI_ENOTSUPP; const char *msg = "trap handler failed"; ulong mcause = csr_read(CSR_MCAUSE); ulong mtval = csr_read(CSR_MTVAL), mtval2 = 0, mtinst = 0; struct sbi_trap_info trap; if (misa_extension('H')) { mtval2 = csr_read(CSR_MTVAL2); mtinst = csr_read(CSR_MTINST); } if (mcause & (1UL << (__riscv_xlen - 1))) { mcause &= ~(1UL << (__riscv_xlen - 1)); switch (mcause) { case IRQ_M_TIMER: sbi_timer_process(); break; case IRQ_M_SOFT: sbi_ipi_process(); break; default: msg = "unhandled external interrupt"; goto trap_error; }; return; } switch (mcause) { case CAUSE_ILLEGAL_INSTRUCTION: rc = sbi_illegal_insn_handler(mtval, regs); msg = "illegal instruction handler failed"; break; case CAUSE_MISALIGNED_LOAD: rc = sbi_misaligned_load_handler(mtval, mtval2, mtinst, regs); msg =<
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。