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_csrrw(CPURISCVState *env, target_ulong src, 41 target_ulong csr) 42 { 43 target_ulong val = 0; 44 RISCVException ret = riscv_csrrw(env, csr, &val, src, -1); 45 46 if (ret != RISCV_EXCP_NONE) { 47 riscv_raise_exception(env, ret, GETPC()); 48 } 49 return val; 50 } 51 52 target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, 53 target_ulong csr, target_ulong rs1_pass) 54 { 55 target_ulong val = 0; 56 RISCVException ret = riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0); 57 58 if (ret != RISCV_EXCP_NONE) { 59 riscv_raise_exception(env, ret, GETPC()); 60 } 61 return val; 62 } 63 64 target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, 65 target_ulong csr, target_ulong rs1_pass) 66 { 67 target_ulong val = 0; 68 RISCVException ret = riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0); 69 70 if (ret != RISCV_EXCP_NONE) { 71 riscv_raise_exception(env, ret, GETPC()); 72 } 73 return val; 74 } 75 76 #ifndef CONFIG_USER_ONLY 77 78 target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) 79 { 80 uint64_t mstatus; 81 target_ulong prev_priv, prev_virt; 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 uint64_t mstatus = env->mstatus; 151 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 152 153 if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) { 154 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 155 } 156 157 target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); 158 mstatus = set_field(mstatus, MSTATUS_MIE, 159 get_field(mstatus, MSTATUS_MPIE)); 160 mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 161 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 162 mstatus = set_field(mstatus, MSTATUS_MPV, 0); 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 bool rvs = riscv_has_ext(env, RVS); 181 bool prv_u = env->priv == PRV_U; 182 bool prv_s = env->priv == PRV_S; 183 184 if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || 185 (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { 186 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 187 } else if (riscv_cpu_virt_enabled(env) && (prv_u || 188 (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { 189 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 190 } else { 191 cs->halted = 1; 192 cs->exception_index = EXCP_HLT; 193 cpu_loop_exit(cs); 194 } 195 } 196 197 void helper_tlb_flush(CPURISCVState *env) 198 { 199 CPUState *cs = env_cpu(env); 200 if (!(env->priv >= PRV_S) || 201 (env->priv == PRV_S && 202 get_field(env->mstatus, MSTATUS_TVM))) { 203 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 204 } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 205 get_field(env->hstatus, HSTATUS_VTVM)) { 206 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 207 } else { 208 tlb_flush(cs); 209 } 210 } 211 212 void helper_hyp_tlb_flush(CPURISCVState *env) 213 { 214 CPUState *cs = env_cpu(env); 215 216 if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { 217 riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 218 } 219 220 if (env->priv == PRV_M || 221 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 222 tlb_flush(cs); 223 return; 224 } 225 226 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 227 } 228 229 void helper_hyp_gvma_tlb_flush(CPURISCVState *env) 230 { 231 if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && 232 get_field(env->mstatus, MSTATUS_TVM)) { 233 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 234 } 235 236 helper_hyp_tlb_flush(env); 237 } 238 239 target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) 240 { 241 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 242 243 return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); 244 } 245 246 target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) 247 { 248 int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 249 250 return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); 251 } 252 253 #endif /* !CONFIG_USER_ONLY */ 254