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