]>
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 */ |
fb738839 | 27 | void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, |
0c3e702a MC |
28 | uint32_t exception, uintptr_t pc) |
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 | { |
a974879b | 53 | RISCVException ret = riscv_csrrw(env, csr, NULL, src, -1); |
57cb2083 | 54 | |
533c91e8 AF |
55 | if (ret != RISCV_EXCP_NONE) { |
56 | riscv_raise_exception(env, ret, GETPC()); | |
0c3e702a | 57 | } |
0c3e702a MC |
58 | } |
59 | ||
a974879b RH |
60 | target_ulong helper_csrrw(CPURISCVState *env, int csr, |
61 | target_ulong src, target_ulong write_mask) | |
0c3e702a | 62 | { |
c7b95171 | 63 | target_ulong val = 0; |
a974879b | 64 | RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); |
57cb2083 | 65 | |
533c91e8 AF |
66 | if (ret != RISCV_EXCP_NONE) { |
67 | riscv_raise_exception(env, ret, GETPC()); | |
0c3e702a | 68 | } |
c7b95171 | 69 | return val; |
0c3e702a MC |
70 | } |
71 | ||
961738ff FP |
72 | target_ulong helper_csrr_i128(CPURISCVState *env, int csr) |
73 | { | |
74 | Int128 rv = int128_zero(); | |
75 | RISCVException ret = riscv_csrrw_i128(env, csr, &rv, | |
76 | int128_zero(), | |
77 | int128_zero()); | |
78 | ||
79 | if (ret != RISCV_EXCP_NONE) { | |
80 | riscv_raise_exception(env, ret, GETPC()); | |
81 | } | |
82 | ||
83 | env->retxh = int128_gethi(rv); | |
84 | return int128_getlo(rv); | |
85 | } | |
86 | ||
87 | void helper_csrw_i128(CPURISCVState *env, int csr, | |
88 | target_ulong srcl, target_ulong srch) | |
89 | { | |
90 | RISCVException ret = riscv_csrrw_i128(env, csr, NULL, | |
91 | int128_make128(srcl, srch), | |
92 | UINT128_MAX); | |
93 | ||
94 | if (ret != RISCV_EXCP_NONE) { | |
95 | riscv_raise_exception(env, ret, GETPC()); | |
96 | } | |
97 | } | |
98 | ||
99 | target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, | |
100 | target_ulong srcl, target_ulong srch, | |
101 | target_ulong maskl, target_ulong maskh) | |
102 | { | |
103 | Int128 rv = int128_zero(); | |
104 | RISCVException ret = riscv_csrrw_i128(env, csr, &rv, | |
105 | int128_make128(srcl, srch), | |
106 | int128_make128(maskl, maskh)); | |
107 | ||
108 | if (ret != RISCV_EXCP_NONE) { | |
109 | riscv_raise_exception(env, ret, GETPC()); | |
110 | } | |
111 | ||
112 | env->retxh = int128_gethi(rv); | |
113 | return int128_getlo(rv); | |
114 | } | |
115 | ||
0c3e702a MC |
116 | #ifndef CONFIG_USER_ONLY |
117 | ||
b655dc7c | 118 | target_ulong helper_sret(CPURISCVState *env) |
0c3e702a | 119 | { |
284d697c YJ |
120 | uint64_t mstatus; |
121 | target_ulong prev_priv, prev_virt; | |
e3fba4ba | 122 | |
0c3e702a | 123 | if (!(env->priv >= PRV_S)) { |
fb738839 | 124 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
0c3e702a MC |
125 | } |
126 | ||
127 | target_ulong retpc = env->sepc; | |
128 | if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { | |
fb738839 | 129 | riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); |
0c3e702a MC |
130 | } |
131 | ||
1a9540d1 | 132 | if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { |
fb738839 | 133 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
7f2b5ff1 MC |
134 | } |
135 | ||
e39a8320 AF |
136 | if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && |
137 | get_field(env->hstatus, HSTATUS_VTSR)) { | |
138 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); | |
139 | } | |
140 | ||
e3fba4ba AF |
141 | mstatus = env->mstatus; |
142 | ||
143 | if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { | |
144 | /* We support Hypervisor extensions and virtulisation is disabled */ | |
145 | target_ulong hstatus = env->hstatus; | |
146 | ||
147 | prev_priv = get_field(mstatus, MSTATUS_SPP); | |
148 | prev_virt = get_field(hstatus, HSTATUS_SPV); | |
149 | ||
f2d5850f AF |
150 | hstatus = set_field(hstatus, HSTATUS_SPV, 0); |
151 | mstatus = set_field(mstatus, MSTATUS_SPP, 0); | |
e3fba4ba AF |
152 | mstatus = set_field(mstatus, SSTATUS_SIE, |
153 | get_field(mstatus, SSTATUS_SPIE)); | |
154 | mstatus = set_field(mstatus, SSTATUS_SPIE, 1); | |
155 | ||
156 | env->mstatus = mstatus; | |
157 | env->hstatus = hstatus; | |
158 | ||
159 | if (prev_virt) { | |
160 | riscv_cpu_swap_hypervisor_regs(env); | |
161 | } | |
162 | ||
163 | riscv_cpu_set_virt_enabled(env, prev_virt); | |
164 | } else { | |
165 | prev_priv = get_field(mstatus, MSTATUS_SPP); | |
166 | ||
1a9540d1 AF |
167 | mstatus = set_field(mstatus, MSTATUS_SIE, |
168 | get_field(mstatus, MSTATUS_SPIE)); | |
e3fba4ba AF |
169 | mstatus = set_field(mstatus, MSTATUS_SPIE, 1); |
170 | mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); | |
171 | env->mstatus = mstatus; | |
172 | } | |
173 | ||
fb738839 | 174 | riscv_cpu_set_mode(env, prev_priv); |
0c3e702a MC |
175 | |
176 | return retpc; | |
177 | } | |
178 | ||
b655dc7c | 179 | target_ulong helper_mret(CPURISCVState *env) |
0c3e702a MC |
180 | { |
181 | if (!(env->priv >= PRV_M)) { | |
fb738839 | 182 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
0c3e702a MC |
183 | } |
184 | ||
185 | target_ulong retpc = env->mepc; | |
186 | if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { | |
fb738839 | 187 | riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); |
0c3e702a MC |
188 | } |
189 | ||
284d697c | 190 | uint64_t mstatus = env->mstatus; |
0c3e702a | 191 | target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); |
d102f19a | 192 | |
0fbb5d2d NS |
193 | if (riscv_feature(env, RISCV_FEATURE_PMP) && |
194 | !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { | |
d102f19a AP |
195 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
196 | } | |
197 | ||
284d697c | 198 | target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); |
1a9540d1 AF |
199 | mstatus = set_field(mstatus, MSTATUS_MIE, |
200 | get_field(mstatus, MSTATUS_MPIE)); | |
a37f21c2 | 201 | mstatus = set_field(mstatus, MSTATUS_MPIE, 1); |
0c3e702a | 202 | mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); |
e3fba4ba | 203 | mstatus = set_field(mstatus, MSTATUS_MPV, 0); |
c7b95171 | 204 | env->mstatus = mstatus; |
e3fba4ba AF |
205 | riscv_cpu_set_mode(env, prev_priv); |
206 | ||
207 | if (riscv_has_ext(env, RVH)) { | |
208 | if (prev_virt) { | |
209 | riscv_cpu_swap_hypervisor_regs(env); | |
210 | } | |
211 | ||
212 | riscv_cpu_set_virt_enabled(env, prev_virt); | |
213 | } | |
0c3e702a MC |
214 | |
215 | return retpc; | |
216 | } | |
217 | ||
0c3e702a MC |
218 | void helper_wfi(CPURISCVState *env) |
219 | { | |
3109cd98 | 220 | CPUState *cs = env_cpu(env); |
719f0f60 JM |
221 | bool rvs = riscv_has_ext(env, RVS); |
222 | bool prv_u = env->priv == PRV_U; | |
223 | bool prv_s = env->priv == PRV_S; | |
0c3e702a | 224 | |
719f0f60 JM |
225 | if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || |
226 | (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { | |
227 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); | |
228 | } else if (riscv_cpu_virt_enabled(env) && (prv_u || | |
229 | (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { | |
e39a8320 | 230 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); |
7f2b5ff1 MC |
231 | } else { |
232 | cs->halted = 1; | |
233 | cs->exception_index = EXCP_HLT; | |
234 | cpu_loop_exit(cs); | |
235 | } | |
0c3e702a MC |
236 | } |
237 | ||
238 | void helper_tlb_flush(CPURISCVState *env) | |
239 | { | |
3109cd98 | 240 | CPUState *cs = env_cpu(env); |
b86f4167 JB |
241 | if (!(env->priv >= PRV_S) || |
242 | (env->priv == PRV_S && | |
b86f4167 | 243 | get_field(env->mstatus, MSTATUS_TVM))) { |
fb738839 | 244 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); |
e39a8320 AF |
245 | } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && |
246 | get_field(env->hstatus, HSTATUS_VTVM)) { | |
247 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); | |
7f2b5ff1 MC |
248 | } else { |
249 | tlb_flush(cs); | |
250 | } | |
0c3e702a MC |
251 | } |
252 | ||
2761db5f AF |
253 | void helper_hyp_tlb_flush(CPURISCVState *env) |
254 | { | |
255 | CPUState *cs = env_cpu(env); | |
256 | ||
e39a8320 AF |
257 | if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { |
258 | riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); | |
259 | } | |
260 | ||
2761db5f AF |
261 | if (env->priv == PRV_M || |
262 | (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { | |
263 | tlb_flush(cs); | |
264 | return; | |
265 | } | |
266 | ||
267 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); | |
268 | } | |
269 | ||
e39a8320 AF |
270 | void helper_hyp_gvma_tlb_flush(CPURISCVState *env) |
271 | { | |
272 | if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && | |
273 | get_field(env->mstatus, MSTATUS_TVM)) { | |
274 | riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); | |
275 | } | |
276 | ||
277 | helper_hyp_tlb_flush(env); | |
278 | } | |
279 | ||
7687537a | 280 | target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) |
8c5362ac | 281 | { |
7687537a | 282 | int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; |
8c5362ac | 283 | |
7687537a AF |
284 | return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); |
285 | } | |
8c5362ac | 286 | |
7687537a AF |
287 | target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) |
288 | { | |
289 | int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; | |
290 | ||
291 | return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); | |
8c5362ac AF |
292 | } |
293 | ||
0c3e702a | 294 | #endif /* !CONFIG_USER_ONLY */ |