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