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