]>
Commit | Line | Data |
---|---|---|
0c3e702a MC |
1 | /* |
2 | * RISC-V Emulation Helpers for QEMU. | |
3 | * | |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, [email protected] | |
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" | |
0c3e702a MC |
21 | #include "cpu.h" |
22 | #include "qemu/main-loop.h" | |
23 | #include "exec/exec-all.h" | |
24 | #include "exec/helper-proto.h" | |
25 | ||
0c3e702a | 26 | /* Exceptions processing helpers */ |
8905770b MAL |
27 | G_NORETURN void riscv_raise_exception(CPURISCVState *env, |
28 | uint32_t exception, uintptr_t pc) | |
0c3e702a | 29 | { |
3109cd98 | 30 | CPUState *cs = env_cpu(env); |
0c3e702a MC |
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 | { | |
fb738839 | 37 | riscv_raise_exception(env, exception, 0); |
0c3e702a MC |
38 | } |
39 | ||
a974879b | 40 | target_ulong helper_csrr(CPURISCVState *env, int csr) |
0c3e702a | 41 | { |
c7b95171 | 42 | target_ulong val = 0; |
a974879b | 43 | RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); |
57cb2083 | 44 | |
533c91e8 AF |
45 | if (ret != RISCV_EXCP_NONE) { |
46 | riscv_raise_exception(env, ret, GETPC()); | |
c7b95171 MC |
47 | } |
48 | return val; | |
0c3e702a MC |
49 | } |
50 | ||
a974879b | 51 | void helper_csrw(CPURISCVState *env, int csr, target_ulong src) |
0c3e702a | 52 | { |
83b519b8 LZ |
53 | target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1; |
54 | RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask); | |
57cb2083 | 55 | |
533c91e8 AF |
56 | if (ret != RISCV_EXCP_NONE) { |
57 | riscv_raise_exception(env, ret, GETPC()); | |
0c3e702a | 58 | } |
0c3e702a MC |
59 | } |
60 | ||
a974879b RH |
61 | target_ulong helper_csrrw(CPURISCVState *env, int csr, |
62 | target_ulong src, target_ulong write_mask) | |
0c3e702a | 63 | { |
c7b95171 | 64 | target_ulong val = 0; |
a974879b | 65 | RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); |
57cb2083 | 66 | |
533c91e8 AF |
67 | if (ret != RISCV_EXCP_NONE) { |
68 | riscv_raise_exception(env, ret, GETPC()); | |
0c3e702a | 69 | } |
c7b95171 | 70 | return val; |
0c3e702a MC |
71 | } |
72 | ||
961738ff FP |
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 | ||
0c3e702a MC |
117 | #ifndef CONFIG_USER_ONLY |
118 | ||
b655dc7c | 119 | target_ulong helper_sret(CPURISCVState *env) |
0c3e702a | 120 | { |
284d697c YJ |
121 | uint64_t mstatus; |
122 | target_ulong prev_priv, prev_virt; | |
e3fba4ba | 123 | |
0c3e702a | 124 | if (!(env->priv >= PRV_S)) { |
fb738839 | 125 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
0c3e702a MC |
126 | } |
127 | ||
128 | target_ulong retpc = env->sepc; | |
129 | if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { | |
fb738839 | 130 | riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); |
0c3e702a MC |
131 | } |
132 | ||
1a9540d1 | 133 | if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { |
fb738839 | 134 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
7f2b5ff1 MC |
135 | } |
136 | ||
e39a8320 AF |
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 | ||
e3fba4ba AF |
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 | ||
f2d5850f AF |
151 | hstatus = set_field(hstatus, HSTATUS_SPV, 0); |
152 | mstatus = set_field(mstatus, MSTATUS_SPP, 0); | |
e3fba4ba AF |
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 | ||
1a9540d1 AF |
168 | mstatus = set_field(mstatus, MSTATUS_SIE, |
169 | get_field(mstatus, MSTATUS_SPIE)); | |
e3fba4ba AF |
170 | mstatus = set_field(mstatus, MSTATUS_SPIE, 1); |
171 | mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); | |
172 | env->mstatus = mstatus; | |
173 | } | |
174 | ||
fb738839 | 175 | riscv_cpu_set_mode(env, prev_priv); |
0c3e702a MC |
176 | |
177 | return retpc; | |
178 | } | |
179 | ||
b655dc7c | 180 | target_ulong helper_mret(CPURISCVState *env) |
0c3e702a MC |
181 | { |
182 | if (!(env->priv >= PRV_M)) { | |
fb738839 | 183 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
0c3e702a MC |
184 | } |
185 | ||
186 | target_ulong retpc = env->mepc; | |
187 | if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { | |
fb738839 | 188 | riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); |
0c3e702a MC |
189 | } |
190 | ||
284d697c | 191 | uint64_t mstatus = env->mstatus; |
0c3e702a | 192 | target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); |
d102f19a | 193 | |
0fbb5d2d NS |
194 | if (riscv_feature(env, RISCV_FEATURE_PMP) && |
195 | !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { | |
d102f19a AP |
196 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
197 | } | |
198 | ||
284d697c | 199 | target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); |
1a9540d1 AF |
200 | mstatus = set_field(mstatus, MSTATUS_MIE, |
201 | get_field(mstatus, MSTATUS_MPIE)); | |
a37f21c2 | 202 | mstatus = set_field(mstatus, MSTATUS_MPIE, 1); |
0c3e702a | 203 | mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); |
e3fba4ba | 204 | mstatus = set_field(mstatus, MSTATUS_MPV, 0); |
c7b95171 | 205 | env->mstatus = mstatus; |
e3fba4ba AF |
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 | } | |
0c3e702a MC |
215 | |
216 | return retpc; | |
217 | } | |
218 | ||
0c3e702a MC |
219 | void helper_wfi(CPURISCVState *env) |
220 | { | |
3109cd98 | 221 | CPUState *cs = env_cpu(env); |
719f0f60 JM |
222 | bool rvs = riscv_has_ext(env, RVS); |
223 | bool prv_u = env->priv == PRV_U; | |
224 | bool prv_s = env->priv == PRV_S; | |
0c3e702a | 225 | |
719f0f60 JM |
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)))) { | |
e39a8320 | 231 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); |
7f2b5ff1 MC |
232 | } else { |
233 | cs->halted = 1; | |
234 | cs->exception_index = EXCP_HLT; | |
235 | cpu_loop_exit(cs); | |
236 | } | |
0c3e702a MC |
237 | } |
238 | ||
239 | void helper_tlb_flush(CPURISCVState *env) | |
240 | { | |
3109cd98 | 241 | CPUState *cs = env_cpu(env); |
b86f4167 JB |
242 | if (!(env->priv >= PRV_S) || |
243 | (env->priv == PRV_S && | |
b86f4167 | 244 | get_field(env->mstatus, MSTATUS_TVM))) { |
fb738839 | 245 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
e39a8320 AF |
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()); | |
7f2b5ff1 MC |
249 | } else { |
250 | tlb_flush(cs); | |
251 | } | |
0c3e702a MC |
252 | } |
253 | ||
2761db5f AF |
254 | void helper_hyp_tlb_flush(CPURISCVState *env) |
255 | { | |
256 | CPUState *cs = env_cpu(env); | |
257 | ||
e39a8320 AF |
258 | if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { |
259 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); | |
260 | } | |
261 | ||
2761db5f AF |
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 | ||
e39a8320 AF |
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 | ||
7687537a | 281 | target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) |
8c5362ac | 282 | { |
7687537a | 283 | int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; |
8c5362ac | 284 | |
7687537a AF |
285 | return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); |
286 | } | |
8c5362ac | 287 | |
7687537a AF |
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()); | |
8c5362ac AF |
293 | } |
294 | ||
0c3e702a | 295 | #endif /* !CONFIG_USER_ONLY */ |