From fc0d878a322efccccde4b5497685280b9904dc4d Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Mon, 24 Jun 2024 09:07:05 +0800 Subject: [PATCH 1/3] Preliminary support for trap handling during block emulation Traps can occur during block emulation, such as from misaligned access. To handle a trap, we temporarily exit the block and transfer control to the trap handler, processing each instruction by stepping. After the trap is resolved, execution resumes from the point where it occurred. We use a flag called 'is_trapped' to indicate when the emulation is in trap-handling mode. Additionally, the 'sret' instruction is used to adjust the relevant supervisor CSR before returning from a trap. Support for S-mode has been enhanced by adding most S-mode CSRs to 'riscv_internal', along with helper macros for easier manipulation of S-mode and M-mode CSRs. Although M-mode CSRs are not directly used during emulation, the helpers have been added for potential future use. Conditional checks for 'SYSTEM' were added where necessary. The 'LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE' functionality is disabled during trap handling, as traps are managed one instruction at a time rather than as a chained block. Related: #310 --- Makefile | 5 +- src/common.h | 2 + src/decode.c | 2 + src/decode.h | 4 +- src/emulate.c | 198 ++++++++++++++++++++++++++++++++------------ src/riscv.c | 14 ++++ src/riscv.h | 55 ++++++++++++ src/riscv_private.h | 94 ++++++++++++++------- src/rv32_constopt.c | 2 + src/rv32_template.c | 53 ++++++++---- src/t2c.c | 4 +- src/t2c_template.c | 2 + 12 files changed, 337 insertions(+), 98 deletions(-) diff --git a/Makefile b/Makefile index 1ce6c5bc..8d8a4a12 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,9 @@ CFLAGS = -std=gnu99 -O2 -Wall -Wextra CFLAGS += -Wno-unused-label CFLAGS += -include src/common.h +ENABLE_SYSTEM ?= 0 +$(call set-feature, SYSTEM) + # Enable link-time optimization (LTO) ENABLE_LTO ?= 1 ifeq ($(call has, LTO), 1) @@ -134,7 +137,7 @@ endif ENABLE_JIT ?= 0 $(call set-feature, JIT) ifeq ($(call has, JIT), 1) -OBJS_EXT += jit.o +OBJS_EXT += jit.o # tier-2 JIT compiler powered LLVM LLVM_CONFIG = llvm-config-17 LLVM_CONFIG := $(shell which $(LLVM_CONFIG)) diff --git a/src/common.h b/src/common.h index f6337f8d..da84ebdb 100644 --- a/src/common.h +++ b/src/common.h @@ -25,6 +25,8 @@ #define ARRAYS_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#define MASK(n) (~((~0U << (n)))) + /* Alignment macro */ #if defined(__GNUC__) || defined(__clang__) #define __ALIGNED(x) __attribute__((aligned(x))) diff --git a/src/decode.c b/src/decode.c index c434b0fa..4dbce5bd 100644 --- a/src/decode.c +++ b/src/decode.c @@ -841,9 +841,11 @@ static inline bool op_system(rv_insn_t *ir, const uint32_t insn) case 0x202: /* HRET: return from traps in H-mode */ /* illegal instruction */ return false; +#if RV32_HAS(SYSTEM) case 0x102: /* SRET: return from traps in S-mode */ ir->opcode = rv_insn_sret; break; +#endif case 0x302: /* MRET */ ir->opcode = rv_insn_mret; break; diff --git a/src/decode.h b/src/decode.h index 6af8ef2e..865ba7cf 100644 --- a/src/decode.h +++ b/src/decode.h @@ -78,7 +78,9 @@ enum op_field { /* RISC-V Privileged Instruction */ \ _(wfi, 0, 4, 0, ENC(rs1, rd)) \ _(uret, 0, 4, 0, ENC(rs1, rd)) \ - _(sret, 1, 4, 0, ENC(rs1, rd)) \ + IIF(RV32_HAS(SYSTEM))( \ + _(sret, 1, 4, 0, ENC(rs1, rd)) \ + ) \ _(hret, 0, 4, 0, ENC(rs1, rd)) \ _(mret, 1, 4, 0, ENC(rs1, rd)) \ _(sfencevma, 1, 4, 0, ENC(rs1, rs2, rd)) \ diff --git a/src/emulate.c b/src/emulate.c index a8856afb..e631f1ca 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -43,7 +43,7 @@ extern struct target_ops gdbstub_ops; /* RISC-V exception code list */ /* clang-format off */ -#define RV_EXCEPTION_LIST \ +#define RV_TRAP_LIST \ IIF(RV32_HAS(EXT_C))(, \ _(insn_misaligned, 0) /* Instruction address misaligned */ \ ) \ @@ -51,74 +51,119 @@ extern struct target_ops gdbstub_ops; _(breakpoint, 3) /* Breakpoint */ \ _(load_misaligned, 4) /* Load address misaligned */ \ _(store_misaligned, 6) /* Store/AMO address misaligned */ \ - _(ecall_M, 11) /* Environment call from M-mode */ + IIF(RV32_HAS(SYSTEM))(, \ + _(ecall_M, 11) /* Environment call from M-mode */ \ + ) /* clang-format on */ enum { -#define _(type, code) rv_exception_code##type = code, - RV_EXCEPTION_LIST +#define _(type, code) rv_trap_code_##type = code, + RV_TRAP_LIST #undef _ }; -static void rv_exception_default_handler(riscv_t *rv) +static void rv_trap_default_handler(riscv_t *rv) { rv->csr_mepc += rv->compressed ? 2 : 4; rv->PC = rv->csr_mepc; /* mret */ } -/* When a trap occurs in M-mode, mtval is either initialized to zero or +/* + * Trap might occurs during block emulation. For instance, page fault. + * In order to handle trap, we have to escape from block and execute + * registered trap handler. This trap_handler function helps to execute + * the registered trap handler, PC by PC. Once the trap is handled, + * resume the previous execution flow where cause the trap. + * + * Since the system emulation has not yet included in rv32emu, the page + * fault is not practical in current test suite. Instead, we try to + * emulate the misaligned handling in the test suite. + */ +#if RV32_HAS(SYSTEM) +static void trap_handler(riscv_t *rv); +#endif + +/* When a trap occurs in M-mode/S-mode, m/stval is either initialized to zero or * populated with exception-specific details to assist software in managing - * the trap. Otherwise, the implementation never modifies mtval, although + * the trap. Otherwise, the implementation never modifies m/stval, although * software can explicitly write to it. The hardware platform will define * which exceptions are required to informatively set mtval and which may * consistently set it to zero. * * When a hardware breakpoint is triggered or an exception like address * misalignment, access fault, or page fault occurs during an instruction - * fetch, load, or store operation, mtval is updated with the virtual address - * that caused the fault. In the case of an illegal instruction trap, mtval + * fetch, load, or store operation, m/stval is updated with the virtual address + * that caused the fault. In the case of an illegal instruction trap, m/stval * might be updated with the first XLEN or ILEN bits of the offending - * instruction. For all other traps, mtval is simply set to zero. However, - * it is worth noting that a future standard could redefine how mtval is + * instruction. For all other traps, m/stval is simply set to zero. However, + * it is worth noting that a future standard could redefine how m/stval is * handled for different types of traps. + * + * For simplicity and clarity, abstracting stval and mtval into a single + * identifier called tval, as both are handled by TRAP_HANDLER_IMPL. */ -#define EXCEPTION_HANDLER_IMPL(type, code) \ - static void rv_except_##type(riscv_t *rv, uint32_t mtval) \ - { \ - /* mtvec (Machine Trap-Vector Base Address Register) \ - * mtvec[MXLEN-1:2]: vector base address \ - * mtvec[1:0] : vector mode \ - */ \ - const uint32_t base = rv->csr_mtvec & ~0x3; \ - const uint32_t mode = rv->csr_mtvec & 0x3; \ - /* mepc (Machine Exception Program Counter) \ - * mtval (Machine Trap Value Register) \ - * mcause (Machine Cause Register): store exception code \ - * mstatus (Machine Status Register): keep track of and controls the \ - * hart’s current operating state \ - */ \ - rv->csr_mepc = rv->PC; \ - rv->csr_mtval = mtval; \ - rv->csr_mcause = code; \ - rv->csr_mstatus = MSTATUS_MPP; /* set privilege mode */ \ - if (!rv->csr_mtvec) { /* in case CSR is not configured */ \ - rv_exception_default_handler(rv); \ - return; \ - } \ - switch (mode) { \ - case 0: /* DIRECT: All exceptions set PC to base */ \ - rv->PC = base; \ - break; \ - /* VECTORED: Asynchronous interrupts set PC to base + 4 * code */ \ - case 1: \ - rv->PC = base + 4 * code; \ - break; \ - } \ +#define TRAP_HANDLER_IMPL(type, code) \ + static void rv_trap_##type(riscv_t *rv, uint32_t tval) \ + { \ + /* m/stvec (Machine/Supervisor Trap-Vector Base Address Register) \ + * m/stvec[MXLEN-1:2]: vector base address \ + * m/stvec[1:0] : vector mode \ + * m/sepc (Machine/Supervisor Exception Program Counter) \ + * m/stval (Machine/Supervisor Trap Value Register) \ + * m/scause (Machine/Supervisor Cause Register): store exception code \ + * m/sstatus (Machine/Supervisor Status Register): keep track of and \ + * controls the hart’s current operating state \ + */ \ + uint32_t base; \ + uint32_t mode; \ + /* user or supervisor */ \ + if (RV_PRIV_IS_U_OR_S_MODE()) { \ + const uint32_t sstatus_sie = \ + (rv->csr_sstatus & SSTATUS_SIE) >> SSTATUS_SIE_SHIFT; \ + rv->csr_sstatus |= (sstatus_sie << SSTATUS_SPIE_SHIFT); \ + rv->csr_sstatus &= ~(SSTATUS_SIE); \ + rv->csr_sstatus |= (rv->priv_mode << SSTATUS_SPP_SHIFT); \ + rv->priv_mode = RV_PRIV_S_MODE; \ + base = rv->csr_stvec & ~0x3; \ + mode = rv->csr_stvec & 0x3; \ + rv->csr_sepc = rv->PC; \ + rv->csr_stval = tval; \ + rv->csr_scause = code; \ + } else { /* machine */ \ + const uint32_t mstatus_mie = \ + (rv->csr_mstatus & MSTATUS_MIE) >> MSTATUS_MIE_SHIFT; \ + rv->csr_mstatus |= (mstatus_mie << MSTATUS_MPIE_SHIFT); \ + rv->csr_mstatus &= ~(MSTATUS_MIE); \ + rv->csr_mstatus |= (rv->priv_mode << MSTATUS_MPP_SHIFT); \ + rv->priv_mode = RV_PRIV_M_MODE; \ + base = rv->csr_mtvec & ~0x3; \ + mode = rv->csr_mtvec & 0x3; \ + rv->csr_mepc = rv->PC; \ + rv->csr_mtval = tval; \ + rv->csr_mcause = code; \ + if (!rv->csr_mtvec) { /* in case CSR is not configured */ \ + rv_trap_default_handler(rv); \ + return; \ + } \ + } \ + switch (mode) { \ + /* DIRECT: All traps set PC to base */ \ + case 0: \ + rv->PC = base; \ + break; \ + /* VECTORED: Asynchronous traps set PC to base + 4 * code */ \ + case 1: \ + /* MSB of code is used to indicate whether the trap is interrupt \ + * or exception, so it is not considered as the 'real' code */ \ + rv->PC = base + 4 * (code & MASK(31)); \ + break; \ + } \ + IIF(RV32_HAS(SYSTEM))(if (rv->is_trapped) trap_handler(rv);, ) \ } /* RISC-V exception handlers */ -#define _(type, code) EXCEPTION_HANDLER_IMPL(type, code) -RV_EXCEPTION_LIST +#define _(type, code) TRAP_HANDLER_IMPL(type, code) +RV_TRAP_LIST #undef _ /* wrap load/store and insn misaligned handler @@ -135,7 +180,8 @@ RV_EXCEPTION_LIST rv->compressed = compress; \ rv->csr_cycle = cycle; \ rv->PC = PC; \ - rv_except_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \ + IIF(RV32_HAS(SYSTEM))(rv->is_trapped = true, ); \ + rv_trap_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \ return false; \ } @@ -196,6 +242,26 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) case CSR_FCSR: return (uint32_t *) (&rv->csr_fcsr); #endif + case CSR_SSTATUS: + return (uint32_t *) (&rv->csr_sstatus); + case CSR_SIE: + return (uint32_t *) (&rv->csr_sie); + case CSR_STVEC: + return (uint32_t *) (&rv->csr_stvec); + case CSR_SCOUNTEREN: + return (uint32_t *) (&rv->csr_scounteren); + case CSR_SSCRATCH: + return (uint32_t *) (&rv->csr_sscratch); + case CSR_SEPC: + return (uint32_t *) (&rv->csr_sepc); + case CSR_SCAUSE: + return (uint32_t *) (&rv->csr_scause); + case CSR_STVAL: + return (uint32_t *) (&rv->csr_stval); + case CSR_SIP: + return (uint32_t *) (&rv->csr_sip); + case CSR_SATP: + return (uint32_t *) (&rv->csr_satp); default: return NULL; } @@ -377,9 +443,10 @@ enum { }; #if RV32_HAS(GDBSTUB) -#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode) +#define RVOP_NO_NEXT(ir) \ + (!ir->next | rv->debug_mode IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, )) #else -#define RVOP_NO_NEXT(ir) (!ir->next) +#define RVOP_NO_NEXT(ir) (!ir->next IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, )) #endif /* record whether the branch is taken or not during emulation */ @@ -565,8 +632,10 @@ FORCE_INLINE bool insn_is_unconditional_branch(uint8_t opcode) case rv_insn_ebreak: case rv_insn_jal: case rv_insn_jalr: - case rv_insn_sret: case rv_insn_mret: +#if RV32_HAS(SYSTEM) + case rv_insn_sret: +#endif #if RV32_HAS(EXT_C) case rv_insn_cj: case rv_insn_cjalr: @@ -598,7 +667,7 @@ static void block_translate(riscv_t *rv, block_t *block) /* decode the instruction */ if (!rv_decode(ir, insn)) { rv->compressed = is_compressed(insn); - rv_except_illegal_insn(rv, insn); + rv_trap_illegal_insn(rv, insn); break; } ir->impl = dispatch_table[ir->opcode]; @@ -1048,17 +1117,42 @@ void rv_step(void *arg) #endif } +#if RV32_HAS(SYSTEM) +static void trap_handler(riscv_t *rv) +{ + rv_insn_t *ir = mpool_alloc(rv->block_ir_mp); + assert(ir); + + /* set to false by sret/mret implementation */ + uint32_t insn; + while (rv->is_trapped && !rv_has_halted(rv)) { + insn = rv->io.mem_ifetch(rv->PC); + assert(insn); + + rv_decode(ir, insn); + ir->impl = dispatch_table[ir->opcode]; + rv->compressed = is_compressed(insn); + ir->impl(rv, ir, rv->csr_cycle, rv->PC); + } +} +#endif + void ebreak_handler(riscv_t *rv) { assert(rv); - rv_except_breakpoint(rv, rv->PC); + rv_trap_breakpoint(rv, rv->PC); } void ecall_handler(riscv_t *rv) { assert(rv); - rv_except_ecall_M(rv, 0); +#if RV32_HAS(SYSTEM) syscall_handler(rv); + rv->PC += 4; +#else + rv_trap_ecall_M(rv, 0); + syscall_handler(rv); +#endif } void memset_handler(riscv_t *rv) diff --git a/src/riscv.c b/src/riscv.c index f163e854..ce77a5a8 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -303,6 +303,20 @@ riscv_t *rv_create(riscv_user_t rv_attr) #endif #endif +#if RV32_HAS(SYSTEM) + /* + * System simulation defaults to S-mode as + * it does not rely on M-mode software like OpenSBI. + */ + rv->priv_mode = RV_PRIV_S_MODE; + + /* not being trapped */ + rv->is_trapped = false; +#else + /* ISA simulation defaults to M-mode */ + rv->priv_mode = RV_PRIV_M_MODE; +#endif + return rv; } diff --git a/src/riscv.h b/src/riscv.h index c4b7091b..30d6369b 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -96,6 +96,61 @@ enum { #define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT) #define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT) +/* The mstatus register keeps track of and controls the hart’s current operating + * state */ +#define MSTATUS_SIE_SHIFT 1 +#define MSTATUS_MIE_SHIFT 3 +#define MSTATUS_SPIE_SHIFT 5 +#define MSTATUS_UBE_SHIFT 6 +#define MSTATUS_MPIE_SHIFT 7 +#define MSTATUS_SPP_SHIFT 8 +#define MSTATUS_MPP_SHIFT 11 +#define MSTATUS_MPRV_SHIFT 17 +#define MSTATUS_SUM_SHIFT 18 +#define MSTATUS_MXR_SHIFT 18 +#define MSTATUS_TVM_SHIFT 20 +#define MSTATUS_TW_SHIFT 21 +#define MSTATUS_TSR_SHIFT 22 +#define MSTATUS_SIE (1 << MSTATUS_SIE_SHIFT) +#define MSTATUS_MIE (1 << MSTATUS_MIE_SHIFT) +#define MSTATUS_SPIE (1 << MSTATUS_SPIE_SHIFT) +#define MSTATUS_UBE (1 << MSTATUS_UBE_SHIFT) +#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT) +#define MSTATUS_SPP (1 << MSTATUS_SPP_SHIFT) +#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT) +#define MSTATUS_MPRV (1 << MSTATUS_MPRV_SHIFT) +#define MSTATUS_SUM (1 << MSTATUS_SUM_SHIFT) +#define MSTATUS_MXR (1 << MSTATUS_MXR_SHIFT) +#define MSTATUS_TVM (1 << MSTATUS_TVM_SHIFT) +#define MSTATUS_TW (1 << MSTATUS_TW_SHIFT) +#define MSTATUS_TSR (1 << MSTATUS_TSR_SHIFT) + +/* sstatus, a restricted view of mstatus */ +#define SSTATUS_SIE_SHIFT 1 +#define SSTATUS_SPIE_SHIFT 5 +#define SSTATUS_UBE_SHIFT 6 +#define SSTATUS_SPP_SHIFT 8 +#define SSTATUS_SUM_SHIFT 18 +#define SSTATUS_MXR_SHIFT 19 +#define SSTATUS_SIE (1 << SSTATUS_SIE_SHIFT) +#define SSTATUS_SPIE (1 << SSTATUS_SPIE_SHIFT) +#define SSTATUS_UBE (1 << SSTATUS_UBE_SHIFT) +#define SSTATUS_SPP (1 << SSTATUS_SPP_SHIFT) +#define SSTATUS_SUM (1 << SSTATUS_SUM_SHIFT) +#define SSTATUS_MXR (1 << SSTATUS_MXR_SHIFT) + +#define SIP_SSIP_SHIFT 1 +#define SIP_STIP_SHIFT 5 +#define SIP_SEIP_SHIFT 9 +#define SIP_SSIP (1 << SIP_SSIP_SHIFT) +#define SIP_STIP (1 << SIP_STIP_SHIFT) +#define SIP_SEIP (1 << SIP_SEIP_SHIFT) + +#define RV_PRIV_U_MODE 0 +#define RV_PRIV_S_MODE 1 +#define RV_PRIV_M_MODE 3 +#define RV_PRIV_IS_U_OR_S_MODE() (rv->priv_mode <= RV_PRIV_S_MODE) + /* * SBI functions must return a pair of values: * diff --git a/src/riscv_private.h b/src/riscv_private.h index 5ca740a1..757e940c 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -25,9 +25,31 @@ /* CSRs */ enum { /* floating point */ - CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ - CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ - CSR_FCSR = 0x003, /* Floating-point control and status register */ + CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ + CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ + CSR_FCSR = 0x003, /* Floating-point control and status register */ + + /* Supervisor trap setup */ + CSR_SSTATUS = 0x100, /* Supervisor status register */ + CSR_SIE = 0x104, /* Supervisor interrupt-enable register */ + CSR_STVEC = 0x105, /* Supervisor trap-handler base address */ + CSR_SCOUNTEREN = 0x106, /* Supervisor counter enable */ + + /* Supervisor trap handling */ + CSR_SSCRATCH = 0x140, /* Supervisor register for machine trap handlers */ + CSR_SEPC = 0x141, /* Supervisor exception program counter */ + CSR_SCAUSE = 0x142, /* Supervisor trap cause */ + CSR_STVAL = 0x143, /* Supervisor bad address or instruction */ + CSR_SIP = 0x144, /* Supervisor interrupt pending */ + + /* Supervisor protection and translation */ + CSR_SATP = 0x180, /* Supervisor address translation and protection */ + + /* Machine information registers */ + CSR_MVENDORID = 0xF11, /* Vendor ID */ + CSR_MARCHID = 0xF12, /* Architecture ID */ + CSR_MIMPID = 0xF13, /* Implementation ID */ + CSR_MHARTID = 0xF14, /* Hardware thread ID */ /* Machine trap setup */ CSR_MSTATUS = 0x300, /* Machine status register */ @@ -39,26 +61,21 @@ enum { CSR_MCOUNTEREN = 0x306, /* Machine counter enable */ /* machine trap handling */ - CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ - CSR_MEPC = 0x341, /* Machine exception program counter */ - CSR_MCAUSE = 0x342, /* Machine trap cause */ - CSR_MTVAL = 0x343, /* Machine bad address or instruction */ - CSR_MIP = 0x344, /* Machine interrupt pending */ + CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ + CSR_MEPC = 0x341, /* Machine exception program counter */ + CSR_MCAUSE = 0x342, /* Machine trap cause */ + CSR_MTVAL = 0x343, /* Machine bad address or instruction */ + CSR_MIP = 0x344, /* Machine interrupt pending */ /* low words */ - CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ - CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ + CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ + CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ CSR_INSTRET = 0xC02, /* high words */ CSR_CYCLEH = 0xC80, CSR_TIMEH = 0xC81, CSR_INSTRETH = 0xC82, - - CSR_MVENDORID = 0xF11, /* Vendor ID */ - CSR_MARCHID = 0xF12, /* Architecture ID */ - CSR_MIMPID = 0xF13, /* Implementation ID */ - CSR_MHARTID = 0xF14, /* Hardware thread ID */ }; /* translated basic block */ @@ -125,21 +142,37 @@ struct riscv_internal { #endif /* csr registers */ - uint64_t csr_cycle; /* Machine cycle counter */ - uint32_t csr_time[2]; /* Performance counter */ - uint32_t csr_mstatus; /* Machine status register */ - uint32_t csr_mtvec; /* Machine trap-handler base address */ - uint32_t csr_misa; /* ISA and extensions */ - uint32_t csr_mtval; /* Machine bad address or instruction */ - uint32_t csr_mcause; /* Machine trap cause */ - uint32_t csr_mscratch; /* Scratch register for machine trap handler */ - uint32_t csr_mepc; /* Machine exception program counter */ - uint32_t csr_mip; /* Machine interrupt pending */ - uint32_t csr_mvendorid; /* vendor ID */ - uint32_t csr_marchid; /* Architecture ID */ - uint32_t csr_mimpid; /* Implementation ID */ + uint64_t csr_cycle; /* Machine cycle counter */ + uint32_t csr_time[2]; /* Performance counter */ + uint32_t csr_mstatus; /* Machine status register */ + uint32_t csr_mtvec; /* Machine trap-handler base address */ + uint32_t csr_misa; /* ISA and extensions */ + uint32_t csr_mtval; /* Machine bad address or instruction */ + uint32_t csr_mcause; /* Machine trap cause */ + uint32_t csr_mscratch; /* Scratch register for machine trap handler */ + uint32_t csr_mepc; /* Machine exception program counter */ + uint32_t csr_mip; /* Machine interrupt pending */ + uint32_t csr_mie; /* Machine interrupt enable */ + uint32_t csr_mideleg; /* Machine interrupt delegation register */ + uint32_t csr_medeleg; /* Machine exception delegation register */ + uint32_t csr_mvendorid; /* vendor ID */ + uint32_t csr_marchid; /* Architecture ID */ + uint32_t csr_mimpid; /* Implementation ID */ uint32_t csr_mbadaddr; + uint32_t csr_sstatus; /* supervisor status register */ + uint32_t csr_stvec; /* supervisor trap vector base address register */ + uint32_t csr_sip; /* supervisor interrupt pending register */ + uint32_t csr_sie; /* supervisor interrupt enable register */ + uint32_t csr_scounteren; /* supervisor counter-enable register */ + uint32_t csr_sscratch; /* supervisor scratch register */ + uint32_t csr_sepc; /* supervisor exception program counter */ + uint32_t csr_scause; /* supervisor cause register */ + uint32_t csr_stval; /* supervisor trap value register */ + uint32_t csr_satp; /* supervisor address translation and protection */ + + uint32_t priv_mode; /* U-mode or S-mode or M-mode */ + bool compressed; /**< current instruction is compressed or not */ #if !RV32_HAS(JIT) block_map_t block_map; /**< basic block map */ @@ -170,6 +203,11 @@ struct riscv_internal { */ bool is_interrupted; #endif + +#if RV32_HAS(SYSTEM) + /* The flag is used to indicate the current emulation is in a trap */ + bool is_trapped; +#endif }; /* sign extend a 16 bit value */ diff --git a/src/rv32_constopt.c b/src/rv32_constopt.c index 9ae5a4e3..0f78271a 100644 --- a/src/rv32_constopt.c +++ b/src/rv32_constopt.c @@ -374,8 +374,10 @@ CONSTOPT(wfi, {}) /* URET: return from traps in U-mode */ CONSTOPT(uret, {}) +#if RV32_HAS(SYSTEM) /* SRET: return from traps in S-mode */ CONSTOPT(sret, {}) +#endif /* HRET: return from traps in H-mode */ CONSTOPT(hret, {}) diff --git a/src/rv32_template.c b/src/rv32_template.c index c021ae0e..e8f9ee99 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -199,19 +199,24 @@ RVOP( #if !RV32_HAS(JIT) #define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ /* lookup branch history table */ \ - for (int i = 0; i < HISTORY_SIZE; i++) { \ - if (ir->branch_table->PC[i] == PC) { \ - MUST_TAIL return ir->branch_table->target[i]->impl( \ - rv, ir->branch_table->target[i], cycle, PC); \ + IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped), )) \ + { \ + for (int i = 0; i < HISTORY_SIZE; i++) { \ + if (ir->branch_table->PC[i] == PC) { \ + MUST_TAIL return ir->branch_table->target[i]->impl( \ + rv, ir->branch_table->target[i], cycle, PC); \ + } \ + } \ + block_t *block = block_find(&rv->block_map, PC); \ + if (block) { \ + /* update branch history table */ \ + ir->branch_table->PC[ir->branch_table->idx] = PC; \ + ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ + ir->branch_table->idx = \ + (ir->branch_table->idx + 1) % HISTORY_SIZE; \ + MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, \ + PC); \ } \ - } \ - block_t *block = block_find(&rv->block_map, PC); \ - if (block) { \ - /* update branch history table */ \ - ir->branch_table->PC[ir->branch_table->idx] = PC; \ - ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ - ir->branch_table->idx = (ir->branch_table->idx + 1) % HISTORY_SIZE; \ - MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, PC); \ } #else #define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ @@ -1004,15 +1009,26 @@ RVOP( })) /* SRET: return from traps in S-mode */ +#if RV32_HAS(SYSTEM) RVOP( sret, { - /* FIXME: Implement */ - return false; + rv->is_trapped = false; + rv->priv_mode = (rv->csr_sstatus & SSTATUS_SPP) >> SSTATUS_SPP_SHIFT; + rv->csr_sstatus &= ~(SSTATUS_SPP); + + const uint32_t sstatus_spie = + (rv->csr_sstatus & SSTATUS_SPIE) >> SSTATUS_SPIE_SHIFT; + rv->csr_sstatus |= (sstatus_spie << SSTATUS_SIE_SHIFT); + rv->csr_sstatus |= SSTATUS_SPIE; + + rv->PC = rv->csr_sepc; + return true; }, GEN({ assert; /* FIXME: Implement */ })) +#endif /* HRET: return from traps in H-mode */ RVOP( @@ -1029,7 +1045,14 @@ RVOP( RVOP( mret, { - rv->csr_mstatus = MSTATUS_MPIE; + rv->priv_mode = (rv->csr_mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; + rv->csr_mstatus &= ~(MSTATUS_MPP); + + const uint32_t mstatus_mpie = + (rv->csr_mstatus & MSTATUS_MPIE) >> MSTATUS_MPIE_SHIFT; + rv->csr_mstatus |= (mstatus_mpie << MSTATUS_MIE_SHIFT); + rv->csr_mstatus |= MSTATUS_MPIE; + rv->PC = rv->csr_mepc; return true; }, diff --git a/src/t2c.c b/src/t2c.c index 17be7893..8e13ceae 100644 --- a/src/t2c.c +++ b/src/t2c.c @@ -159,8 +159,10 @@ FORCE_INLINE bool t2c_insn_is_terminal(uint8_t opcode) case rv_insn_ecall: case rv_insn_ebreak: case rv_insn_jalr: - case rv_insn_sret: case rv_insn_mret: +#if RV32_HAS(SYSTEM) + case rv_insn_sret: +#endif #if RV32_HAS(EXT_C) case rv_insn_cjalr: case rv_insn_cjr: diff --git a/src/t2c_template.c b/src/t2c_template.c index fd2ded42..2ee33720 100644 --- a/src/t2c_template.c +++ b/src/t2c_template.c @@ -361,7 +361,9 @@ T2C_OP(wfi, { __UNREACHABLE; }) T2C_OP(uret, { __UNREACHABLE; }) +#if RV32_HAS(SYSTEM) T2C_OP(sret, { __UNREACHABLE; }) +#endif T2C_OP(hret, { __UNREACHABLE; }) From e6d12a8ba1d497d7e01dc794383ca122b4da9f87 Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Wed, 26 Jun 2024 02:06:32 +0800 Subject: [PATCH 2/3] Add misaligned access test suite Place a variable and function at a misaligned address to trigger all three misaligned traps. The test suite verifies misalignment for instruction fetch and lw/sw instructions, ensuring the correct trap handler is invoked. Since our CI integrates the xPack toolchain, we use it to build the test suite. --- src/emulate.c | 4 ++ src/riscv_private.h | 74 +++++++++++++++---------------- tests/system/alignment/Makefile | 26 +++++++++++ tests/system/alignment/linker.ld | 15 +++++++ tests/system/alignment/main.c | 67 ++++++++++++++++++++++++++++ tests/system/alignment/misalign.S | 69 ++++++++++++++++++++++++++++ 6 files changed, 218 insertions(+), 37 deletions(-) create mode 100644 tests/system/alignment/Makefile create mode 100644 tests/system/alignment/linker.ld create mode 100644 tests/system/alignment/main.c create mode 100644 tests/system/alignment/misalign.S diff --git a/src/emulate.c b/src/emulate.c index e631f1ca..52e1f037 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -210,6 +210,10 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) return (uint32_t *) (&rv->csr_misa); /* Machine Trap Handling */ + case CSR_MEDELEG: /* Machine Exception Delegation Register */ + return (uint32_t *) (&rv->csr_medeleg); + case CSR_MIDELEG: /* Machine Interrupt Delegation Register */ + return (uint32_t *) (&rv->csr_mideleg); case CSR_MSCRATCH: /* Machine Scratch Register */ return (uint32_t *) (&rv->csr_mscratch); case CSR_MEPC: /* Machine Exception Program Counter */ diff --git a/src/riscv_private.h b/src/riscv_private.h index 757e940c..f9c87664 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -25,9 +25,9 @@ /* CSRs */ enum { /* floating point */ - CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ - CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ - CSR_FCSR = 0x003, /* Floating-point control and status register */ + CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ + CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ + CSR_FCSR = 0x003, /* Floating-point control and status register */ /* Supervisor trap setup */ CSR_SSTATUS = 0x100, /* Supervisor status register */ @@ -36,20 +36,20 @@ enum { CSR_SCOUNTEREN = 0x106, /* Supervisor counter enable */ /* Supervisor trap handling */ - CSR_SSCRATCH = 0x140, /* Supervisor register for machine trap handlers */ - CSR_SEPC = 0x141, /* Supervisor exception program counter */ - CSR_SCAUSE = 0x142, /* Supervisor trap cause */ - CSR_STVAL = 0x143, /* Supervisor bad address or instruction */ - CSR_SIP = 0x144, /* Supervisor interrupt pending */ + CSR_SSCRATCH = 0x140, /* Supervisor register for machine trap handlers */ + CSR_SEPC = 0x141, /* Supervisor exception program counter */ + CSR_SCAUSE = 0x142, /* Supervisor trap cause */ + CSR_STVAL = 0x143, /* Supervisor bad address or instruction */ + CSR_SIP = 0x144, /* Supervisor interrupt pending */ /* Supervisor protection and translation */ - CSR_SATP = 0x180, /* Supervisor address translation and protection */ + CSR_SATP = 0x180, /* Supervisor address translation and protection */ /* Machine information registers */ - CSR_MVENDORID = 0xF11, /* Vendor ID */ - CSR_MARCHID = 0xF12, /* Architecture ID */ - CSR_MIMPID = 0xF13, /* Implementation ID */ - CSR_MHARTID = 0xF14, /* Hardware thread ID */ + CSR_MVENDORID = 0xF11, /* Vendor ID */ + CSR_MARCHID = 0xF12, /* Architecture ID */ + CSR_MIMPID = 0xF13, /* Implementation ID */ + CSR_MHARTID = 0xF14, /* Hardware thread ID */ /* Machine trap setup */ CSR_MSTATUS = 0x300, /* Machine status register */ @@ -61,15 +61,15 @@ enum { CSR_MCOUNTEREN = 0x306, /* Machine counter enable */ /* machine trap handling */ - CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ - CSR_MEPC = 0x341, /* Machine exception program counter */ - CSR_MCAUSE = 0x342, /* Machine trap cause */ - CSR_MTVAL = 0x343, /* Machine bad address or instruction */ - CSR_MIP = 0x344, /* Machine interrupt pending */ + CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ + CSR_MEPC = 0x341, /* Machine exception program counter */ + CSR_MCAUSE = 0x342, /* Machine trap cause */ + CSR_MTVAL = 0x343, /* Machine bad address or instruction */ + CSR_MIP = 0x344, /* Machine interrupt pending */ /* low words */ - CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ - CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ + CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ + CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ CSR_INSTRET = 0xC02, /* high words */ @@ -142,22 +142,22 @@ struct riscv_internal { #endif /* csr registers */ - uint64_t csr_cycle; /* Machine cycle counter */ - uint32_t csr_time[2]; /* Performance counter */ - uint32_t csr_mstatus; /* Machine status register */ - uint32_t csr_mtvec; /* Machine trap-handler base address */ - uint32_t csr_misa; /* ISA and extensions */ - uint32_t csr_mtval; /* Machine bad address or instruction */ - uint32_t csr_mcause; /* Machine trap cause */ - uint32_t csr_mscratch; /* Scratch register for machine trap handler */ - uint32_t csr_mepc; /* Machine exception program counter */ - uint32_t csr_mip; /* Machine interrupt pending */ - uint32_t csr_mie; /* Machine interrupt enable */ - uint32_t csr_mideleg; /* Machine interrupt delegation register */ - uint32_t csr_medeleg; /* Machine exception delegation register */ - uint32_t csr_mvendorid; /* vendor ID */ - uint32_t csr_marchid; /* Architecture ID */ - uint32_t csr_mimpid; /* Implementation ID */ + uint64_t csr_cycle; /* Machine cycle counter */ + uint32_t csr_time[2]; /* Performance counter */ + uint32_t csr_mstatus; /* Machine status register */ + uint32_t csr_mtvec; /* Machine trap-handler base address */ + uint32_t csr_misa; /* ISA and extensions */ + uint32_t csr_mtval; /* Machine bad address or instruction */ + uint32_t csr_mcause; /* Machine trap cause */ + uint32_t csr_mscratch; /* Scratch register for machine trap handler */ + uint32_t csr_mepc; /* Machine exception program counter */ + uint32_t csr_mip; /* Machine interrupt pending */ + uint32_t csr_mie; /* Machine interrupt enable */ + uint32_t csr_mideleg; /* Machine interrupt delegation register */ + uint32_t csr_medeleg; /* Machine exception delegation register */ + uint32_t csr_mvendorid; /* vendor ID */ + uint32_t csr_marchid; /* Architecture ID */ + uint32_t csr_mimpid; /* Implementation ID */ uint32_t csr_mbadaddr; uint32_t csr_sstatus; /* supervisor status register */ @@ -171,7 +171,7 @@ struct riscv_internal { uint32_t csr_stval; /* supervisor trap value register */ uint32_t csr_satp; /* supervisor address translation and protection */ - uint32_t priv_mode; /* U-mode or S-mode or M-mode */ + uint32_t priv_mode; /* U-mode or S-mode or M-mode */ bool compressed; /**< current instruction is compressed or not */ #if !RV32_HAS(JIT) diff --git a/tests/system/alignment/Makefile b/tests/system/alignment/Makefile new file mode 100644 index 00000000..7b34cb35 --- /dev/null +++ b/tests/system/alignment/Makefile @@ -0,0 +1,26 @@ +PREFIX ?= riscv-none-elf- +ARCH = -march=rv32izicsr +LINKER_SCRIPT = linker.ld + +DEBUG_CFLAGS = -g +CFLAGS = -c -march=rv32i_zicsr +LDFLAGS = -T +EXEC = misalign.elf + +CC = $(PREFIX)gcc +AS = $(PREFIX)as +LD = $(PREFIX)ld +OBJDUMP = $(PREFIX)objdump + +deps = main.o misalign.o + +all: + $(CC) $(DEBUG_CLAGS) $(CFLAGS) main.c + $(AS) $(DEBUG_CLAGS) $(ARCH) misalign.S -o misalign.o + $(LD) $(LDFLAGS) $(LINKER_SCRIPT) -o $(EXEC) $(deps) + +dump: + $(OBJDUMP) -Ds $(EXEC) | less + +clean: + rm $(EXEC) $(deps) diff --git a/tests/system/alignment/linker.ld b/tests/system/alignment/linker.ld new file mode 100644 index 00000000..fe8ba902 --- /dev/null +++ b/tests/system/alignment/linker.ld @@ -0,0 +1,15 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY(_start) + +SECTIONS +{ + . = 0x10000; + .text : { *(.text) } + . = 0x8000003; + .misdata : { *(.misdata) } + . = 0x10000007; + .mistext : { *(.mistext) } + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/system/alignment/main.c b/tests/system/alignment/main.c new file mode 100644 index 00000000..da347569 --- /dev/null +++ b/tests/system/alignment/main.c @@ -0,0 +1,67 @@ +#define write_csr(reg, val) ({ asm volatile("csrw " #reg ", %0" ::"rK"(val)); }) + +#define printstr(ptr, length) \ + do { \ + asm volatile( \ + "add a7, x0, 0x40;" \ + "add a0, x0, 0x1;" /* stdout */ \ + "add a1, x0, %0;" \ + "mv a2, %1;" /* length character */ \ + "ecall;" \ + : \ + : "r"(ptr), "r"(length) \ + : "a0", "a1", "a2", "a7"); \ + } while (0) + +#define TEST_OUTPUT(msg, length) printstr(msg, length) + +#define TEST_LOGGER(msg) \ + { \ + char _msg[] = msg; \ + TEST_OUTPUT(_msg, sizeof(_msg) - 1); \ + } + +extern int *misalign_data; + +extern void misalign_func(); + +extern void misalign_trap_handler(); + +int main() +{ + /* init s-mode trap handler */ + write_csr(stvec, misalign_trap_handler); + + /* misalign load */ + const int x = *misalign_data; + /* + * execute the registered trap handler is considered a pass + * (use gdb to track) + */ + TEST_LOGGER("MISALIGNED LOAD TEST PASSED!\n"); + + /* misalign store */ + char *ptr = (char *) misalign_data; + *(int *) (ptr + 3) = x + 3; + /* + * execute the registered trap handler is considered a pass + * (use gdb to track) + */ + TEST_LOGGER("MISALIGNED STORE TEST PASSED!\n"); + + /* + * misalign instuction fetch + * + * MUST disable ENABLE_EXT_C when building rv32emu before running this test + * since jalr instruction only check misaligned if lacks of compressed + * instruction support + */ + misalign_func(); + /* + * execute the registered trap handler is considered a pass + * (use gdb to track) + */ + TEST_LOGGER("MISALIGNED INSTRUCTION FETCH TEST PASSED!\n"); + + return 0; +} diff --git a/tests/system/alignment/misalign.S b/tests/system/alignment/misalign.S new file mode 100644 index 00000000..190556ad --- /dev/null +++ b/tests/system/alignment/misalign.S @@ -0,0 +1,69 @@ +CAUSE_MISALIGNED_INSN_FETCH = 0x0 +CAUSE_MISALIGNED_LOAD = 0x4 +CAUSE_MISALIGNED_STORE = 0x6 + +.section .misdata, "aw", @progbits +.global misalign_data +.type misalign_data, @object +misalign_data: + .word = 0xBAAAAAAD + . = 0x8000003 # force misaligned address + +.section .mistext, "ax", @progbits +.global misalign_func +.type misalign_func, @function +misalign_func: + . = 0x10000007 # force misaligned address + addi t0, t0, 1 + +.section .text +.global _start +_start: + call main + j exit + +# Assume three traps are delegated to supervisor, +# so handle them using supervisor CSR +.global misalign_trap_handler +misalign_trap_handler: + csrr t0, scause + + # Check for misaligned instruction fetch + li t1, CAUSE_MISALIGNED_INSN_FETCH + beq t0, t1, misaligned_insn_fetch_handler + + # Check for misaligned load + li t1, CAUSE_MISALIGNED_LOAD + beq t0, t1, misaligned_load_handler + + # Check for misaligned store + li t1, CAUSE_MISALIGNED_STORE + beq t0, t1, misaligned_store_handler + + # If none of the above, exit failed +fail: + li a0, 1 + j exit + +exit: + li a7, 93 + ecall + +misaligned_insn_fetch_handler: + # simply jump back to caller since no really handling it + # since the rest of instruction are also misaligned + jalr zero, ra, 0 + +misaligned_load_handler: + # Handle load misalignment: skip load + csrr t0, sepc + addi t0, t0, 4 + csrw sepc, t0 + sret + +misaligned_store_handler: + # Handle store misalignment: skip store + csrr t0, sepc + addi t0, t0, 4 + csrw sepc, t0 + sret From a4f6dab3f65c9ea2ae518e59dcd0f21ae8ac4576 Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Wed, 9 Oct 2024 04:02:47 +0800 Subject: [PATCH 3/3] CI: Add misaligned access handling test suite during block emulation --- .github/workflows/main.yml | 4 ++++ Makefile | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9c4a2a60..00f2dd4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,10 @@ jobs: make distclean && make ENABLE_EXT_F=0 check -j$(nproc) make distclean && make ENABLE_EXT_C=0 check -j$(nproc) make distclean && make ENABLE_SDL=0 check -j$(nproc) + - name: misalignment test in block emulation + run: | + make -C tests/system/alignment/ + make distclean && make ENABLE_EXT_C=0 ENABLE_SYSTEM=1 misalign-in-blk-emu -j$(nproc) - name: gdbstub test run: | make distclean && make ENABLE_GDBSTUB=1 gdbstub-test -j$(nproc) diff --git a/Makefile b/Makefile index 8d8a4a12..58a41ae4 100644 --- a/Makefile +++ b/Makefile @@ -289,6 +289,16 @@ misalign: $(BIN) artifact $(PRINTF) "Failed.\n"; \ fi +EXPECTED_misalign = MISALIGNED INSTRUCTION FETCH TEST PASSED! +misalign-in-blk-emu: $(BIN) + $(Q)$(PRINTF) "Running misalign.elf ... "; \ + if [ "$(shell $(BIN) tests/system/alignment/misalign.elf | tail -n 2)" = "$(strip $(EXPECTED_misalign)) inferior exit code 0" ]; then \ + $(call notice, [OK]); \ + else \ + $(PRINTF) "Failed.\n"; \ + exit 1; \ + fi; + # Non-trivial demonstration programs ifeq ($(call has, SDL), 1) doom_action := (cd $(OUT); ../$(BIN) riscv32/doom)