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