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 (env->priv_ver >= PRIV_VERSION_1_10_0 && 88 get_field(env->mstatus, MSTATUS_TSR)) { 89 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 90 } 91 92 mstatus = env->mstatus; 93 94 if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 95 /* We support Hypervisor extensions and virtulisation is disabled */ 96 target_ulong hstatus = env->hstatus; 97 98 prev_priv = get_field(mstatus, MSTATUS_SPP); 99 prev_virt = get_field(hstatus, HSTATUS_SPV); 100 101 hstatus = set_field(hstatus, HSTATUS_SPV, 102 get_field(hstatus, HSTATUS_SP2V)); 103 mstatus = set_field(mstatus, MSTATUS_SPP, 104 get_field(hstatus, HSTATUS_SP2P)); 105 hstatus = set_field(hstatus, HSTATUS_SP2V, 0); 106 hstatus = set_field(hstatus, HSTATUS_SP2P, 0); 107 mstatus = set_field(mstatus, SSTATUS_SIE, 108 get_field(mstatus, SSTATUS_SPIE)); 109 mstatus = set_field(mstatus, SSTATUS_SPIE, 1); 110 111 env->mstatus = mstatus; 112 env->hstatus = hstatus; 113 114 if (prev_virt) { 115 riscv_cpu_swap_hypervisor_regs(env); 116 } 117 118 riscv_cpu_set_virt_enabled(env, prev_virt); 119 } else { 120 prev_priv = get_field(mstatus, MSTATUS_SPP); 121 122 mstatus = set_field(mstatus, 123 env->priv_ver >= PRIV_VERSION_1_10_0 ? 124 MSTATUS_SIE : MSTATUS_UIE << prev_priv, 125 get_field(mstatus, MSTATUS_SPIE)); 126 mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 127 mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 128 env->mstatus = mstatus; 129 } 130 131 riscv_cpu_set_mode(env, prev_priv); 132 133 return retpc; 134 } 135 136 target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) 137 { 138 if (!(env->priv >= PRV_M)) { 139 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 140 } 141 142 target_ulong retpc = env->mepc; 143 if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 144 riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 145 } 146 147 target_ulong mstatus = env->mstatus; 148 target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 149 target_ulong prev_virt = get_field(mstatus, MSTATUS_MPV); 150 mstatus = set_field(mstatus, 151 env->priv_ver >= PRIV_VERSION_1_10_0 ? 152 MSTATUS_MIE : MSTATUS_UIE << prev_priv, 153 get_field(mstatus, MSTATUS_MPIE)); 154 mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 155 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 156 #ifdef TARGET_RISCV32 157 env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0); 158 #else 159 mstatus = set_field(mstatus, MSTATUS_MPV, 0); 160 #endif 161 env->mstatus = mstatus; 162 riscv_cpu_set_mode(env, prev_priv); 163 164 if (riscv_has_ext(env, RVH)) { 165 if (prev_virt) { 166 riscv_cpu_swap_hypervisor_regs(env); 167 } 168 169 riscv_cpu_set_virt_enabled(env, prev_virt); 170 } 171 172 return retpc; 173 } 174 175 void helper_wfi(CPURISCVState *env) 176 { 177 CPUState *cs = env_cpu(env); 178 179 if ((env->priv == PRV_S && 180 env->priv_ver >= PRIV_VERSION_1_10_0 && 181 get_field(env->mstatus, MSTATUS_TW)) || 182 riscv_cpu_virt_enabled(env)) { 183 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 184 } else { 185 cs->halted = 1; 186 cs->exception_index = EXCP_HLT; 187 cpu_loop_exit(cs); 188 } 189 } 190 191 void helper_tlb_flush(CPURISCVState *env) 192 { 193 CPUState *cs = env_cpu(env); 194 if (!(env->priv >= PRV_S) || 195 (env->priv == PRV_S && 196 env->priv_ver >= PRIV_VERSION_1_10_0 && 197 get_field(env->mstatus, MSTATUS_TVM))) { 198 riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 199 } else { 200 tlb_flush(cs); 201 } 202 } 203 204 #endif /* !CONFIG_USER_ONLY */ 205