#include "emulation.h" #include "fp_emulation.h" #include "config.h" #include "unprivileged_memory.h" #include "mtrap.h" #include void illegal_insn_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { asm (".pushsection .rodata\n" "illegal_insn_trap_table:\n" " .word truly_illegal_insn\n" #if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) " .word emulate_float_load\n" #else " .word truly_illegal_insn\n" #endif " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" #if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) " .word emulate_float_store\n" #else " .word truly_illegal_insn\n" #endif " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" #if !defined(__riscv_muldiv) " .word emulate_mul_div\n" #else " .word truly_illegal_insn\n" #endif " .word truly_illegal_insn\n" #if !defined(__riscv_muldiv) && __riscv_xlen >= 64 " .word emulate_mul_div32\n" #else " .word truly_illegal_insn\n" #endif " .word truly_illegal_insn\n" #ifdef PK_ENABLE_FP_EMULATION " .word emulate_fmadd\n" " .word emulate_fmadd\n" " .word emulate_fmadd\n" " .word emulate_fmadd\n" " .word emulate_fp\n" #else " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" #endif " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word emulate_system_opcode\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .word truly_illegal_insn\n" " .popsection"); uintptr_t mstatus; insn_t insn = get_insn(mepc, &mstatus); if (unlikely((insn & 3) != 3)) return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); write_csr(mepc, mepc + 4); extern uint32_t illegal_insn_trap_table[]; uint32_t* pf = (void*)illegal_insn_trap_table + (insn & 0x7c); emulation_func f = (emulation_func)(uintptr_t)*pf; f(regs, mcause, mepc, mstatus, insn); } void __attribute__((noinline)) truly_illegal_insn(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc, uintptr_t mstatus, insn_t insn) { redirect_trap(mepc, mstatus); } static inline int emulate_read_csr(int num, uintptr_t mstatus, uintptr_t* result) { uintptr_t counteren = EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U ? read_csr(mucounteren) : read_csr(mscounteren); switch (num) { case CSR_TIME: if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1)) return -1; *result = *mtime; return 0; #if __riscv_xlen == 32 case CSR_TIMEH: if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1)) return -1; *result = *mtime >> 32; return 0; #endif #if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) case CSR_FRM: if ((mstatus & MSTATUS_FS) == 0) break; *result = GET_FRM(); return 0; case CSR_FFLAGS: if ((mstatus & MSTATUS_FS) == 0) break; *result = GET_FFLAGS(); return 0; case CSR_FCSR: if ((mstatus & MSTATUS_FS) == 0) break; *result = GET_FCSR(); return 0; #endif } return -1; } static inline int emulate_write_csr(int num, uintptr_t value, uintptr_t mstatus) { switch (num) { #if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) case CSR_FRM: SET_FRM(value); return 0; case CSR_FFLAGS: SET_FFLAGS(value); return 0; case CSR_FCSR: SET_FCSR(value); return 0; #endif } return -1; } DECLARE_EMULATION_FUNC(emulate_system_opcode) { int rs1_num = (insn >> 15) & 0x1f; uintptr_t rs1_val = GET_RS1(insn, regs); int csr_num = (uint32_t)insn >> 20; uintptr_t csr_val, new_csr_val; if (emulate_read_csr(csr_num, mstatus, &csr_val)) return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); int do_write = rs1_num; switch (GET_RM(insn)) { case 0: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); case 1: new_csr_val = rs1_val; do_write = 1; break; case 2: new_csr_val = csr_val | rs1_val; break; case 3: new_csr_val = csr_val & ~rs1_val; break; case 4: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); case 5: new_csr_val = rs1_num; do_write = 1; break; case 6: new_csr_val = csr_val | rs1_num; break; case 7: new_csr_val = csr_val & ~rs1_num; break; } if (do_write && emulate_write_csr(csr_num, new_csr_val, mstatus)) return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); SET_RD(insn, regs, csr_val); }