RISC-V Proxy Kernel
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

167 lines
4.9 KiB

#include "emulation.h"
#include "fp_emulation.h"
#include "config.h"
#include "unprivileged_memory.h"
#include "mtrap.h"
#include <limits.h>
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);
}