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 */ 278905770bSMarc-André Lureau G_NORETURN void 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 40a974879bSRichard Henderson target_ulong helper_csrr(CPURISCVState *env, int csr) 410c3e702aSMichael Clark { 4277442380SWeiwei Li /* 4377442380SWeiwei Li * The seed CSR must be accessed with a read-write instruction. A 4477442380SWeiwei Li * read-only instruction such as CSRRS/CSRRC with rs1=x0 or CSRRSI/ 4577442380SWeiwei Li * CSRRCI with uimm=0 will raise an illegal instruction exception. 4677442380SWeiwei Li */ 4777442380SWeiwei Li if (csr == CSR_SEED) { 4877442380SWeiwei Li riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 4977442380SWeiwei Li } 5077442380SWeiwei Li 51c7b95171SMichael Clark target_ulong val = 0; 52a974879bSRichard Henderson RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); 5357cb2083SAlistair Francis 54533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 55533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 56c7b95171SMichael Clark } 57c7b95171SMichael Clark return val; 580c3e702aSMichael Clark } 590c3e702aSMichael Clark 60a974879bSRichard Henderson void helper_csrw(CPURISCVState *env, int csr, target_ulong src) 610c3e702aSMichael Clark { 6283b519b8SLIU Zhiwei target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1; 6383b519b8SLIU Zhiwei RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask); 6457cb2083SAlistair Francis 65533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 66533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 670c3e702aSMichael Clark } 680c3e702aSMichael Clark } 690c3e702aSMichael Clark 70a974879bSRichard Henderson target_ulong helper_csrrw(CPURISCVState *env, int csr, 71a974879bSRichard Henderson target_ulong src, target_ulong write_mask) 720c3e702aSMichael Clark { 73c7b95171SMichael Clark target_ulong val = 0; 74a974879bSRichard Henderson RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); 7557cb2083SAlistair Francis 76533c91e8SAlistair Francis if (ret != RISCV_EXCP_NONE) { 77533c91e8SAlistair Francis riscv_raise_exception(env, ret, GETPC()); 780c3e702aSMichael Clark } 79c7b95171SMichael Clark return val; 800c3e702aSMichael Clark } 810c3e702aSMichael Clark 82961738ffSFrédéric Pétrot target_ulong helper_csrr_i128(CPURISCVState *env, int csr) 83961738ffSFrédéric Pétrot { 84961738ffSFrédéric Pétrot Int128 rv = int128_zero(); 85961738ffSFrédéric Pétrot RISCVException ret = riscv_csrrw_i128(env, csr, &rv, 86961738ffSFrédéric Pétrot int128_zero(), 87961738ffSFrédéric Pétrot int128_zero()); 88961738ffSFrédéric Pétrot 89961738ffSFrédéric Pétrot if (ret != RISCV_EXCP_NONE) { 90961738ffSFrédéric Pétrot riscv_raise_exception(env, ret, GETPC()); 91961738ffSFrédéric Pétrot } 92961738ffSFrédéric Pétrot 93961738ffSFrédéric Pétrot env->retxh = int128_gethi(rv); 94961738ffSFrédéric Pétrot return int128_getlo(rv); 95961738ffSFrédéric Pétrot } 96961738ffSFrédéric Pétrot 97961738ffSFrédéric Pétrot void helper_csrw_i128(CPURISCVState *env, int csr, 98961738ffSFrédéric Pétrot target_ulong srcl, target_ulong srch) 99961738ffSFrédéric Pétrot { 100961738ffSFrédéric Pétrot RISCVException ret = riscv_csrrw_i128(env, csr, NULL, 101961738ffSFrédéric Pétrot int128_make128(srcl, srch), 102961738ffSFrédéric Pétrot UINT128_MAX); 103961738ffSFrédéric Pétrot 104961738ffSFrédéric Pétrot if (ret != RISCV_EXCP_NONE) { 105961738ffSFrédéric Pétrot riscv_raise_exception(env, ret, GETPC()); 106961738ffSFrédéric Pétrot } 107961738ffSFrédéric Pétrot } 108961738ffSFrédéric Pétrot 109961738ffSFrédéric Pétrot target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, 110961738ffSFrédéric Pétrot target_ulong srcl, target_ulong srch, 111961738ffSFrédéric Pétrot target_ulong maskl, target_ulong maskh) 112961738ffSFrédéric Pétrot { 113961738ffSFrédéric Pétrot Int128 rv = int128_zero(); 114961738ffSFrédéric Pétrot RISCVException ret = riscv_csrrw_i128(env, csr, &rv, 115961738ffSFrédéric Pétrot int128_make128(srcl, srch), 116961738ffSFrédéric Pétrot int128_make128(maskl, maskh)); 117961738ffSFrédéric Pétrot 118961738ffSFrédéric Pétrot if (ret != RISCV_EXCP_NONE) { 119961738ffSFrédéric Pétrot riscv_raise_exception(env, ret, GETPC()); 120961738ffSFrédéric Pétrot } 121961738ffSFrédéric Pétrot 122961738ffSFrédéric Pétrot env->retxh = int128_gethi(rv); 123961738ffSFrédéric Pétrot return int128_getlo(rv); 124961738ffSFrédéric Pétrot } 125961738ffSFrédéric Pétrot 1260c3e702aSMichael Clark #ifndef CONFIG_USER_ONLY 1270c3e702aSMichael Clark 128b655dc7cSLIU Zhiwei target_ulong helper_sret(CPURISCVState *env) 1290c3e702aSMichael Clark { 130284d697cSYifei Jiang uint64_t mstatus; 131284d697cSYifei Jiang target_ulong prev_priv, prev_virt; 132e3fba4baSAlistair Francis 1330c3e702aSMichael Clark if (!(env->priv >= PRV_S)) { 134fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 1350c3e702aSMichael Clark } 1360c3e702aSMichael Clark 1370c3e702aSMichael Clark target_ulong retpc = env->sepc; 1380c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 139fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 1400c3e702aSMichael Clark } 1410c3e702aSMichael Clark 1421a9540d1SAlistair Francis if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { 143fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 1447f2b5ff1SMichael Clark } 1457f2b5ff1SMichael Clark 146e39a8320SAlistair Francis if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 147e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTSR)) { 148e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 149e39a8320SAlistair Francis } 150e39a8320SAlistair Francis 151e3fba4baSAlistair Francis mstatus = env->mstatus; 152e3fba4baSAlistair Francis 153e3fba4baSAlistair Francis if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 154e3fba4baSAlistair Francis /* We support Hypervisor extensions and virtulisation is disabled */ 155e3fba4baSAlistair Francis target_ulong hstatus = env->hstatus; 156e3fba4baSAlistair Francis 157e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 158e3fba4baSAlistair Francis prev_virt = get_field(hstatus, HSTATUS_SPV); 159e3fba4baSAlistair Francis 160f2d5850fSAlistair Francis hstatus = set_field(hstatus, HSTATUS_SPV, 0); 161f2d5850fSAlistair Francis mstatus = set_field(mstatus, MSTATUS_SPP, 0); 162e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SIE, 163e3fba4baSAlistair Francis get_field(mstatus, SSTATUS_SPIE)); 164e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SPIE, 1); 165e3fba4baSAlistair Francis 166e3fba4baSAlistair Francis env->mstatus = mstatus; 167e3fba4baSAlistair Francis env->hstatus = hstatus; 168e3fba4baSAlistair Francis 169e3fba4baSAlistair Francis if (prev_virt) { 170e3fba4baSAlistair Francis riscv_cpu_swap_hypervisor_regs(env); 171e3fba4baSAlistair Francis } 172e3fba4baSAlistair Francis 173e3fba4baSAlistair Francis riscv_cpu_set_virt_enabled(env, prev_virt); 174e3fba4baSAlistair Francis } else { 175e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 176e3fba4baSAlistair Francis 1771a9540d1SAlistair Francis mstatus = set_field(mstatus, MSTATUS_SIE, 1780c3e702aSMichael Clark get_field(mstatus, MSTATUS_SPIE)); 179a37f21c2SYiting Wang mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 1800c3e702aSMichael Clark mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 181c7b95171SMichael Clark env->mstatus = mstatus; 182e3fba4baSAlistair Francis } 183e3fba4baSAlistair Francis 184e3fba4baSAlistair Francis riscv_cpu_set_mode(env, prev_priv); 1850c3e702aSMichael Clark 1860c3e702aSMichael Clark return retpc; 1870c3e702aSMichael Clark } 1880c3e702aSMichael Clark 189b655dc7cSLIU Zhiwei target_ulong helper_mret(CPURISCVState *env) 1900c3e702aSMichael Clark { 1910c3e702aSMichael Clark if (!(env->priv >= PRV_M)) { 192fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 1930c3e702aSMichael Clark } 1940c3e702aSMichael Clark 1950c3e702aSMichael Clark target_ulong retpc = env->mepc; 1960c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 197fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 1980c3e702aSMichael Clark } 1990c3e702aSMichael Clark 200284d697cSYifei Jiang uint64_t mstatus = env->mstatus; 2010c3e702aSMichael Clark target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 202d102f19aSAtish Patra 2030fbb5d2dSNikita Shubin if (riscv_feature(env, RISCV_FEATURE_PMP) && 2040fbb5d2dSNikita Shubin !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { 205*4c48aad1SBin Meng riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); 206d102f19aSAtish Patra } 207d102f19aSAtish Patra 208284d697cSYifei Jiang target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); 2091a9540d1SAlistair Francis mstatus = set_field(mstatus, MSTATUS_MIE, 2100c3e702aSMichael Clark get_field(mstatus, MSTATUS_MPIE)); 211a37f21c2SYiting Wang mstatus = set_field(mstatus, MSTATUS_MPIE, 1); 2120c3e702aSMichael Clark mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); 213e3fba4baSAlistair Francis mstatus = set_field(mstatus, MSTATUS_MPV, 0); 214c7b95171SMichael Clark env->mstatus = mstatus; 215e3fba4baSAlistair Francis riscv_cpu_set_mode(env, prev_priv); 216e3fba4baSAlistair Francis 217e3fba4baSAlistair Francis if (riscv_has_ext(env, RVH)) { 218e3fba4baSAlistair Francis if (prev_virt) { 219e3fba4baSAlistair Francis riscv_cpu_swap_hypervisor_regs(env); 220e3fba4baSAlistair Francis } 221e3fba4baSAlistair Francis 222e3fba4baSAlistair Francis riscv_cpu_set_virt_enabled(env, prev_virt); 223e3fba4baSAlistair Francis } 2240c3e702aSMichael Clark 2250c3e702aSMichael Clark return retpc; 2260c3e702aSMichael Clark } 2270c3e702aSMichael Clark 2280c3e702aSMichael Clark void helper_wfi(CPURISCVState *env) 2290c3e702aSMichael Clark { 2303109cd98SRichard Henderson CPUState *cs = env_cpu(env); 231719f0f60SJose Martins bool rvs = riscv_has_ext(env, RVS); 232719f0f60SJose Martins bool prv_u = env->priv == PRV_U; 233719f0f60SJose Martins bool prv_s = env->priv == PRV_S; 2340c3e702aSMichael Clark 235719f0f60SJose Martins if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || 236719f0f60SJose Martins (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { 237719f0f60SJose Martins riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 238719f0f60SJose Martins } else if (riscv_cpu_virt_enabled(env) && (prv_u || 239719f0f60SJose Martins (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { 240e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 2417f2b5ff1SMichael Clark } else { 2420c3e702aSMichael Clark cs->halted = 1; 2430c3e702aSMichael Clark cs->exception_index = EXCP_HLT; 2440c3e702aSMichael Clark cpu_loop_exit(cs); 2450c3e702aSMichael Clark } 2467f2b5ff1SMichael Clark } 2470c3e702aSMichael Clark 2480c3e702aSMichael Clark void helper_tlb_flush(CPURISCVState *env) 2490c3e702aSMichael Clark { 2503109cd98SRichard Henderson CPUState *cs = env_cpu(env); 251b86f4167SJonathan Behrens if (!(env->priv >= PRV_S) || 252b86f4167SJonathan Behrens (env->priv == PRV_S && 253b86f4167SJonathan Behrens get_field(env->mstatus, MSTATUS_TVM))) { 254fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 255e39a8320SAlistair Francis } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 256e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTVM)) { 257e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 2587f2b5ff1SMichael Clark } else { 2590c3e702aSMichael Clark tlb_flush(cs); 2600c3e702aSMichael Clark } 2617f2b5ff1SMichael Clark } 2620c3e702aSMichael Clark 2632761db5fSAlistair Francis void helper_hyp_tlb_flush(CPURISCVState *env) 2642761db5fSAlistair Francis { 2652761db5fSAlistair Francis CPUState *cs = env_cpu(env); 2662761db5fSAlistair Francis 267e39a8320SAlistair Francis if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { 268e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 269e39a8320SAlistair Francis } 270e39a8320SAlistair Francis 2712761db5fSAlistair Francis if (env->priv == PRV_M || 2722761db5fSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 2732761db5fSAlistair Francis tlb_flush(cs); 2742761db5fSAlistair Francis return; 2752761db5fSAlistair Francis } 2762761db5fSAlistair Francis 2772761db5fSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 2782761db5fSAlistair Francis } 2792761db5fSAlistair Francis 280e39a8320SAlistair Francis void helper_hyp_gvma_tlb_flush(CPURISCVState *env) 281e39a8320SAlistair Francis { 282e39a8320SAlistair Francis if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && 283e39a8320SAlistair Francis get_field(env->mstatus, MSTATUS_TVM)) { 284e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 285e39a8320SAlistair Francis } 286e39a8320SAlistair Francis 287e39a8320SAlistair Francis helper_hyp_tlb_flush(env); 288e39a8320SAlistair Francis } 289e39a8320SAlistair Francis 2907687537aSAlistair Francis target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) 2918c5362acSAlistair Francis { 2927687537aSAlistair Francis int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 2938c5362acSAlistair Francis 2947687537aSAlistair Francis return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); 2958c5362acSAlistair Francis } 2968c5362acSAlistair Francis 2977687537aSAlistair Francis target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) 2987687537aSAlistair Francis { 2997687537aSAlistair Francis int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; 3008c5362acSAlistair Francis 3017687537aSAlistair Francis return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); 3028c5362acSAlistair Francis } 3038c5362acSAlistair Francis 3040c3e702aSMichael Clark #endif /* !CONFIG_USER_ONLY */ 305