10c3e702aSMichael Clark /* 20c3e702aSMichael Clark * RISC-V Emulation Helpers for QEMU. 30c3e702aSMichael Clark * 40c3e702aSMichael Clark * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu 50c3e702aSMichael Clark * Copyright (c) 2017-2018 SiFive, Inc. 60c3e702aSMichael Clark * 70c3e702aSMichael Clark * This program is free software; you can redistribute it and/or modify it 80c3e702aSMichael Clark * under the terms and conditions of the GNU General Public License, 90c3e702aSMichael Clark * version 2 or later, as published by the Free Software Foundation. 100c3e702aSMichael Clark * 110c3e702aSMichael Clark * This program is distributed in the hope it will be useful, but WITHOUT 120c3e702aSMichael Clark * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 130c3e702aSMichael Clark * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 140c3e702aSMichael Clark * more details. 150c3e702aSMichael Clark * 160c3e702aSMichael Clark * You should have received a copy of the GNU General Public License along with 170c3e702aSMichael Clark * this program. If not, see <http://www.gnu.org/licenses/>. 180c3e702aSMichael Clark */ 190c3e702aSMichael Clark 200c3e702aSMichael Clark #include "qemu/osdep.h" 210c3e702aSMichael Clark #include "cpu.h" 220c3e702aSMichael Clark #include "qemu/main-loop.h" 230c3e702aSMichael Clark #include "exec/exec-all.h" 240c3e702aSMichael Clark #include "exec/helper-proto.h" 250c3e702aSMichael Clark 260c3e702aSMichael Clark /* Exceptions processing helpers */ 27fb738839SMichael Clark void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, 280c3e702aSMichael Clark uint32_t exception, uintptr_t pc) 290c3e702aSMichael Clark { 303109cd98SRichard Henderson CPUState *cs = env_cpu(env); 310c3e702aSMichael Clark cs->exception_index = exception; 320c3e702aSMichael Clark cpu_loop_exit_restore(cs, pc); 330c3e702aSMichael Clark } 340c3e702aSMichael Clark 350c3e702aSMichael Clark void helper_raise_exception(CPURISCVState *env, uint32_t exception) 360c3e702aSMichael Clark { 37fb738839SMichael Clark riscv_raise_exception(env, exception, 0); 380c3e702aSMichael Clark } 390c3e702aSMichael Clark 40*a974879bSRichard Henderson target_ulong helper_csrr(CPURISCVState *env, int csr) 410c3e702aSMichael Clark { 42c7b95171SMichael Clark target_ulong val = 0; 43*a974879bSRichard Henderson RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); 4457cb2083SAlistair Francis 45533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 46533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 47c7b95171SMichael Clark } 48c7b95171SMichael Clark return val; 490c3e702aSMichael Clark } 500c3e702aSMichael Clark 51*a974879bSRichard Henderson void helper_csrw(CPURISCVState *env, int csr, target_ulong src) 520c3e702aSMichael Clark { 53*a974879bSRichard Henderson RISCVException ret = riscv_csrrw(env, csr, NULL, src, -1); 5457cb2083SAlistair Francis 55533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 56533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 570c3e702aSMichael Clark } 580c3e702aSMichael Clark } 590c3e702aSMichael Clark 60*a974879bSRichard Henderson target_ulong helper_csrrw(CPURISCVState *env, int csr, 61*a974879bSRichard Henderson target_ulong src, target_ulong write_mask) 620c3e702aSMichael Clark { 63c7b95171SMichael Clark target_ulong val = 0; 64*a974879bSRichard Henderson RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); 6557cb2083SAlistair Francis 66533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 67533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 680c3e702aSMichael Clark } 69c7b95171SMichael Clark return val; 700c3e702aSMichael Clark } 710c3e702aSMichael Clark 720c3e702aSMichael Clark #ifndef CONFIG_USER_ONLY 730c3e702aSMichael Clark 740c3e702aSMichael Clark target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) 750c3e702aSMichael Clark { 76284d697cSYifei Jiang uint64_t mstatus; 77284d697cSYifei Jiang target_ulong prev_priv, prev_virt; 78e3fba4baSAlistair Francis 790c3e702aSMichael Clark if (!(env->priv >= PRV_S)) { 80fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 810c3e702aSMichael Clark } 820c3e702aSMichael Clark 830c3e702aSMichael Clark target_ulong retpc = env->sepc; 840c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 85fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 860c3e702aSMichael Clark } 870c3e702aSMichael Clark 881a9540d1SAlistair Francis if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { 89fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 907f2b5ff1SMichael Clark } 917f2b5ff1SMichael Clark 92e39a8320SAlistair Francis if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 93e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTSR)) { 94e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 95e39a8320SAlistair Francis } 96e39a8320SAlistair Francis 97e3fba4baSAlistair Francis mstatus = env->mstatus; 98e3fba4baSAlistair Francis 99e3fba4baSAlistair Francis if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 100e3fba4baSAlistair Francis /* We support Hypervisor extensions and virtulisation is disabled */ 101e3fba4baSAlistair Francis target_ulong hstatus = env->hstatus; 102e3fba4baSAlistair Francis 103e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 104e3fba4baSAlistair Francis prev_virt = get_field(hstatus, HSTATUS_SPV); 105e3fba4baSAlistair Francis 106f2d5850fSAlistair Francis hstatus = set_field(hstatus, HSTATUS_SPV, 0); 107f2d5850fSAlistair Francis mstatus = set_field(mstatus, MSTATUS_SPP, 0); 108e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SIE, 109e3fba4baSAlistair Francis get_field(mstatus, SSTATUS_SPIE)); 110e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SPIE, 1); 111e3fba4baSAlistair Francis 112e3fba4baSAlistair Francis env->mstatus = mstatus; 113e3fba4baSAlistair Francis env->hstatus = hstatus; 114e3fba4baSAlistair Francis 115e3fba4baSAlistair Francis if (prev_virt) { 116e3fba4baSAlistair Francis riscv_cpu_swap_hypervisor_regs(env); 117e3fba4baSAlistair Francis } 118e3fba4baSAlistair Francis 119e3fba4baSAlistair Francis riscv_cpu_set_virt_enabled(env, prev_virt); 120e3fba4baSAlistair Francis } else { 121e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 122e3fba4baSAlistair Francis 1231a9540d1SAlistair Francis mstatus = set_field(mstatus, MSTATUS_SIE, 1240c3e702aSMichael Clark get_field(mstatus, MSTATUS_SPIE)); 125a37f21c2SYiting Wang mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 1260c3e702aSMichael Clark mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 127c7b95171SMichael Clark env->mstatus = mstatus; 128e3fba4baSAlistair Francis } 129e3fba4baSAlistair Francis 130e3fba4baSAlistair Francis riscv_cpu_set_mode(env, prev_priv); 1310c3e702aSMichael Clark 1320c3e702aSMichael Clark return retpc; 1330c3e702aSMichael Clark } 1340c3e702aSMichael Clark 1350c3e702aSMichael Clark target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) 1360c3e702aSMichael Clark { 1370c3e702aSMichael Clark if (!(env->priv >= PRV_M)) { 138fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 1390c3e702aSMichael Clark } 1400c3e702aSMichael Clark 1410c3e702aSMichael Clark target_ulong retpc = env->mepc; 1420c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 143fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 1440c3e702aSMichael Clark } 1450c3e702aSMichael Clark 146284d697cSYifei Jiang uint64_t mstatus = env->mstatus; 1470c3e702aSMichael Clark target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 148d102f19aSAtish Patra 149d102f19aSAtish Patra if (!pmp_get_num_rules(env) && (prev_priv != PRV_M)) { 150d102f19aSAtish Patra riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 151d102f19aSAtish Patra } 152d102f19aSAtish Patra 153284d697cSYifei Jiang target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); 1541a9540d1SAlistair Francis mstatus = set_field(mstatus, MSTATUS_MIE, 1550c3e702aSMichael Clark get_field(mstatus, MSTATUS_MPIE)); 156a37f21c2SYiting Wang mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 1570c3e702aSMichael Clark mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 158e3fba4baSAlistair Francis mstatus = set_field(mstatus, MSTATUS_MPV, 0); 159c7b95171SMichael Clark env->mstatus = mstatus; 160e3fba4baSAlistair Francis riscv_cpu_set_mode(env, prev_priv); 161e3fba4baSAlistair Francis 162e3fba4baSAlistair Francis if (riscv_has_ext(env, RVH)) { 163e3fba4baSAlistair Francis if (prev_virt) { 164e3fba4baSAlistair Francis riscv_cpu_swap_hypervisor_regs(env); 165e3fba4baSAlistair Francis } 166e3fba4baSAlistair Francis 167e3fba4baSAlistair Francis riscv_cpu_set_virt_enabled(env, prev_virt); 168e3fba4baSAlistair Francis } 1690c3e702aSMichael Clark 1700c3e702aSMichael Clark return retpc; 1710c3e702aSMichael Clark } 1720c3e702aSMichael Clark 1730c3e702aSMichael Clark void helper_wfi(CPURISCVState *env) 1740c3e702aSMichael Clark { 1753109cd98SRichard Henderson CPUState *cs = env_cpu(env); 176719f0f60SJose Martins bool rvs = riscv_has_ext(env, RVS); 177719f0f60SJose Martins bool prv_u = env->priv == PRV_U; 178719f0f60SJose Martins bool prv_s = env->priv == PRV_S; 1790c3e702aSMichael Clark 180719f0f60SJose Martins if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || 181719f0f60SJose Martins (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { 182719f0f60SJose Martins riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 183719f0f60SJose Martins } else if (riscv_cpu_virt_enabled(env) && (prv_u || 184719f0f60SJose Martins (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { 185e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 1867f2b5ff1SMichael Clark } else { 1870c3e702aSMichael Clark cs->halted = 1; 1880c3e702aSMichael Clark cs->exception_index = EXCP_HLT; 1890c3e702aSMichael Clark cpu_loop_exit(cs); 1900c3e702aSMichael Clark } 1917f2b5ff1SMichael Clark } 1920c3e702aSMichael Clark 1930c3e702aSMichael Clark void helper_tlb_flush(CPURISCVState *env) 1940c3e702aSMichael Clark { 1953109cd98SRichard Henderson CPUState *cs = env_cpu(env); 196b86f4167SJonathan Behrens if (!(env->priv >= PRV_S) || 197b86f4167SJonathan Behrens (env->priv == PRV_S && 198b86f4167SJonathan Behrens get_field(env->mstatus, MSTATUS_TVM))) { 199fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 200e39a8320SAlistair Francis } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 201e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTVM)) { 202e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 2037f2b5ff1SMichael Clark } else { 2040c3e702aSMichael Clark tlb_flush(cs); 2050c3e702aSMichael Clark } 2067f2b5ff1SMichael Clark } 2070c3e702aSMichael Clark 2082761db5fSAlistair Francis void helper_hyp_tlb_flush(CPURISCVState *env) 2092761db5fSAlistair Francis { 2102761db5fSAlistair Francis CPUState *cs = env_cpu(env); 2112761db5fSAlistair Francis 212e39a8320SAlistair Francis if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { 213e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 214e39a8320SAlistair Francis } 215e39a8320SAlistair Francis 2162761db5fSAlistair Francis if (env->priv == PRV_M || 2172761db5fSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 2182761db5fSAlistair Francis tlb_flush(cs); 2192761db5fSAlistair Francis return; 2202761db5fSAlistair Francis } 2212761db5fSAlistair Francis 2222761db5fSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 2232761db5fSAlistair Francis } 2242761db5fSAlistair Francis 225e39a8320SAlistair Francis void helper_hyp_gvma_tlb_flush(CPURISCVState *env) 226e39a8320SAlistair Francis { 227e39a8320SAlistair Francis if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && 228e39a8320SAlistair Francis get_field(env->mstatus, MSTATUS_TVM)) { 229e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 230e39a8320SAlistair Francis } 231e39a8320SAlistair Francis 232e39a8320SAlistair Francis helper_hyp_tlb_flush(env); 233e39a8320SAlistair Francis } 234e39a8320SAlistair Francis 2357687537aSAlistair Francis target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) 2368c5362acSAlistair Francis { 2377687537aSAlistair Francis int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 2388c5362acSAlistair Francis 2397687537aSAlistair Francis return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); 2408c5362acSAlistair Francis } 2418c5362acSAlistair Francis 2427687537aSAlistair Francis target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) 2437687537aSAlistair Francis { 2447687537aSAlistair Francis int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 2458c5362acSAlistair Francis 2467687537aSAlistair Francis return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); 2478c5362acSAlistair Francis } 2488c5362acSAlistair Francis 2490c3e702aSMichael Clark #endif /* !CONFIG_USER_ONLY */ 250