xref: /qemu/target/riscv/op_helper.c (revision 4ac2ee19)
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 
27 /* Exceptions processing helpers */
28 void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
29                                           uint32_t exception, uintptr_t pc)
30 {
31     CPUState *cs = env_cpu(env);
32     qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception);
33     cs->exception_index = exception;
34     cpu_loop_exit_restore(cs, pc);
35 }
36 
37 void helper_raise_exception(CPURISCVState *env, uint32_t exception)
38 {
39     riscv_raise_exception(env, exception, 0);
40 }
41 
42 target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
43         target_ulong csr)
44 {
45     target_ulong val = 0;
46     int ret = riscv_csrrw(env, csr, &val, src, -1);
47 
48     if (ret < 0) {
49         riscv_raise_exception(env, -ret, GETPC());
50     }
51     return val;
52 }
53 
54 target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
55         target_ulong csr, target_ulong rs1_pass)
56 {
57     target_ulong val = 0;
58     int ret = riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0);
59 
60     if (ret < 0) {
61         riscv_raise_exception(env, -ret, GETPC());
62     }
63     return val;
64 }
65 
66 target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
67         target_ulong csr, target_ulong rs1_pass)
68 {
69     target_ulong val = 0;
70     int ret = riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0);
71 
72     if (ret < 0) {
73         riscv_raise_exception(env, -ret, GETPC());
74     }
75     return val;
76 }
77 
78 #ifndef CONFIG_USER_ONLY
79 
80 target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
81 {
82     target_ulong prev_priv, prev_virt, mstatus;
83 
84     if (!(env->priv >= PRV_S)) {
85         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
86     }
87 
88     target_ulong retpc = env->sepc;
89     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
90         riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
91     }
92 
93     if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) {
94         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
95     }
96 
97     if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
98         get_field(env->hstatus, HSTATUS_VTSR)) {
99         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
100     }
101 
102     mstatus = env->mstatus;
103 
104     if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) {
105         /* We support Hypervisor extensions and virtulisation is disabled */
106         target_ulong hstatus = env->hstatus;
107 
108         prev_priv = get_field(mstatus, MSTATUS_SPP);
109         prev_virt = get_field(hstatus, HSTATUS_SPV);
110 
111         hstatus = set_field(hstatus, HSTATUS_SPV, 0);
112         mstatus = set_field(mstatus, MSTATUS_SPP, 0);
113         mstatus = set_field(mstatus, SSTATUS_SIE,
114                             get_field(mstatus, SSTATUS_SPIE));
115         mstatus = set_field(mstatus, SSTATUS_SPIE, 1);
116 
117         env->mstatus = mstatus;
118         env->hstatus = hstatus;
119 
120         if (prev_virt) {
121             riscv_cpu_swap_hypervisor_regs(env);
122         }
123 
124         riscv_cpu_set_virt_enabled(env, prev_virt);
125     } else {
126         prev_priv = get_field(mstatus, MSTATUS_SPP);
127 
128         mstatus = set_field(mstatus, MSTATUS_SIE,
129                             get_field(mstatus, MSTATUS_SPIE));
130         mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
131         mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
132         env->mstatus = mstatus;
133     }
134 
135     riscv_cpu_set_mode(env, prev_priv);
136 
137     return retpc;
138 }
139 
140 target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
141 {
142     if (!(env->priv >= PRV_M)) {
143         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
144     }
145 
146     target_ulong retpc = env->mepc;
147     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
148         riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
149     }
150 
151     target_ulong mstatus = env->mstatus;
152     target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
153     target_ulong prev_virt = MSTATUS_MPV_ISSET(env);
154     mstatus = set_field(mstatus, MSTATUS_MIE,
155                         get_field(mstatus, MSTATUS_MPIE));
156     mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
157     mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
158 #ifdef TARGET_RISCV32
159     env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0);
160 #else
161     mstatus = set_field(mstatus, MSTATUS_MPV, 0);
162 #endif
163     env->mstatus = mstatus;
164     riscv_cpu_set_mode(env, prev_priv);
165 
166     if (riscv_has_ext(env, RVH)) {
167         if (prev_virt) {
168             riscv_cpu_swap_hypervisor_regs(env);
169         }
170 
171         riscv_cpu_set_virt_enabled(env, prev_virt);
172     }
173 
174     return retpc;
175 }
176 
177 void helper_wfi(CPURISCVState *env)
178 {
179     CPUState *cs = env_cpu(env);
180 
181     if ((env->priv == PRV_S &&
182         get_field(env->mstatus, MSTATUS_TW)) ||
183         riscv_cpu_virt_enabled(env)) {
184         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
185     } else {
186         cs->halted = 1;
187         cs->exception_index = EXCP_HLT;
188         cpu_loop_exit(cs);
189     }
190 }
191 
192 void helper_tlb_flush(CPURISCVState *env)
193 {
194     CPUState *cs = env_cpu(env);
195     if (!(env->priv >= PRV_S) ||
196         (env->priv == PRV_S &&
197          get_field(env->mstatus, MSTATUS_TVM))) {
198         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
199     } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
200                get_field(env->hstatus, HSTATUS_VTVM)) {
201         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
202     } else {
203         tlb_flush(cs);
204     }
205 }
206 
207 void helper_hyp_tlb_flush(CPURISCVState *env)
208 {
209     CPUState *cs = env_cpu(env);
210 
211     if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) {
212         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
213     }
214 
215     if (env->priv == PRV_M ||
216         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) {
217         tlb_flush(cs);
218         return;
219     }
220 
221     riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
222 }
223 
224 void helper_hyp_gvma_tlb_flush(CPURISCVState *env)
225 {
226     if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) &&
227         get_field(env->mstatus, MSTATUS_TVM)) {
228         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
229     }
230 
231     helper_hyp_tlb_flush(env);
232 }
233 
234 target_ulong helper_hyp_load(CPURISCVState *env, target_ulong address,
235                              target_ulong attrs, target_ulong memop)
236 {
237     if (env->priv == PRV_M ||
238         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) ||
239         (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) &&
240             get_field(env->hstatus, HSTATUS_HU))) {
241         target_ulong pte;
242 
243         riscv_cpu_set_two_stage_lookup(env, true);
244 
245         switch (memop) {
246         case MO_SB:
247             pte = cpu_ldsb_data_ra(env, address, GETPC());
248             break;
249         case MO_UB:
250             pte = cpu_ldub_data_ra(env, address, GETPC());
251             break;
252         case MO_TESW:
253             pte = cpu_ldsw_data_ra(env, address, GETPC());
254             break;
255         case MO_TEUW:
256             pte = cpu_lduw_data_ra(env, address, GETPC());
257             break;
258         case MO_TESL:
259             pte = cpu_ldl_data_ra(env, address, GETPC());
260             break;
261         case MO_TEUL:
262             pte = cpu_ldl_data_ra(env, address, GETPC());
263             break;
264         case MO_TEQ:
265             pte = cpu_ldq_data_ra(env, address, GETPC());
266             break;
267         default:
268             g_assert_not_reached();
269         }
270 
271         riscv_cpu_set_two_stage_lookup(env, false);
272 
273         return pte;
274     }
275 
276     if (riscv_cpu_virt_enabled(env)) {
277         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
278     } else {
279         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
280     }
281     return 0;
282 }
283 
284 void helper_hyp_store(CPURISCVState *env, target_ulong address,
285                       target_ulong val, target_ulong attrs, target_ulong memop)
286 {
287     if (env->priv == PRV_M ||
288         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) ||
289         (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) &&
290             get_field(env->hstatus, HSTATUS_HU))) {
291         riscv_cpu_set_two_stage_lookup(env, true);
292 
293         switch (memop) {
294         case MO_SB:
295         case MO_UB:
296             cpu_stb_data_ra(env, address, val, GETPC());
297             break;
298         case MO_TESW:
299         case MO_TEUW:
300             cpu_stw_data_ra(env, address, val, GETPC());
301             break;
302         case MO_TESL:
303         case MO_TEUL:
304             cpu_stl_data_ra(env, address, val, GETPC());
305             break;
306         case MO_TEQ:
307             cpu_stq_data_ra(env, address, val, GETPC());
308             break;
309         default:
310             g_assert_not_reached();
311         }
312 
313         riscv_cpu_set_two_stage_lookup(env, false);
314 
315         return;
316     }
317 
318     if (riscv_cpu_virt_enabled(env)) {
319         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
320     } else {
321         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
322     }
323 }
324 
325 target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address,
326                                target_ulong attrs, target_ulong memop)
327 {
328     if (env->priv == PRV_M ||
329         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) ||
330         (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) &&
331             get_field(env->hstatus, HSTATUS_HU))) {
332         target_ulong pte;
333 
334         riscv_cpu_set_two_stage_lookup(env, true);
335 
336         switch (memop) {
337         case MO_TEUL:
338             pte = cpu_ldub_data_ra(env, address, GETPC());
339             break;
340         case MO_TEUW:
341             pte = cpu_lduw_data_ra(env, address, GETPC());
342             break;
343         default:
344             g_assert_not_reached();
345         }
346 
347         riscv_cpu_set_two_stage_lookup(env, false);
348 
349         return pte;
350     }
351 
352     if (riscv_cpu_virt_enabled(env)) {
353         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
354     } else {
355         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
356     }
357     return 0;
358 }
359 
360 #endif /* !CONFIG_USER_ONLY */
361