LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

RISC-V:异常及其在Linux下的处理

首先了解RISC-V异常相关配置和寄存器,然后了解各种异常类型以及Linux下是如何处理的。

1 RISC-V异常类型

 RISC-V异常配置和处理寄存器组
   
机器模式
超级用户模式
异常配置寄存器组
 
处理器状态寄存器
 
MSTATUS
 
存储了处理器在机器模式下的状态和控制信息,包括全局中断有效位、异常保留中断有效位、异常保留特权模式位等。
 
存储了处理器在超级用户模式下的状态和控制信息,包括全局中断有效位、异常保留中断有效位、异常保留特权模式位等,是 MSTATUS 的部分映射。
处理器指令集特性寄存器
MISA
存储了处理器所支持的指令集架构特性。
   
异常降级控制寄存器
MEDELEG
可以将超级用户和用户模式发生的异常降级到超级用户模式响应。
   
中断降级控制寄存器
MIDELEG
可以将超级用户模式中断降级到超级用户模式响应。
   
中断使能控制寄存器
MIE
用于控制不同中断类型的使能和屏蔽。
用于控制不同中断类型的使能和屏蔽,是 MIE 的部分映射。
向量基址寄存器
MTVEC
用于配置异常服务程序的入口地址。
用于配置异常服务程序的入口地址。
计数器访问授权寄存器
MCOUNTEREN
用于授权超级用户模式是否可以访问用户模式计数器。
用于授权用户模式是否可以访问用户模式计数器。
异常处理寄存器组
异常临时数据备份寄存器
MSCRATCH
 
用于处理器在异常服务程序中备份临时数据。一般用来存储机器模式本地上下文空间的入口指针值。
用于处理器在异常服务程序中备份临时数据。一般用来存储超级用户模式本地上下文空间的入口指针值。
异常保留程序计数器寄存器
MEPC
用于存储程序从异常服务程序退出时的程序计数器值(即 PC 值)。
用于存储程序从异常服务程序退出时的程序计数器值(即 PC 值)
异常事件向量寄存器
MCAUSE
用于保存触发异常的异常事件向量号,用于在异常服务程序中处理对应事件。
用于保存触发异常的异常事件向量号,用于在异常服务程序中处理对应事件。
中断等待状态寄存器
MIP
用于保存处理器的中断等待状态。当处理器出现中断无法立即响应的情况时,MIP 寄存器中的对应位会被置位。
用于保存处理器的中断等待状态。当处理器出现中断无法立即响应的情况时, SIP 寄存器中的对应位会被置位。

1.1 scause

mcause/scause记录了M/S模式下异常类型:

Interrupt-中断标记位:
  • 当 Interrupt 位为 1 时,表示触发异常的来源是中断, Exception Code 按照中断解析。包括机器模式(软件中断、计时器中断、外部中断)、超级用户模式(软件中断、计时器中断、外部中断)、L1数据ECC中断、PMU中断。
  • 当 Interrupt 位为 0 时,表示触发异常的来源不是中断, Exception Code 按照异常解析。

具体异常类型说明如下:

如果同时产生多个同步异常,由于mcause只能保存一个Exception Code,最终mcause会按照如下优先级选择高优先级的Exception Code。

1.2 stval

stval是Supervisor Trap Value寄存器,提供与当前异常scause相关的附加信息,包括:

  • when a breakpoint(3), address-misaligned(0), access-fault(1、5、7), or page-fault(12、13、15) exception occurs on an instruction fetch, load, or store, then stval will contain the faulting virtual address

  • when a misaligned load or store(4、6) causes an access-fault or page-fault exception, then stval will contain the virtual address of the portion of the access that caused the fault.

  • when an instruction access-fault or page-fault(12、13、15) exception occurs on a system with variable-length instructions, then stval will contain the virtual address of the portion of the instruction that caused the fault, while sepc will point to the beginning of the instruction.

  • when an illegal-instruction(2) exception occurs, then stval will contain the shortest of:
    • the actual faulting instruction
    • the first ILEN bits of the faulting instruction
    • the first SXLEN bits of the faulting instruction
  • For other traps, stval is set to zero, but a future standard may redefine stval’s setting for other traps.

1.3 stvec

stvec是Supervisor Trap-Vector基地址寄存器,由两部分组成:BASE和MODE。

  • BASE是4字节对齐地址。
  • MODE指定异常发生时pc指向何处,即使用哪个异常入口函数。

MODE=0时,所有的Traps处理都交给BASE指定的地址。

MODE=1时,所有同步异常处理交给BASE指定的地址;中断则指向BASE+irq*0x04的偏移值。比如Machine Timer Interrupt中断号为7,对应的中断处理函数指向BASE+0x1c。 

1.4 sstatus

sstatus是Supervisor模式下状态寄存器:

详细解释如下:

  • SD (State Dirty) [31]
    • 脏状态标志:当 FS(浮点状态)或 XS(扩展状态)字段非零时置 1,表示浮点或扩展单元上下文需要保存。
  • MXR (Make eXecutable Readable) [19]
    • 可执行页面可读控制:允许将不可执行的虚拟内存页标记为可读(需支持虚拟内存扩展,如 Sv32)。
  • SUM (permit Supervisor User Memory access) [18]
    • 监督者模式用户内存访问权限:置 1 时允许监督者模式访问用户模式内存(需虚拟内存扩展支持)。
  • XS (Extension State) [16:15]
    • 扩展单元状态:管理自定义扩展模块(如加速器)的上下文状态(编码同 FS:00=关闭,01=初始,10=清理,11=活跃)。
  • FS (Floating-point State) [14:13]
    • 浮点单元状态:控制浮点寄存器上下文状态(00=关闭,01=初始,10=清理,11=活跃)。
  • VS (Vector State) [10:9]
    • 向量单元状态:管理向量扩展(如 RVV)的上下文状态(编码同 FS,需支持向量扩展)。
  • SPP (Supervisor Previous Privilege) [8]
    • 前特权级记录:记录进入监督者模式前的特权级(0=用户模式,1=监督者模式)。
  • UBE (User-mode Byte Order) [6]
    • 用户模式字节序配置:设置用户模式内存访问的字节序(0=小端,1=大端,需硬件支持)。
  • SPIE (Supervisor Previous Interrupt Enable) [5]
    • 前中断使能状态:保存进入监督者模式前的中断使能状态(1=中断开启,0=中断关闭)。
  • SIE (Supervisor Interrupt Enable) [1]
    • 监督者模式中断使能:控制监督者模式下的全局中断(1=启用中断,0=禁用中断)。

1.5 sip和sie

sip是Supervisor Interrupt Pending寄存器,记录Supervisor模式下哪些中断处于Pending的寄存器:

  • SSIP (Supervisor Software Interrupt Pending) [1]
    • 监督者模式软件中断挂起标志(1=挂起,需手动清除)。
  • STIP (Supervisor Timer Interrupt Pending) [5]
    • 监督者模式定时器中断挂起标志(1=挂起,由硬件置位/清除)。
  • SEIP (Supervisor External Interrupt Pending) [9]
    • 监督者模式外部中断挂起标志(1=挂起,由外部中断源触发)。

sie是Supervisor Interrupt Enable寄存器,控制Supervisor模式下中断使能:

  • SSIE (Supervisor Software Interrupt Enable) [1]
    • 监督者模式软件中断使能(1=启用,0=禁用)。
  • STIE (Supervisor Timer Interrupt Enable) [5]
    • 监督者模式定时器中断使能(1=启用,0=禁用)。
  • SEIE (Supervisor External Interrupt Enable) [9]
    • 监督者模式外部中断使能(1=启用,0=禁用)。

1.6 sscratch和sepc

sscratch是Supervisor模式下的临时存储寄存器,主要用于异常处理或上下文切换时的临时数据保存。

sepc是Supervisor模式下保存发生异常或中断时的程序计数器(PC)值,用于在异常处理完成后恢复原执行流程。

1.7 scounteren

scounteren控制用户模式下对特定硬件计数器的访问权限,1表示允许;0表示禁止。

1. CY (Cycle Counter Enable) [0]
用户模式 cycle 计数器访问使能:
- 1:允许用户程序读取 cycle 计数器(记录处理器周期数)。
- 0:禁止访问,读操作会触发非法指令异常。

2. TM (Time Counter Enable) [1]
用户模式 time 计数器访问使能:
- 1:允许用户程序读取 time 计数器(记录实时时钟值)。
- 0:禁止访问,读操作会触发非法指令异常。

3. IR (Instret Counter Enable) [2]
用户模式 instret 计数器访问使能:
- 1:允许用户程序读取 instret 计数器(记录已执行指令数)。
- 0:禁止访问,读操作会触发非法指令异常。

4. HPM (HPM Counter Enable) [3:31]
用户模式硬件性能计数器(HPM)访问使能:
- 每个位对应一个 HPM 计数器(如 hpmcounter3 对应位 3)。
- 1:允许用户程序读取对应 HPM 计数器。
- 0:禁止访问。

2 Linux下RISC-V异常处理

2.1 异常向量注册

_start(head.S)是RISCV CPU的入口,设置异常服务程序入口地址:
_start
  _start_kernel
    setup_trap_vector
      la a0, handle_exception
      csrw CSR_TVEC, a0--设置Trap Vector为handle_exception函数。

2.2 异常处理入口函数

Linux下异常处理入口函数为:

复制代码
SYM_CODE_START(handle_exception)
    /*
     * If coming from userspace, preserve the user thread pointer and load
     * the kernel thread pointer.  If we came from the kernel, the scratch
     * register will contain 0, and we should continue on the current TP.
     */
    csrrw tp, CSR_SCRATCH, tp
    bnez tp, .Lsave_context

.Lrestore_kernel_tpsp:
    csrr tp, CSR_SCRATCH
    REG_S sp, TASK_TI_KERNEL_SP(tp)

#ifdef CONFIG_VMAP_STACK
    addi sp, sp, -(PT_SIZE_ON_STACK)
    srli sp, sp, THREAD_SHIFT
    andi sp, sp, 0x1
    bnez sp, handle_kernel_stack_overflow
    REG_L sp, TASK_TI_KERNEL_SP(tp)
#endif

.Lsave_context:
    REG_S sp, TASK_TI_USER_SP(tp)
    REG_L sp, TASK_TI_KERNEL_SP(tp)
    addi sp, sp, -(PT_SIZE_ON_STACK)
    REG_S x1,  PT_RA(sp)
    REG_S x3,  PT_GP(sp)
    REG_S x5,  PT_T0(sp)
    save_from_x6_to_x31

    /*
     * Disable user-mode memory access as it should only be set in the
     * actual user copy routines.
     *
     * Disable the FPU/Vector to detect illegal usage of floating point
     * or vector in kernel space.
     */
    li t0, SR_SUM | SR_FS_VS--禁用用户模式内存访问和FPU/Vector。

    REG_L s0, TASK_TI_USER_SP(tp)
    csrrc s1, CSR_STATUS, t0
    csrr s2, CSR_EPC
    csrr s3, CSR_TVAL
    csrr s4, CSR_CAUSE
    csrr s5, CSR_SCRATCH
    REG_S s0, PT_SP(sp)
    REG_S s1, PT_STATUS(sp)
    REG_S s2, PT_EPC(sp)
    REG_S s3, PT_BADADDR(sp)
    REG_S s4, PT_CAUSE(sp)
    REG_S s5, PT_TP(sp)

    /*
     * Set the scratch register to 0, so that if a recursive exception
     * occurs, the exception vector knows it came from the kernel
     */
    csrw CSR_SCRATCH, x0

    /* Load the global pointer */
    load_global_pointer

    /* Load the kernel shadow call stack pointer if coming from userspace */
    scs_load_current_if_task_changed s5

#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
    move a0, sp
    call riscv_v_context_nesting_start
#endif
    move a0, sp /* pt_regs */

    /*
     * MSB of cause differentiates between
     * interrupts and exceptions
     */
    bge s4, zero, 1f--bge的s4是有符号数,根据scause寄存器如果是中断则MSB为1,即负值。所以大于等于0,则是异常。

    /* Handle interrupts */
    call do_irq--进行中断处理。
    j ret_from_exception--返回。
1:
    /* Handle other exceptions */
    slli t0, s4, RISCV_LGPTR--slli是逻辑左移指令。如果是异常,每个异常跳转指针为4字节,根据异常向量号,计算出异常跳转指针在异常向量表中偏移量。
    la t1, excp_vect_table--异常向量表起始地址。
    la t2, excp_vect_table_end--异常向量表结束地址。
    add t0, t1, t0--得到异常对应的异常处理函数指针地址。
    /* Check if exception code lies within bounds */
    bgeu t0, t2, 3f--检查异常函数是否在范围内。
    REG_L t1, 0(t0)--加载异常处理函数到寄存器t1。
2:    jalr t1--跳转到异常函数处理。
    j ret_from_exception--返回。
3:

    la t1, do_trap_unknown--处理无法识别的异常。
    j 2b
SYM_CODE_END(handle_exception)
复制代码

 

在entry.S中定义了异常向量表:

复制代码
SYM_CODE_START(excp_vect_table)
        RISCV_PTR do_trap_insn_misaligned
        ALT_INSN_FAULT(RISCV_PTR do_trap_insn_fault)
        RISCV_PTR do_trap_insn_illegal
        RISCV_PTR do_trap_break
        RISCV_PTR do_trap_load_misaligned
        RISCV_PTR do_trap_load_fault
        RISCV_PTR do_trap_store_misaligned
        RISCV_PTR do_trap_store_fault
        RISCV_PTR do_trap_ecall_u /* system call */
        RISCV_PTR do_trap_ecall_s
        RISCV_PTR do_trap_unknown
        RISCV_PTR do_trap_ecall_m
        /* instruciton page fault */
        ALT_PAGE_FAULT(RISCV_PTR do_page_fault)
        RISCV_PTR do_page_fault   /* load page fault */
        RISCV_PTR do_trap_unknown
        RISCV_PTR do_page_fault   /* store page fault */
excp_vect_table_end:
SYM_CODE_END(excp_vect_table)
复制代码

 

2.3 不同类型异常处理

 不同异常类型在Linux中处理:

posted on   ArnoldLu  阅读(440)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示