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

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

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

1 RISC-V异常类型

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

1.1 scause

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

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

具体异常类型说明如下:

1.2 stvec

 

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)

2.3 不同类型异常处理

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

posted on 2024-11-09 23:59  ArnoldLu  阅读(77)  评论(0编辑  收藏  举报

导航