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 mstatus = env->mstatus; 98 99 if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 100 /* We support Hypervisor extensions and virtulisation is disabled */ 101 target_ulong hstatus = env->hstatus; 102 103 prev_priv = get_field(mstatus, MSTATUS_SPP); 104 prev_virt = get_field(hstatus, HSTATUS_SPV); 105 106 hstatus = set_field(hstatus, HSTATUS_SPV, 0); 107 mstatus = set_field(mstatus, MSTATUS_SPP, 0); 108 mstatus = set_field(mstatus, SSTATUS_SIE, 109 get_field(mstatus, SSTATUS_SPIE)); 110 mstatus = set_field(mstatus, SSTATUS_SPIE, 1); 111 112 env->mstatus = mstatus; 113 env->hstatus = hstatus; 114 115 if (prev_virt) { 116 riscv_cpu_swap_hypervisor_regs(env); 117 } 118 119 riscv_cpu_set_virt_enabled(env, prev_virt); 120 } else { 121 prev_priv = get_field(mstatus, MSTATUS_SPP); 122 123 mstatus = set_field(mstatus, MSTATUS_SIE, 124 get_field(mstatus, MSTATUS_SPIE)); 125 mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 126 mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 127 env->mstatus = mstatus; 128 } 129 130 riscv_cpu_set_mode(env, prev_priv); 131 132 return retpc; 133 } 134 135 target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) 136 { 137 if (!(env->priv >= PRV_M)) { 138 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 139 } 140 141 target_ulong retpc = env->mepc; 142 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 143 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 144 } 145 146 target_ulong mstatus = env->mstatus; 147 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 148 target_ulong prev_virt = MSTATUS_MPV_ISSET(env); 149 mstatus = set_field(mstatus, MSTATUS_MIE, 150 get_field(mstatus, MSTATUS_MPIE)); 151 mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 152 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 153 #ifdef TARGET_RISCV32 154 env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0); 155 #else 156 mstatus = set_field(mstatus, MSTATUS_MPV, 0); 157 #endif 158 env->mstatus = mstatus; 159 riscv_cpu_set_mode(env, prev_priv); 160 161 if (riscv_has_ext(env, RVH)) { 162 if (prev_virt) { 163 riscv_cpu_swap_hypervisor_regs(env); 164 } 165 166 riscv_cpu_set_virt_enabled(env, prev_virt); 167 } 168 169 return retpc; 170 } 171 172 void helper_wfi(CPURISCVState *env) 173 { 174 CPUState *cs = env_cpu(env); 175 176 if ((env->priv == PRV_S && 177 get_field(env->mstatus, MSTATUS_TW)) || 178 riscv_cpu_virt_enabled(env)) { 179 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 180 } else { 181 cs->halted = 1; 182 cs->exception_index = EXCP_HLT; 183 cpu_loop_exit(cs); 184 } 185 } 186 187 void helper_tlb_flush(CPURISCVState *env) 188 { 189 CPUState *cs = env_cpu(env); 190 if (!(env->priv >= PRV_S) || 191 (env->priv == PRV_S && 192 get_field(env->mstatus, MSTATUS_TVM))) { 193 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 194 } else { 195 tlb_flush(cs); 196 } 197 } 198 199 void helper_hyp_tlb_flush(CPURISCVState *env) 200 { 201 CPUState *cs = env_cpu(env); 202 203 if (env->priv == PRV_M || 204 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 205 tlb_flush(cs); 206 return; 207 } 208 209 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 210 } 211 212 target_ulong helper_hyp_load(CPURISCVState *env, target_ulong address, 213 target_ulong attrs, target_ulong memop) 214 { 215 if (env->priv == PRV_M || 216 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 217 (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 218 get_field(env->hstatus, HSTATUS_HU))) { 219 target_ulong pte; 220 221 riscv_cpu_set_two_stage_lookup(env, true); 222 223 switch (memop) { 224 case MO_SB: 225 pte = cpu_ldsb_data_ra(env, address, GETPC()); 226 break; 227 case MO_UB: 228 pte = cpu_ldub_data_ra(env, address, GETPC()); 229 break; 230 case MO_TESW: 231 pte = cpu_ldsw_data_ra(env, address, GETPC()); 232 break; 233 case MO_TEUW: 234 pte = cpu_lduw_data_ra(env, address, GETPC()); 235 break; 236 case MO_TESL: 237 pte = cpu_ldl_data_ra(env, address, GETPC()); 238 break; 239 case MO_TEUL: 240 pte = cpu_ldl_data_ra(env, address, GETPC()); 241 break; 242 case MO_TEQ: 243 pte = cpu_ldq_data_ra(env, address, GETPC()); 244 break; 245 default: 246 g_assert_not_reached(); 247 } 248 249 riscv_cpu_set_two_stage_lookup(env, false); 250 251 return pte; 252 } 253 254 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 255 return 0; 256 } 257 258 void helper_hyp_store(CPURISCVState *env, target_ulong address, 259 target_ulong val, target_ulong attrs, target_ulong memop) 260 { 261 if (env->priv == PRV_M || 262 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 263 (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 264 get_field(env->hstatus, HSTATUS_HU))) { 265 riscv_cpu_set_two_stage_lookup(env, true); 266 267 switch (memop) { 268 case MO_SB: 269 case MO_UB: 270 cpu_stb_data_ra(env, address, val, GETPC()); 271 break; 272 case MO_TESW: 273 case MO_TEUW: 274 cpu_stw_data_ra(env, address, val, GETPC()); 275 break; 276 case MO_TESL: 277 case MO_TEUL: 278 cpu_stl_data_ra(env, address, val, GETPC()); 279 break; 280 case MO_TEQ: 281 cpu_stq_data_ra(env, address, val, GETPC()); 282 break; 283 default: 284 g_assert_not_reached(); 285 } 286 287 riscv_cpu_set_two_stage_lookup(env, false); 288 289 return; 290 } 291 292 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 293 } 294 295 target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address, 296 target_ulong attrs, target_ulong memop) 297 { 298 if (env->priv == PRV_M || 299 (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 300 (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 301 get_field(env->hstatus, HSTATUS_HU))) { 302 target_ulong pte; 303 304 riscv_cpu_set_two_stage_lookup(env, true); 305 306 switch (memop) { 307 case MO_TEUL: 308 pte = cpu_ldub_data_ra(env, address, GETPC()); 309 break; 310 case MO_TEUW: 311 pte = cpu_lduw_data_ra(env, address, GETPC()); 312 break; 313 default: 314 g_assert_not_reached(); 315 } 316 317 riscv_cpu_set_two_stage_lookup(env, false); 318 319 return pte; 320 } 321 322 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 323 return 0; 324 } 325 326 #endif /* !CONFIG_USER_ONLY */ 327