xref: /qemu/target/riscv/op_helper.c (revision b21e2380)
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 "cpu.h"
22 #include "qemu/main-loop.h"
23 #include "exec/exec-all.h"
24 #include "exec/helper-proto.h"
25 
26 /* Exceptions processing helpers */
27 void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
28                                           uint32_t exception, uintptr_t pc)
29 {
30     CPUState *cs = env_cpu(env);
31     cs->exception_index = exception;
32     cpu_loop_exit_restore(cs, pc);
33 }
34 
35 void helper_raise_exception(CPURISCVState *env, uint32_t exception)
36 {
37     riscv_raise_exception(env, exception, 0);
38 }
39 
40 target_ulong helper_csrr(CPURISCVState *env, int csr)
41 {
42     target_ulong val = 0;
43     RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0);
44 
45     if (ret != RISCV_EXCP_NONE) {
46         riscv_raise_exception(env, ret, GETPC());
47     }
48     return val;
49 }
50 
51 void helper_csrw(CPURISCVState *env, int csr, target_ulong src)
52 {
53     target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1;
54     RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask);
55 
56     if (ret != RISCV_EXCP_NONE) {
57         riscv_raise_exception(env, ret, GETPC());
58     }
59 }
60 
61 target_ulong helper_csrrw(CPURISCVState *env, int csr,
62                           target_ulong src, target_ulong write_mask)
63 {
64     target_ulong val = 0;
65     RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask);
66 
67     if (ret != RISCV_EXCP_NONE) {
68         riscv_raise_exception(env, ret, GETPC());
69     }
70     return val;
71 }
72 
73 target_ulong helper_csrr_i128(CPURISCVState *env, int csr)
74 {
75     Int128 rv = int128_zero();
76     RISCVException ret = riscv_csrrw_i128(env, csr, &rv,
77                                           int128_zero(),
78                                           int128_zero());
79 
80     if (ret != RISCV_EXCP_NONE) {
81         riscv_raise_exception(env, ret, GETPC());
82     }
83 
84     env->retxh = int128_gethi(rv);
85     return int128_getlo(rv);
86 }
87 
88 void helper_csrw_i128(CPURISCVState *env, int csr,
89                       target_ulong srcl, target_ulong srch)
90 {
91     RISCVException ret = riscv_csrrw_i128(env, csr, NULL,
92                                           int128_make128(srcl, srch),
93                                           UINT128_MAX);
94 
95     if (ret != RISCV_EXCP_NONE) {
96         riscv_raise_exception(env, ret, GETPC());
97     }
98 }
99 
100 target_ulong helper_csrrw_i128(CPURISCVState *env, int csr,
101                        target_ulong srcl, target_ulong srch,
102                        target_ulong maskl, target_ulong maskh)
103 {
104     Int128 rv = int128_zero();
105     RISCVException ret = riscv_csrrw_i128(env, csr, &rv,
106                                           int128_make128(srcl, srch),
107                                           int128_make128(maskl, maskh));
108 
109     if (ret != RISCV_EXCP_NONE) {
110         riscv_raise_exception(env, ret, GETPC());
111     }
112 
113     env->retxh = int128_gethi(rv);
114     return int128_getlo(rv);
115 }
116 
117 #ifndef CONFIG_USER_ONLY
118 
119 target_ulong helper_sret(CPURISCVState *env)
120 {
121     uint64_t mstatus;
122     target_ulong prev_priv, prev_virt;
123 
124     if (!(env->priv >= PRV_S)) {
125         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
126     }
127 
128     target_ulong retpc = env->sepc;
129     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
130         riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
131     }
132 
133     if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) {
134         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
135     }
136 
137     if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
138         get_field(env->hstatus, HSTATUS_VTSR)) {
139         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
140     }
141 
142     mstatus = env->mstatus;
143 
144     if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) {
145         /* We support Hypervisor extensions and virtulisation is disabled */
146         target_ulong hstatus = env->hstatus;
147 
148         prev_priv = get_field(mstatus, MSTATUS_SPP);
149         prev_virt = get_field(hstatus, HSTATUS_SPV);
150 
151         hstatus = set_field(hstatus, HSTATUS_SPV, 0);
152         mstatus = set_field(mstatus, MSTATUS_SPP, 0);
153         mstatus = set_field(mstatus, SSTATUS_SIE,
154                             get_field(mstatus, SSTATUS_SPIE));
155         mstatus = set_field(mstatus, SSTATUS_SPIE, 1);
156 
157         env->mstatus = mstatus;
158         env->hstatus = hstatus;
159 
160         if (prev_virt) {
161             riscv_cpu_swap_hypervisor_regs(env);
162         }
163 
164         riscv_cpu_set_virt_enabled(env, prev_virt);
165     } else {
166         prev_priv = get_field(mstatus, MSTATUS_SPP);
167 
168         mstatus = set_field(mstatus, MSTATUS_SIE,
169                             get_field(mstatus, MSTATUS_SPIE));
170         mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
171         mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
172         env->mstatus = mstatus;
173     }
174 
175     riscv_cpu_set_mode(env, prev_priv);
176 
177     return retpc;
178 }
179 
180 target_ulong helper_mret(CPURISCVState *env)
181 {
182     if (!(env->priv >= PRV_M)) {
183         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
184     }
185 
186     target_ulong retpc = env->mepc;
187     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
188         riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
189     }
190 
191     uint64_t mstatus = env->mstatus;
192     target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
193 
194     if (riscv_feature(env, RISCV_FEATURE_PMP) &&
195         !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
196         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
197     }
198 
199     target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV);
200     mstatus = set_field(mstatus, MSTATUS_MIE,
201                         get_field(mstatus, MSTATUS_MPIE));
202     mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
203     mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
204     mstatus = set_field(mstatus, MSTATUS_MPV, 0);
205     env->mstatus = mstatus;
206     riscv_cpu_set_mode(env, prev_priv);
207 
208     if (riscv_has_ext(env, RVH)) {
209         if (prev_virt) {
210             riscv_cpu_swap_hypervisor_regs(env);
211         }
212 
213         riscv_cpu_set_virt_enabled(env, prev_virt);
214     }
215 
216     return retpc;
217 }
218 
219 void helper_wfi(CPURISCVState *env)
220 {
221     CPUState *cs = env_cpu(env);
222     bool rvs = riscv_has_ext(env, RVS);
223     bool prv_u = env->priv == PRV_U;
224     bool prv_s = env->priv == PRV_S;
225 
226     if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) ||
227         (rvs && prv_u && !riscv_cpu_virt_enabled(env))) {
228         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
229     } else if (riscv_cpu_virt_enabled(env) && (prv_u ||
230         (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) {
231         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
232     } else {
233         cs->halted = 1;
234         cs->exception_index = EXCP_HLT;
235         cpu_loop_exit(cs);
236     }
237 }
238 
239 void helper_tlb_flush(CPURISCVState *env)
240 {
241     CPUState *cs = env_cpu(env);
242     if (!(env->priv >= PRV_S) ||
243         (env->priv == PRV_S &&
244          get_field(env->mstatus, MSTATUS_TVM))) {
245         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
246     } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
247                get_field(env->hstatus, HSTATUS_VTVM)) {
248         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
249     } else {
250         tlb_flush(cs);
251     }
252 }
253 
254 void helper_hyp_tlb_flush(CPURISCVState *env)
255 {
256     CPUState *cs = env_cpu(env);
257 
258     if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) {
259         riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
260     }
261 
262     if (env->priv == PRV_M ||
263         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) {
264         tlb_flush(cs);
265         return;
266     }
267 
268     riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
269 }
270 
271 void helper_hyp_gvma_tlb_flush(CPURISCVState *env)
272 {
273     if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) &&
274         get_field(env->mstatus, MSTATUS_TVM)) {
275         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
276     }
277 
278     helper_hyp_tlb_flush(env);
279 }
280 
281 target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address)
282 {
283     int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
284 
285     return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC());
286 }
287 
288 target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address)
289 {
290     int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
291 
292     return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC());
293 }
294 
295 #endif /* !CONFIG_USER_ONLY */
296