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