387 lines
12 KiB
C
387 lines
12 KiB
C
/*
|
|
* Copyright (c) 2006-2024, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2022-12-08 RT-Thread first version
|
|
*/
|
|
|
|
#include <rthw.h>
|
|
#include <rtthread.h>
|
|
#include <stdint.h>
|
|
|
|
#include <mm_fault.h>
|
|
#include <mmu.h>
|
|
#include <encoding.h>
|
|
#include <stack.h>
|
|
#include <sbi.h>
|
|
#include <riscv.h>
|
|
#include <interrupt.h>
|
|
#include <plic.h>
|
|
#include <tick.h>
|
|
|
|
#ifdef RT_USING_SMART
|
|
#include <lwp_arch.h>
|
|
#endif
|
|
|
|
#define DBG_TAG "libcpu.trap"
|
|
#define DBG_LVL DBG_INFO
|
|
#include <rtdbg.h>
|
|
|
|
void dump_regs(struct rt_hw_stack_frame *regs)
|
|
{
|
|
rt_kprintf("--------------Dump Registers-----------------\n");
|
|
|
|
rt_kprintf("Function Registers:\n");
|
|
rt_kprintf("\tra(x1) = %p\tuser_sp = %p\n", regs->ra,
|
|
regs->user_sp_exc_stack);
|
|
rt_kprintf("\tgp(x3) = %p\ttp(x4) = %p\n", regs->gp, regs->tp);
|
|
rt_kprintf("Temporary Registers:\n");
|
|
rt_kprintf("\tt0(x5) = %p\tt1(x6) = %p\n", regs->t0, regs->t1);
|
|
rt_kprintf("\tt2(x7) = %p\n", regs->t2);
|
|
rt_kprintf("\tt3(x28) = %p\tt4(x29) = %p\n", regs->t3, regs->t4);
|
|
rt_kprintf("\tt5(x30) = %p\tt6(x31) = %p\n", regs->t5, regs->t6);
|
|
rt_kprintf("Saved Registers:\n");
|
|
rt_kprintf("\ts0/fp(x8) = %p\ts1(x9) = %p\n", regs->s0_fp, regs->s1);
|
|
rt_kprintf("\ts2(x18) = %p\ts3(x19) = %p\n", regs->s2, regs->s3);
|
|
rt_kprintf("\ts4(x20) = %p\ts5(x21) = %p\n", regs->s4, regs->s5);
|
|
rt_kprintf("\ts6(x22) = %p\ts7(x23) = %p\n", regs->s6, regs->s7);
|
|
rt_kprintf("\ts8(x24) = %p\ts9(x25) = %p\n", regs->s8, regs->s9);
|
|
rt_kprintf("\ts10(x26) = %p\ts11(x27) = %p\n", regs->s10, regs->s11);
|
|
rt_kprintf("Function Arguments Registers:\n");
|
|
rt_kprintf("\ta0(x10) = %p\ta1(x11) = %p\n", regs->a0, regs->a1);
|
|
rt_kprintf("\ta2(x12) = %p\ta3(x13) = %p\n", regs->a2, regs->a3);
|
|
rt_kprintf("\ta4(x14) = %p\ta5(x15) = %p\n", regs->a4, regs->a5);
|
|
rt_kprintf("\ta6(x16) = %p\ta7(x17) = %p\n", regs->a6, regs->a7);
|
|
rt_kprintf("sstatus = %p\n", regs->sstatus);
|
|
rt_kprintf("\t%s\n", (regs->sstatus & SSTATUS_SIE)
|
|
? "Supervisor Interrupt Enabled"
|
|
: "Supervisor Interrupt Disabled");
|
|
rt_kprintf("\t%s\n", (regs->sstatus & SSTATUS_SPIE)
|
|
? "Last Time Supervisor Interrupt Enabled"
|
|
: "Last Time Supervisor Interrupt Disabled");
|
|
rt_kprintf("\t%s\n", (regs->sstatus & SSTATUS_SPP)
|
|
? "Last Privilege is Supervisor Mode"
|
|
: "Last Privilege is User Mode");
|
|
rt_kprintf("\t%s\n", (regs->sstatus & SSTATUS_SUM)
|
|
? "Permit to Access User Page"
|
|
: "Not Permit to Access User Page");
|
|
rt_kprintf("\t%s\n", (regs->sstatus & (1 << 19))
|
|
? "Permit to Read Executable-only Page"
|
|
: "Not Permit to Read Executable-only Page");
|
|
rt_ubase_t satp_v = read_csr(satp);
|
|
rt_kprintf("satp = %p\n", satp_v);
|
|
rt_kprintf("\tCurrent Page Table(Physical) = %p\n",
|
|
__MASKVALUE(satp_v, __MASK(44)) << PAGE_OFFSET_BIT);
|
|
rt_kprintf("\tCurrent ASID = %p\n", __MASKVALUE(satp_v >> 44, __MASK(16))
|
|
<< PAGE_OFFSET_BIT);
|
|
const char *mode_str = "Unknown Address Translation/Protection Mode";
|
|
|
|
switch (__MASKVALUE(satp_v >> 60, __MASK(4)))
|
|
{
|
|
case 0:
|
|
mode_str = "No Address Translation/Protection Mode";
|
|
break;
|
|
|
|
case 8:
|
|
mode_str = "Page-based 39-bit Virtual Addressing Mode";
|
|
break;
|
|
|
|
case 9:
|
|
mode_str = "Page-based 48-bit Virtual Addressing Mode";
|
|
break;
|
|
}
|
|
|
|
rt_kprintf("\tMode = %s\n", mode_str);
|
|
rt_kprintf("-----------------Dump OK---------------------\n");
|
|
}
|
|
|
|
static const char *Exception_Name[] = {"Instruction Address Misaligned",
|
|
"Instruction Access Fault",
|
|
"Illegal Instruction",
|
|
"Breakpoint",
|
|
"Load Address Misaligned",
|
|
"Load Access Fault",
|
|
"Store/AMO Address Misaligned",
|
|
"Store/AMO Access Fault",
|
|
"Environment call from U-mode",
|
|
"Environment call from S-mode",
|
|
"Reserved-10",
|
|
"Reserved-11",
|
|
"Instruction Page Fault",
|
|
"Load Page Fault",
|
|
"Reserved-14",
|
|
"Store/AMO Page Fault"};
|
|
|
|
static const char *Interrupt_Name[] = {
|
|
"User Software Interrupt",
|
|
"Supervisor Software Interrupt",
|
|
"Reversed-2",
|
|
"Reversed-3",
|
|
"User Timer Interrupt",
|
|
"Supervisor Timer Interrupt",
|
|
"Reversed-6",
|
|
"Reversed-7",
|
|
"User External Interrupt",
|
|
"Supervisor External Interrupt",
|
|
"Reserved-10",
|
|
"Reserved-11",
|
|
};
|
|
|
|
#ifndef RT_USING_SMP
|
|
static volatile int nested = 0;
|
|
#define ENTER_TRAP nested += 1
|
|
#define EXIT_TRAP nested -= 1
|
|
#define CHECK_NESTED_PANIC(cause, tval, epc, eframe) \
|
|
if (nested != 1) handle_nested_trap_panic(cause, tval, epc, eframe)
|
|
#endif /* RT_USING_SMP */
|
|
|
|
static const char *get_exception_msg(int id)
|
|
{
|
|
const char *msg;
|
|
if (id < sizeof(Exception_Name) / sizeof(const char *))
|
|
{
|
|
msg = Exception_Name[id];
|
|
}
|
|
else
|
|
{
|
|
msg = "Unknown Exception";
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
#ifdef RT_USING_SMART
|
|
#include "lwp.h"
|
|
void handle_user(rt_ubase_t scause, rt_ubase_t stval, rt_ubase_t sepc,
|
|
struct rt_hw_stack_frame *sp)
|
|
{
|
|
rt_ubase_t id = __MASKVALUE(scause, __MASK(63UL));
|
|
struct rt_lwp *lwp;
|
|
|
|
/* user page fault */
|
|
enum rt_mm_fault_op fault_op;
|
|
enum rt_mm_fault_type fault_type;
|
|
switch (id)
|
|
{
|
|
case EP_LOAD_PAGE_FAULT:
|
|
fault_op = MM_FAULT_OP_READ;
|
|
fault_type = MM_FAULT_TYPE_GENERIC_MMU;
|
|
break;
|
|
case EP_LOAD_ACCESS_FAULT:
|
|
fault_op = MM_FAULT_OP_READ;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
case EP_LOAD_ADDRESS_MISALIGNED:
|
|
fault_op = MM_FAULT_OP_READ;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
case EP_STORE_PAGE_FAULT:
|
|
fault_op = MM_FAULT_OP_WRITE;
|
|
fault_type = MM_FAULT_TYPE_GENERIC_MMU;
|
|
break;
|
|
case EP_STORE_ACCESS_FAULT:
|
|
fault_op = MM_FAULT_OP_WRITE;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
case EP_STORE_ADDRESS_MISALIGNED:
|
|
fault_op = MM_FAULT_OP_WRITE;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
case EP_INSTRUCTION_PAGE_FAULT:
|
|
fault_op = MM_FAULT_OP_EXECUTE;
|
|
fault_type = MM_FAULT_TYPE_GENERIC_MMU;
|
|
break;
|
|
case EP_INSTRUCTION_ACCESS_FAULT:
|
|
fault_op = MM_FAULT_OP_EXECUTE;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
case EP_INSTRUCTION_ADDRESS_MISALIGNED:
|
|
fault_op = MM_FAULT_OP_EXECUTE;
|
|
fault_type = MM_FAULT_TYPE_BUS_ERROR;
|
|
break;
|
|
default:
|
|
fault_op = 0;
|
|
}
|
|
|
|
if (fault_op)
|
|
{
|
|
rt_base_t saved_stat;
|
|
lwp = lwp_self();
|
|
struct rt_aspace_fault_msg msg = {
|
|
.fault_op = fault_op,
|
|
.fault_type = fault_type,
|
|
.fault_vaddr = (void *)stval,
|
|
};
|
|
|
|
__asm__ volatile("csrrsi %0, sstatus, 2" : "=r"(saved_stat));
|
|
if (lwp && rt_aspace_fault_try_fix(lwp->aspace, &msg))
|
|
{
|
|
__asm__ volatile("csrw sstatus, %0" ::"r"(saved_stat));
|
|
return;
|
|
}
|
|
__asm__ volatile("csrw sstatus, %0" ::"r"(saved_stat));
|
|
}
|
|
LOG_E("[FATAL ERROR] Exception %ld:%s\n", id, get_exception_msg(id));
|
|
LOG_E("scause:%p,stval:%p,sepc:%p\n", scause, stval, sepc);
|
|
dump_regs(sp);
|
|
|
|
rt_thread_t cur_thr = rt_thread_self();
|
|
struct rt_hw_backtrace_frame frame = {.fp = sp->s0_fp, .pc = sepc};
|
|
rt_kprintf("fp = %p\n", frame.fp);
|
|
lwp_backtrace_frame(cur_thr, &frame);
|
|
|
|
LOG_E("User Fault, killing thread: %s", cur_thr->parent.name);
|
|
|
|
EXIT_TRAP;
|
|
sys_exit_group(-1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ARCH_RISCV_VECTOR
|
|
static void vector_enable(struct rt_hw_stack_frame *sp)
|
|
{
|
|
sp->sstatus |= SSTATUS_VS_INITIAL;
|
|
}
|
|
|
|
/**
|
|
* detect V/D support, and do not distinguish V/D instruction
|
|
*/
|
|
static int illegal_inst_recoverable(rt_ubase_t stval,
|
|
struct rt_hw_stack_frame *sp)
|
|
{
|
|
// first 7 bits is opcode
|
|
int opcode = stval & 0x7f;
|
|
int csr = (stval & 0xFFF00000) >> 20;
|
|
// ref riscv-v-spec-1.0, [Vector Instruction Formats]
|
|
int width = ((stval & 0x7000) >> 12) - 1;
|
|
int flag = 0;
|
|
|
|
switch (opcode)
|
|
{
|
|
case 0x57: // V
|
|
case 0x27: // scalar FLOAT
|
|
case 0x07:
|
|
case 0x73: // CSR
|
|
flag = 1;
|
|
break;
|
|
}
|
|
|
|
if (flag)
|
|
{
|
|
vector_enable(sp);
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
#endif
|
|
|
|
static void handle_nested_trap_panic(rt_ubase_t cause, rt_ubase_t tval,
|
|
rt_ubase_t epc,
|
|
struct rt_hw_stack_frame *eframe)
|
|
{
|
|
LOG_E("\n-------- [SEVER ERROR] --------");
|
|
LOG_E("Nested trap detected");
|
|
LOG_E("scause:%p,stval:%p,sepc:%p\n", cause, tval, epc);
|
|
dump_regs(eframe);
|
|
rt_hw_cpu_shutdown();
|
|
}
|
|
|
|
#define IN_USER_SPACE (stval >= USER_VADDR_START && stval < USER_VADDR_TOP)
|
|
#define PAGE_FAULT (id == EP_LOAD_PAGE_FAULT || id == EP_STORE_PAGE_FAULT)
|
|
|
|
/* Trap entry */
|
|
void handle_trap(rt_ubase_t scause, rt_ubase_t stval, rt_ubase_t sepc,
|
|
struct rt_hw_stack_frame *sp)
|
|
{
|
|
ENTER_TRAP;
|
|
rt_ubase_t id = __MASKVALUE(scause, __MASK(63UL));
|
|
const char *msg;
|
|
|
|
/* supervisor external interrupt */
|
|
if ((SCAUSE_INTERRUPT & scause) &&
|
|
SCAUSE_S_EXTERNAL_INTR == (scause & 0xff))
|
|
{
|
|
rt_interrupt_enter();
|
|
plic_handle_irq();
|
|
rt_interrupt_leave();
|
|
}
|
|
else if ((SCAUSE_INTERRUPT | SCAUSE_S_TIMER_INTR) == scause)
|
|
{
|
|
/* supervisor timer */
|
|
rt_interrupt_enter();
|
|
tick_isr();
|
|
rt_interrupt_leave();
|
|
}
|
|
else
|
|
{
|
|
if (SCAUSE_INTERRUPT & scause)
|
|
{
|
|
if (id < sizeof(Interrupt_Name) / sizeof(const char *))
|
|
{
|
|
msg = Interrupt_Name[id];
|
|
}
|
|
else
|
|
{
|
|
msg = "Unknown Interrupt";
|
|
}
|
|
|
|
LOG_E("Unhandled Interrupt %ld:%s\n", id, msg);
|
|
}
|
|
else
|
|
{
|
|
#ifdef ARCH_RISCV_VECTOR
|
|
if (scause == 0x2)
|
|
{
|
|
if (!(sp->sstatus & SSTATUS_VS) &&
|
|
illegal_inst_recoverable(stval, sp))
|
|
goto _exit;
|
|
}
|
|
#endif /* ARCH_RISCV_VECTOR */
|
|
#ifdef RT_USING_SMART
|
|
if (!(sp->sstatus & 0x100) || (PAGE_FAULT && IN_USER_SPACE))
|
|
{
|
|
handle_user(scause, stval, sepc, sp);
|
|
// if handle_user() return here, jump to u mode then
|
|
goto _exit;
|
|
}
|
|
#endif
|
|
|
|
// handle kernel exception:
|
|
rt_kprintf("Unhandled Exception %ld:%s\n", id,
|
|
get_exception_msg(id));
|
|
}
|
|
|
|
// trap cannot nested when handling another trap / interrupt
|
|
CHECK_NESTED_PANIC(scause, stval, sepc, sp);
|
|
|
|
rt_kprintf("scause:%p,stval:%p,sepc:%p\n", scause, stval, sepc);
|
|
dump_regs(sp);
|
|
|
|
rt_thread_t cur_thr = rt_thread_self();
|
|
rt_kprintf("--------------Thread list--------------\n");
|
|
rt_kprintf("current thread: %s\n", cur_thr->parent.name);
|
|
|
|
rt_kprintf("--------------Backtrace--------------\n");
|
|
struct rt_hw_backtrace_frame frame = {.fp = sp->s0_fp, .pc = sepc};
|
|
|
|
#ifdef RT_USING_SMART
|
|
if (!(sp->sstatus & 0x100))
|
|
{
|
|
lwp_backtrace_frame(cur_thr, &frame);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
rt_backtrace_frame(cur_thr, &frame);
|
|
}
|
|
|
|
while (1)
|
|
;
|
|
}
|
|
_exit:
|
|
EXIT_TRAP;
|
|
return;
|
|
}
|