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