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 "cpu.h" 22 #include "qemu/main-loop.h" 23 #include "exec/exec-all.h" 24 #include "exec/helper-proto.h" 25 26 /* Exceptions processing helpers */ 27 G_NORETURN void 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 35 void helper_raise_exception(CPURISCVState *env, uint32_t exception) 36 { 37 riscv_raise_exception(env, exception, 0); 38 } 39 40 target_ulong helper_csrr(CPURISCVState *env, int csr) 41 { 42 /* 43 * The seed CSR must be accessed with a read-write instruction. A 44 * read-only instruction such as CSRRS/CSRRC with rs1=x0 or CSRRSI/ 45 * CSRRCI with uimm=0 will raise an illegal instruction exception. 46 */ 47 if (csr == CSR_SEED) { 48 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 49 } 50 51 target_ulong val = 0; 52 RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); 53 54 if (ret != RISCV_EXCP_NONE) { 55 riscv_raise_exception(env, ret, GETPC()); 56 } 57 return val; 58 } 59 60 void helper_csrw(CPURISCVState *env, int csr, target_ulong src) 61 { 62 target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1; 63 RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask); 64 65 if (ret != RISCV_EXCP_NONE) { 66 riscv_raise_exception(env, ret, GETPC()); 67 } 68 } 69 70 target_ulong helper_csrrw(CPURISCVState *env, int csr, 71 target_ulong src, target_ulong write_mask) 72 { 73 target_ulong val = 0; 74 RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); 75 76 if (ret != RISCV_EXCP_NONE) { 77 riscv_raise_exception(env, ret, GETPC()); 78 } 79 return val; 80 } 81 82 target_ulong helper_csrr_i128(CPURISCVState *env, int csr) 83 { 84 Int128 rv = int128_zero(); 85 RISCVException ret = riscv_csrrw_i128(env, csr, &rv, 86 int128_zero(), 87 int128_zero()); 88 89 if (ret != RISCV_EXCP_NONE) { 90 riscv_raise_exception(env, ret, GETPC()); 91 } 92 93 env->retxh = int128_gethi(rv); 94 return int128_getlo(rv); 95 } 96 97 void helper_csrw_i128(CPURISCVState *env, int csr, 98 target_ulong srcl, target_ulong srch) 99 { 100 RISCVException ret = riscv_csrrw_i128(env, csr, NULL, 101 int128_make128(srcl, srch), 102 UINT128_MAX); 103 104 if (ret != RISCV_EXCP_NONE) { 105 riscv_raise_exception(env, ret, GETPC()); 106 } 107 } 108 109 target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, 110 target_ulong srcl, target_ulong srch, 111 target_ulong maskl, target_ulong maskh) 112 { 113 Int128 rv = int128_zero(); 114 RISCVException ret = riscv_csrrw_i128(env, csr, &rv, 115 int128_make128(srcl, srch), 116 int128_make128(maskl, maskh)); 117 118 if (ret != RISCV_EXCP_NONE) { 119 riscv_raise_exception(env, ret, GETPC()); 120 } 121 122 env->retxh = int128_gethi(rv); 123 return int128_getlo(rv); 124 } 125 126 #ifndef CONFIG_USER_ONLY 127 128 target_ulong helper_sret(CPURISCVState *env) 129 { 130 uint64_t mstatus; 131 target_ulong prev_priv, prev_virt; 132 133 if (!(env->priv >= PRV_S)) { 134 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 135 } 136 137 target_ulong retpc = env->sepc; 138 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 139 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 140 } 141 142 if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { 143 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 144 } 145 146 if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 147 get_field(env->hstatus, HSTATUS_VTSR)) { 148 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 149 } 150 151 mstatus = env->mstatus; 152 prev_priv = get_field(mstatus, MSTATUS_SPP); 153 mstatus = set_field(mstatus, MSTATUS_SIE, 154 get_field(mstatus, MSTATUS_SPIE)); 155 mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 156 mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 157 if (env->priv_ver >= PRIV_VERSION_1_12_0) { 158 mstatus = set_field(mstatus, MSTATUS_MPRV, 0); 159 } 160 env->mstatus = mstatus; 161 162 if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 163 /* We support Hypervisor extensions and virtulisation is disabled */ 164 target_ulong hstatus = env->hstatus; 165 166 prev_virt = get_field(hstatus, HSTATUS_SPV); 167 168 hstatus = set_field(hstatus, HSTATUS_SPV, 0); 169 170 env->hstatus = hstatus; 171 172 if (prev_virt) { 173 riscv_cpu_swap_hypervisor_regs(env); 174 } 175 176 riscv_cpu_set_virt_enabled(env, prev_virt); 177 } 178 179 riscv_cpu_set_mode(env, prev_priv); 180 181 return retpc; 182 } 183 184 target_ulong helper_mret(CPURISCVState *env) 185 { 186 if (!(env->priv >= PRV_M)) { 187 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 188 } 189 190 target_ulong retpc = env->mepc; 191 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 192 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 193 } 194 195 uint64_t mstatus = env->mstatus; 196 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 197 198 if (riscv_cpu_cfg(env)->pmp && 199 !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { 200 riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); 201 } 202 203 target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); 204 mstatus = set_field(mstatus, MSTATUS_MIE, 205 get_field(mstatus, MSTATUS_MPIE)); 206 mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 207 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 208 mstatus = set_field(mstatus, MSTATUS_MPV, 0); 209 if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { 210 mstatus = set_field(mstatus, MSTATUS_MPRV, 0); 211 } 212 env->mstatus = mstatus; 213 riscv_cpu_set_mode(env, prev_priv); 214 215 if (riscv_has_ext(env, RVH)) { 216 if (prev_virt) { 217 riscv_cpu_swap_hypervisor_regs(env); 218 } 219 220 riscv_cpu_set_virt_enabled(env, prev_virt); 221 } 222 223 return retpc; 224 } 225 226 void helper_wfi(CPURISCVState *env) 227 { 228 CPUState *cs = env_cpu(env); 229 bool rvs = riscv_has_ext(env, RVS); 230 bool prv_u = env->priv == PRV_U; 231 bool prv_s = env->priv == PRV_S; 232 233 if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || 234 (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { 235 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 236 } else if (riscv_cpu_virt_enabled(env) && (prv_u || 237 (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { 238 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 239 } else { 240 cs->halted = 1; 241 cs->exception_index = EXCP_HLT; 242 cpu_loop_exit(cs); 243 } 244 } 245 246 void helper_tlb_flush(CPURISCVState *env) 247 { 248 CPUState *cs = env_cpu(env); 249 if (!(env->priv >= PRV_S) || 250 (env->priv == PRV_S && 251 get_field(env->mstatus, MSTATUS_TVM))) { 252 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 253 } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 254 get_field(env->hstatus, HSTATUS_VTVM)) { 255 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 256 } else { 257 tlb_flush(cs); 258 } 259 } 260 261 void helper_tlb_flush_all(CPURISCVState *env) 262 { 263 CPUState *cs = env_cpu(env); 264 tlb_flush_all_cpus_synced(cs); 265 } 266 267 void helper_hyp_tlb_flush(CPURISCVState *env) 268 { 269 CPUState *cs = env_cpu(env); 270 271 if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { 272 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 273 } 274 275 if (env->priv == PRV_M || 276 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 277 tlb_flush(cs); 278 return; 279 } 280 281 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 282 } 283 284 void helper_hyp_gvma_tlb_flush(CPURISCVState *env) 285 { 286 if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && 287 get_field(env->mstatus, MSTATUS_TVM)) { 288 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 289 } 290 291 helper_hyp_tlb_flush(env); 292 } 293 294 target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) 295 { 296 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 297 298 return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); 299 } 300 301 target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) 302 { 303 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 304 305 return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); 306 } 307 308 #endif /* !CONFIG_USER_ONLY */ 309