1// See LICENSE for license details. 2 3#include "emulation.h" 4#include "fp_emulation.h" 5#include "config.h" 6#include "unprivileged_memory.h" 7#include "mtrap.h" 8#include <limits.h> 9 10static DECLARE_EMULATION_FUNC(emulate_rvc) 11{ 12#ifdef __riscv_compressed 13 // the only emulable RVC instructions are FP loads and stores. 14# if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) 15 write_csr(mepc, mepc + 2); 16 17 // if FPU is disabled, punt back to the OS 18 if (unlikely((mstatus & MSTATUS_FS) == 0)) 19 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 20 21 if ((insn & MASK_C_FLD) == MATCH_C_FLD) { 22 uintptr_t addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn); 23 if (unlikely(addr % sizeof(uintptr_t))) 24 return misaligned_load_trap(regs, mcause, mepc); 25 SET_F64_RD(RVC_RS2S(insn) << SH_RD, regs, load_uint64_t((void *)addr, mepc)); 26 } else if ((insn & MASK_C_FLDSP) == MATCH_C_FLDSP) { 27 uintptr_t addr = GET_SP(regs) + RVC_LDSP_IMM(insn); 28 if (unlikely(addr % sizeof(uintptr_t))) 29 return misaligned_load_trap(regs, mcause, mepc); 30 SET_F64_RD(insn, regs, load_uint64_t((void *)addr, mepc)); 31 } else if ((insn & MASK_C_FSD) == MATCH_C_FSD) { 32 uintptr_t addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn); 33 if (unlikely(addr % sizeof(uintptr_t))) 34 return misaligned_store_trap(regs, mcause, mepc); 35 store_uint64_t((void *)addr, GET_F64_RS2(RVC_RS2S(insn) << SH_RS2, regs), mepc); 36 } else if ((insn & MASK_C_FSDSP) == MATCH_C_FSDSP) { 37 uintptr_t addr = GET_SP(regs) + RVC_SDSP_IMM(insn); 38 if (unlikely(addr % sizeof(uintptr_t))) 39 return misaligned_store_trap(regs, mcause, mepc); 40 store_uint64_t((void *)addr, GET_F64_RS2(RVC_RS2(insn) << SH_RS2, regs), mepc); 41 } else 42# if __riscv_xlen == 32 43 if ((insn & MASK_C_FLW) == MATCH_C_FLW) { 44 uintptr_t addr = GET_RS1S(insn, regs) + RVC_LW_IMM(insn); 45 if (unlikely(addr % 4)) 46 return misaligned_load_trap(regs, mcause, mepc); 47 SET_F32_RD(RVC_RS2S(insn) << SH_RD, regs, load_int32_t((void *)addr, mepc)); 48 } else if ((insn & MASK_C_FLWSP) == MATCH_C_FLWSP) { 49 uintptr_t addr = GET_SP(regs) + RVC_LWSP_IMM(insn); 50 if (unlikely(addr % 4)) 51 return misaligned_load_trap(regs, mcause, mepc); 52 SET_F32_RD(insn, regs, load_int32_t((void *)addr, mepc)); 53 } else if ((insn & MASK_C_FSW) == MATCH_C_FSW) { 54 uintptr_t addr = GET_RS1S(insn, regs) + RVC_LW_IMM(insn); 55 if (unlikely(addr % 4)) 56 return misaligned_store_trap(regs, mcause, mepc); 57 store_uint32_t((void *)addr, GET_F32_RS2(RVC_RS2S(insn) << SH_RS2, regs), mepc); 58 } else if ((insn & MASK_C_FSWSP) == MATCH_C_FSWSP) { 59 uintptr_t addr = GET_SP(regs) + RVC_SWSP_IMM(insn); 60 if (unlikely(addr % 4)) 61 return misaligned_store_trap(regs, mcause, mepc); 62 store_uint32_t((void *)addr, GET_F32_RS2(RVC_RS2(insn) << SH_RS2, regs), mepc); 63 } else 64# endif 65# endif 66#endif 67 68 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 69} 70 71void illegal_insn_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) 72{ 73 asm (".pushsection .rodata\n" 74 "illegal_insn_trap_table:\n" 75 " .word truly_illegal_insn\n" 76#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) 77 " .word emulate_float_load\n" 78#else 79 " .word truly_illegal_insn\n" 80#endif 81 " .word truly_illegal_insn\n" 82 " .word truly_illegal_insn\n" 83 " .word truly_illegal_insn\n" 84 " .word truly_illegal_insn\n" 85 " .word truly_illegal_insn\n" 86 " .word truly_illegal_insn\n" 87 " .word truly_illegal_insn\n" 88#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) 89 " .word emulate_float_store\n" 90#else 91 " .word truly_illegal_insn\n" 92#endif 93 " .word truly_illegal_insn\n" 94 " .word truly_illegal_insn\n" 95#if !defined(__riscv_muldiv) 96 " .word emulate_mul_div\n" 97#else 98 " .word truly_illegal_insn\n" 99#endif 100 " .word truly_illegal_insn\n" 101#if !defined(__riscv_muldiv) && __riscv_xlen >= 64 102 " .word emulate_mul_div32\n" 103#else 104 " .word truly_illegal_insn\n" 105#endif 106 " .word truly_illegal_insn\n" 107#ifdef PK_ENABLE_FP_EMULATION 108 " .word emulate_fmadd\n" 109 " .word emulate_fmadd\n" 110 " .word emulate_fmadd\n" 111 " .word emulate_fmadd\n" 112 " .word emulate_fp\n" 113#else 114 " .word truly_illegal_insn\n" 115 " .word truly_illegal_insn\n" 116 " .word truly_illegal_insn\n" 117 " .word truly_illegal_insn\n" 118 " .word truly_illegal_insn\n" 119#endif 120 " .word truly_illegal_insn\n" 121 " .word truly_illegal_insn\n" 122 " .word truly_illegal_insn\n" 123 " .word truly_illegal_insn\n" 124 " .word truly_illegal_insn\n" 125 " .word truly_illegal_insn\n" 126 " .word truly_illegal_insn\n" 127 " .word emulate_system_opcode\n" 128 " .word truly_illegal_insn\n" 129 " .word truly_illegal_insn\n" 130 " .word truly_illegal_insn\n" 131 " .popsection"); 132 133 uintptr_t mstatus = read_csr(mstatus); 134 insn_t insn = read_csr(mbadaddr); 135 136 if (unlikely((insn & 3) != 3)) { 137 if (insn == 0) 138 insn = get_insn(mepc, &mstatus); 139 if ((insn & 3) != 3) 140 return emulate_rvc(regs, mcause, mepc, mstatus, insn); 141 } 142 143 write_csr(mepc, mepc + 4); 144 145 extern uint32_t illegal_insn_trap_table[]; 146 uint32_t* pf = (void*)illegal_insn_trap_table + (insn & 0x7c); 147 emulation_func f = (emulation_func)(uintptr_t)*pf; 148 f(regs, mcause, mepc, mstatus, insn); 149} 150 151__attribute__((noinline)) 152DECLARE_EMULATION_FUNC(truly_illegal_insn) 153{ 154 return redirect_trap(mepc, mstatus, insn); 155} 156 157static inline int emulate_read_csr(int num, uintptr_t mstatus, uintptr_t* result) 158{ 159 uintptr_t counteren = -1; 160 if (EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U) 161 counteren = read_csr(scounteren); 162 163 switch (num) 164 { 165 case CSR_CYCLE: 166 if (!((counteren >> (CSR_CYCLE - CSR_CYCLE)) & 1)) 167 return -1; 168 *result = read_csr(mcycle); 169 return 0; 170 case CSR_TIME: 171 if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1)) 172 return -1; 173 *result = *mtime; 174 return 0; 175 case CSR_INSTRET: 176 if (!((counteren >> (CSR_INSTRET - CSR_CYCLE)) & 1)) 177 return -1; 178 *result = read_csr(minstret); 179 return 0; 180 case CSR_MHPMCOUNTER3: 181 if (!((counteren >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1)) 182 return -1; 183 *result = read_csr(mhpmcounter3); 184 return 0; 185 case CSR_MHPMCOUNTER4: 186 if (!((counteren >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1)) 187 return -1; 188 *result = read_csr(mhpmcounter4); 189 return 0; 190#if __riscv_xlen == 32 191 case CSR_CYCLEH: 192 if (!((counteren >> (CSR_CYCLE - CSR_CYCLE)) & 1)) 193 return -1; 194 *result = read_csr(mcycleh); 195 return 0; 196 case CSR_TIMEH: 197 if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1)) 198 return -1; 199 *result = *mtime >> 32; 200 return 0; 201 case CSR_INSTRETH: 202 if (!((counteren >> (CSR_INSTRET - CSR_CYCLE)) & 1)) 203 return -1; 204 *result = read_csr(minstreth); 205 return 0; 206 case CSR_MHPMCOUNTER3H: 207 if (!((counteren >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1)) 208 return -1; 209 *result = read_csr(mhpmcounter3h); 210 return 0; 211 case CSR_MHPMCOUNTER4H: 212 if (!((counteren >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1)) 213 return -1; 214 *result = read_csr(mhpmcounter4h); 215 return 0; 216#endif 217 case CSR_MHPMEVENT3: 218 *result = read_csr(mhpmevent3); 219 return 0; 220 case CSR_MHPMEVENT4: 221 *result = read_csr(mhpmevent4); 222 return 0; 223#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) 224 case CSR_FRM: 225 if ((mstatus & MSTATUS_FS) == 0) break; 226 *result = GET_FRM(); 227 return 0; 228 case CSR_FFLAGS: 229 if ((mstatus & MSTATUS_FS) == 0) break; 230 *result = GET_FFLAGS(); 231 return 0; 232 case CSR_FCSR: 233 if ((mstatus & MSTATUS_FS) == 0) break; 234 *result = GET_FCSR(); 235 return 0; 236#endif 237 } 238 return -1; 239} 240 241static inline int emulate_write_csr(int num, uintptr_t value, uintptr_t mstatus) 242{ 243 switch (num) 244 { 245 case CSR_CYCLE: write_csr(mcycle, value); return 0; 246 case CSR_INSTRET: write_csr(minstret, value); return 0; 247 case CSR_MHPMCOUNTER3: write_csr(mhpmcounter3, value); return 0; 248 case CSR_MHPMCOUNTER4: write_csr(mhpmcounter4, value); return 0; 249#if __riscv_xlen == 32 250 case CSR_CYCLEH: write_csr(mcycleh, value); return 0; 251 case CSR_INSTRETH: write_csr(minstreth, value); return 0; 252 case CSR_MHPMCOUNTER3H: write_csr(mhpmcounter3h, value); return 0; 253 case CSR_MHPMCOUNTER4H: write_csr(mhpmcounter4h, value); return 0; 254#endif 255 case CSR_MHPMEVENT3: write_csr(mhpmevent3, value); return 0; 256 case CSR_MHPMEVENT4: write_csr(mhpmevent4, value); return 0; 257#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION) 258 case CSR_FRM: SET_FRM(value); return 0; 259 case CSR_FFLAGS: SET_FFLAGS(value); return 0; 260 case CSR_FCSR: SET_FCSR(value); return 0; 261#endif 262 } 263 return -1; 264} 265 266DECLARE_EMULATION_FUNC(emulate_system_opcode) 267{ 268 int rs1_num = (insn >> 15) & 0x1f; 269 uintptr_t rs1_val = GET_RS1(insn, regs); 270 int csr_num = (uint32_t)insn >> 20; 271 uintptr_t csr_val, new_csr_val; 272 273 if (emulate_read_csr(csr_num, mstatus, &csr_val)) 274 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 275 276 int do_write = rs1_num; 277 switch (GET_RM(insn)) 278 { 279 case 0: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 280 case 1: new_csr_val = rs1_val; do_write = 1; break; 281 case 2: new_csr_val = csr_val | rs1_val; break; 282 case 3: new_csr_val = csr_val & ~rs1_val; break; 283 case 4: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 284 case 5: new_csr_val = rs1_num; do_write = 1; break; 285 case 6: new_csr_val = csr_val | rs1_num; break; 286 case 7: new_csr_val = csr_val & ~rs1_num; break; 287 } 288 289 if (do_write && emulate_write_csr(csr_num, new_csr_val, mstatus)) 290 return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); 291 292 SET_RD(insn, regs, csr_val); 293} 294