1 /*
2 * RISC-V Emulation Helpers for QEMU.
3 *
4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
5 * Copyright (c) 2017-2018 SiFive, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2 or later, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "qemu/log.h"
22 #include "cpu.h"
23 #include "qemu/main-loop.h"
24 #include "exec/exec-all.h"
25 #include "exec/helper-proto.h"
26 #ifdef TARGET_CHERI
27 #include "cheri-helper-utils.h"
28 #endif
29
30
exception_str(uint32_t exception)31 static inline const char* exception_str(uint32_t exception)
32 {
33 // See Table 3.6 In privileged ISA spec (20190608-Priv-MSU-Ratified)
34 switch(exception) {
35 case RISCV_EXCP_INST_ADDR_MIS: return "Instruction address misaligned";
36 case RISCV_EXCP_INST_ACCESS_FAULT: return "Instruction access fault";
37 case RISCV_EXCP_ILLEGAL_INST: return "Illegal instruction";
38 case RISCV_EXCP_BREAKPOINT: return "Breakpoint";
39 case RISCV_EXCP_LOAD_ADDR_MIS: return "Load address misaligned";
40 case RISCV_EXCP_LOAD_ACCESS_FAULT: return "Load access fault";
41 case RISCV_EXCP_STORE_AMO_ADDR_MIS: return "Store/AMO address misaligned";
42 case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: return "Store/AMO access fault";
43 case RISCV_EXCP_U_ECALL: return "Environment call from U-mode";
44 case RISCV_EXCP_S_ECALL: return "Environment call from S-mode";
45 case RISCV_EXCP_VS_ECALL: return "Environment call from H-mode";
46 case RISCV_EXCP_M_ECALL: return "Environment call from M-mode";
47 case RISCV_EXCP_INST_PAGE_FAULT: return "Instruction page fault";
48 case RISCV_EXCP_LOAD_PAGE_FAULT: return "Load page fault";
49 // 14 Reserved for future standard use
50 case RISCV_EXCP_STORE_PAGE_FAULT: return "Store/AMO page fault";
51 // 16–23 Reserved for future standard use
52 case RISCV_EXCP_INST_GUEST_PAGE_FAULT: return "Guest instruction page fault";
53 case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: return "Guest load page fault";
54 case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: return "Guest store/AMO page fault";
55 // 24-31 Reserved for custom use (we use 28/0x1c) for CHERI
56 case RISCV_EXCP_CHERI: return "CHERI fault";
57 // 32–47 Reserved for future standard use
58 // 48-63 Reserved for custom use
59 // >64 Reserved for future standard use
60 default: return "Unknown exception";
61 }
62 }
63
64 /* Exceptions processing helpers */
riscv_raise_exception(CPURISCVState * env,uint32_t exception,uintptr_t pc)65 void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
66 uint32_t exception, uintptr_t pc)
67 {
68 CPUState *cs = env_cpu(env);
69 qemu_log_mask(CPU_LOG_INT, "%s: %s (%d)\n", __func__, exception_str(exception), exception);
70 cs->exception_index = exception;
71 // Expand this call to print debug info: cpu_loop_exit_restore(cs, pc);
72 if (pc) {
73 cpu_restore_state(cs, pc, true);
74 }
75 if (exception == RISCV_EXCP_ILLEGAL_INST) {
76 // Try to fetch the faulting instruction and store it in badaddr
77 uint32_t opcode = 0;
78 int ret = cpu_memory_rw_debug(env_cpu(env), PC_ADDR(env),
79 (uint8_t *)&opcode, sizeof(opcode),
80 /*is_write=*/false);
81 opcode = tswap32(opcode); // FIXME is this needed?
82 if (ret != 0) {
83 warn_report("RISCV_EXCP_ILLEGAL_INST: Could not read %zu bytes at "
84 "vaddr 0x" TARGET_FMT_lx "\r\n",
85 sizeof(opcode), PC_ADDR(env));
86 } else {
87 env->badaddr = opcode;
88 }
89 }
90 cpu_loop_exit(cs);
91 }
92
helper_raise_exception(CPURISCVState * env,uint32_t exception)93 void helper_raise_exception(CPURISCVState *env, uint32_t exception)
94 {
95 riscv_raise_exception(env, exception, 0);
96 }
97
helper_csrrw(CPURISCVState * env,target_ulong src,target_ulong csr)98 target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
99 target_ulong csr)
100 {
101 target_ulong val = 0;
102 if (riscv_csrrw(env, csr, &val, src, -1, GETPC()) < 0) {
103 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
104 }
105 return val;
106 }
107
helper_csrrs(CPURISCVState * env,target_ulong src,target_ulong csr,target_ulong rs1_pass)108 target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
109 target_ulong csr, target_ulong rs1_pass)
110 {
111 target_ulong val = 0;
112 if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0, GETPC()) < 0) {
113 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
114 }
115 return val;
116 }
117
helper_csrrc(CPURISCVState * env,target_ulong src,target_ulong csr,target_ulong rs1_pass)118 target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
119 target_ulong csr, target_ulong rs1_pass)
120 {
121 target_ulong val = 0;
122 if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0, GETPC()) < 0) {
123 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
124 }
125 return val;
126 }
127
128 #ifndef CONFIG_USER_ONLY
129
helper_sret(CPURISCVState * env,target_ulong cpu_pc_deb)130 target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
131 {
132 target_ulong prev_priv, prev_virt, mstatus;
133
134 if (!(env->priv >= PRV_S)) {
135 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
136 }
137
138 target_ulong retpc = GET_SPECIAL_REG(env, sepc, SEPCC);
139
140 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
141 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
142 }
143
144 if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
145 get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) {
146 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
147 }
148
149 mstatus = env->mstatus;
150
151 if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) {
152 /* We support Hypervisor extensions and virtulisation is disabled */
153 target_ulong hstatus = env->hstatus;
154
155 prev_priv = get_field(mstatus, MSTATUS_SPP);
156 prev_virt = get_field(hstatus, HSTATUS_SPV);
157
158 hstatus = set_field(hstatus, HSTATUS_SPV,
159 get_field(hstatus, HSTATUS_SP2V));
160 mstatus = set_field(mstatus, MSTATUS_SPP,
161 get_field(hstatus, HSTATUS_SP2P));
162 hstatus = set_field(hstatus, HSTATUS_SP2V, 0);
163 hstatus = set_field(hstatus, HSTATUS_SP2P, 0);
164 mstatus = set_field(mstatus, SSTATUS_SIE,
165 get_field(mstatus, SSTATUS_SPIE));
166 mstatus = set_field(mstatus, SSTATUS_SPIE, 1);
167
168 env->mstatus = mstatus;
169 env->hstatus = hstatus;
170
171 if (prev_virt) {
172 riscv_cpu_swap_hypervisor_regs(env);
173 }
174
175 riscv_cpu_set_virt_enabled(env, prev_virt);
176 } else {
177 prev_priv = get_field(mstatus, MSTATUS_SPP);
178
179 mstatus = set_field(mstatus,
180 env->priv_ver >= PRIV_VERSION_1_10_0 ?
181 MSTATUS_SIE : MSTATUS_UIE << prev_priv,
182 get_field(mstatus, MSTATUS_SPIE));
183 mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
184 mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
185 env->mstatus = mstatus;
186 }
187
188 riscv_cpu_set_mode(env, prev_priv);
189
190 #ifdef TARGET_CHERI
191 qemu_log_mask_and_addr(CPU_LOG_INSTR, cpu_get_recent_pc(env),
192 "%s: Updating PCC from SEPCC: " PRINT_CAP_FMTSTR "\n",
193 __func__, PRINT_CAP_ARGS(&env->SEPCC));
194 cheri_update_pcc_for_exc_return(&env->PCC, &env->SEPCC, retpc);
195 #endif
196 return retpc;
197 }
198
helper_mret(CPURISCVState * env,target_ulong cpu_pc_deb)199 target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
200 {
201 if (!(env->priv >= PRV_M)) {
202 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
203 }
204
205 target_ulong retpc = GET_SPECIAL_REG(env, mepc, MEPCC);
206 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
207 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
208 }
209
210 target_ulong mstatus = env->mstatus;
211 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
212 target_ulong prev_virt = MSTATUS_MPV_ISSET(env);
213 mstatus = set_field(mstatus,
214 env->priv_ver >= PRIV_VERSION_1_10_0 ?
215 MSTATUS_MIE : MSTATUS_UIE << prev_priv,
216 get_field(mstatus, MSTATUS_MPIE));
217 mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
218 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
219 #ifdef TARGET_RISCV32
220 env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0);
221 #else
222 mstatus = set_field(mstatus, MSTATUS_MPV, 0);
223 #endif
224 env->mstatus = mstatus;
225 riscv_cpu_set_mode(env, prev_priv);
226
227 if (riscv_has_ext(env, RVH)) {
228 if (prev_virt) {
229 riscv_cpu_swap_hypervisor_regs(env);
230 }
231
232 riscv_cpu_set_virt_enabled(env, prev_virt);
233 }
234
235 #ifdef TARGET_CHERI
236 qemu_log_mask_and_addr(CPU_LOG_INSTR, cpu_get_recent_pc(env),
237 "%s: Updating PCC from MEPCC: " PRINT_CAP_FMTSTR "\n",
238 __func__, PRINT_CAP_ARGS(&env->MEPCC));
239 cheri_update_pcc_for_exc_return(&env->PCC, &env->MEPCC, retpc);
240 #endif
241 return retpc;
242 }
243
244 #ifdef CONFIG_MIPS_LOG_INSTR
HELPER(log_gpr_write)245 void HELPER(log_gpr_write)(uint32_t regnum, target_ulong value, target_ulong pc)
246 {
247 qemu_log_mask_and_addr(CPU_LOG_INSTR, pc,
248 " Write %s = " TARGET_FMT_lx "\n",
249 riscv_int_regnames[regnum], value);
250 }
251 #endif
252
helper_wfi(CPURISCVState * env)253 void helper_wfi(CPURISCVState *env)
254 {
255 CPUState *cs = env_cpu(env);
256
257 if ((env->priv == PRV_S &&
258 env->priv_ver >= PRIV_VERSION_1_10_0 &&
259 get_field(env->mstatus, MSTATUS_TW)) ||
260 riscv_cpu_virt_enabled(env)) {
261 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
262 } else {
263 cs->halted = 1;
264 cs->exception_index = EXCP_HLT;
265 cpu_loop_exit(cs);
266 }
267 }
268
helper_tlb_flush(CPURISCVState * env)269 void helper_tlb_flush(CPURISCVState *env)
270 {
271 CPUState *cs = env_cpu(env);
272 if (!(env->priv >= PRV_S) ||
273 (env->priv == PRV_S &&
274 env->priv_ver >= PRIV_VERSION_1_10_0 &&
275 get_field(env->mstatus, MSTATUS_TVM))) {
276 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
277 } else {
278 tlb_flush(cs);
279 }
280 }
281
282 #endif /* !CONFIG_USER_ONLY */
283