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 1ce6c5bc..58a41ae4 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)) @@ -286,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) 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..52e1f037 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; \ } @@ -164,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 */ @@ -196,6 +246,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 +447,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 +636,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 +671,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 +1121,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..f9c87664 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -29,6 +29,28 @@ enum { 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 */ CSR_MISA = 0x301, /* ISA and extensions */ @@ -54,11 +76,6 @@ enum { 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 */ @@ -135,11 +152,27 @@ struct riscv_internal { 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; }) 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