1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
27void 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
35void helper_raise_exception(CPURISCVState *env, uint32_t exception)
36{
37 riscv_raise_exception(env, exception, 0);
38}
39
40target_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
51void helper_csrw(CPURISCVState *env, int csr, target_ulong src)
52{
53 RISCVException ret = riscv_csrrw(env, csr, NULL, src, -1);
54
55 if (ret != RISCV_EXCP_NONE) {
56 riscv_raise_exception(env, ret, GETPC());
57 }
58}
59
60target_ulong helper_csrrw(CPURISCVState *env, int csr,
61 target_ulong src, target_ulong write_mask)
62{
63 target_ulong val = 0;
64 RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask);
65
66 if (ret != RISCV_EXCP_NONE) {
67 riscv_raise_exception(env, ret, GETPC());
68 }
69 return val;
70}
71
72#ifndef CONFIG_USER_ONLY
73
74target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
75{
76 uint64_t mstatus;
77 target_ulong prev_priv, prev_virt;
78
79 if (!(env->priv >= PRV_S)) {
80 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
81 }
82
83 target_ulong retpc = env->sepc;
84 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
85 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
86 }
87
88 if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) {
89 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
90 }
91
92 if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
93 get_field(env->hstatus, HSTATUS_VTSR)) {
94 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
95 }
96
97 mstatus = env->mstatus;
98
99 if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) {
100
101 target_ulong hstatus = env->hstatus;
102
103 prev_priv = get_field(mstatus, MSTATUS_SPP);
104 prev_virt = get_field(hstatus, HSTATUS_SPV);
105
106 hstatus = set_field(hstatus, HSTATUS_SPV, 0);
107 mstatus = set_field(mstatus, MSTATUS_SPP, 0);
108 mstatus = set_field(mstatus, SSTATUS_SIE,
109 get_field(mstatus, SSTATUS_SPIE));
110 mstatus = set_field(mstatus, SSTATUS_SPIE, 1);
111
112 env->mstatus = mstatus;
113 env->hstatus = hstatus;
114
115 if (prev_virt) {
116 riscv_cpu_swap_hypervisor_regs(env);
117 }
118
119 riscv_cpu_set_virt_enabled(env, prev_virt);
120 } else {
121 prev_priv = get_field(mstatus, MSTATUS_SPP);
122
123 mstatus = set_field(mstatus, MSTATUS_SIE,
124 get_field(mstatus, MSTATUS_SPIE));
125 mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
126 mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
127 env->mstatus = mstatus;
128 }
129
130 riscv_cpu_set_mode(env, prev_priv);
131
132 return retpc;
133}
134
135target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
136{
137 if (!(env->priv >= PRV_M)) {
138 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
139 }
140
141 target_ulong retpc = env->mepc;
142 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
143 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
144 }
145
146 uint64_t mstatus = env->mstatus;
147 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
148
149 if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
150 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
151 }
152
153 target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV);
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 mstatus = set_field(mstatus, MSTATUS_MPV, 0);
159 env->mstatus = mstatus;
160 riscv_cpu_set_mode(env, prev_priv);
161
162 if (riscv_has_ext(env, RVH)) {
163 if (prev_virt) {
164 riscv_cpu_swap_hypervisor_regs(env);
165 }
166
167 riscv_cpu_set_virt_enabled(env, prev_virt);
168 }
169
170 return retpc;
171}
172
173void helper_wfi(CPURISCVState *env)
174{
175 CPUState *cs = env_cpu(env);
176 bool rvs = riscv_has_ext(env, RVS);
177 bool prv_u = env->priv == PRV_U;
178 bool prv_s = env->priv == PRV_S;
179
180 if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) ||
181 (rvs && prv_u && !riscv_cpu_virt_enabled(env))) {
182 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
183 } else if (riscv_cpu_virt_enabled(env) && (prv_u ||
184 (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) {
185 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
186 } else {
187 cs->halted = 1;
188 cs->exception_index = EXCP_HLT;
189 cpu_loop_exit(cs);
190 }
191}
192
193void helper_tlb_flush(CPURISCVState *env)
194{
195 CPUState *cs = env_cpu(env);
196 if (!(env->priv >= PRV_S) ||
197 (env->priv == PRV_S &&
198 get_field(env->mstatus, MSTATUS_TVM))) {
199 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
200 } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
201 get_field(env->hstatus, HSTATUS_VTVM)) {
202 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
203 } else {
204 tlb_flush(cs);
205 }
206}
207
208void helper_hyp_tlb_flush(CPURISCVState *env)
209{
210 CPUState *cs = env_cpu(env);
211
212 if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) {
213 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
214 }
215
216 if (env->priv == PRV_M ||
217 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) {
218 tlb_flush(cs);
219 return;
220 }
221
222 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
223}
224
225void helper_hyp_gvma_tlb_flush(CPURISCVState *env)
226{
227 if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) &&
228 get_field(env->mstatus, MSTATUS_TVM)) {
229 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
230 }
231
232 helper_hyp_tlb_flush(env);
233}
234
235target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address)
236{
237 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
238
239 return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC());
240}
241
242target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address)
243{
244 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
245
246 return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC());
247}
248
249#endif
250