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 "qemu/log.h" 220c3e702aSMichael Clark #include "cpu.h" 230c3e702aSMichael Clark #include "qemu/main-loop.h" 240c3e702aSMichael Clark #include "exec/exec-all.h" 250c3e702aSMichael Clark #include "exec/helper-proto.h" 260c3e702aSMichael Clark 270c3e702aSMichael Clark /* Exceptions processing helpers */ 28fb738839SMichael Clark void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, 290c3e702aSMichael Clark uint32_t exception, uintptr_t pc) 300c3e702aSMichael Clark { 313109cd98SRichard Henderson CPUState *cs = env_cpu(env); 320c3e702aSMichael Clark cs->exception_index = exception; 330c3e702aSMichael Clark cpu_loop_exit_restore(cs, pc); 340c3e702aSMichael Clark } 350c3e702aSMichael Clark 360c3e702aSMichael Clark void helper_raise_exception(CPURISCVState *env, uint32_t exception) 370c3e702aSMichael Clark { 38fb738839SMichael Clark riscv_raise_exception(env, exception, 0); 390c3e702aSMichael Clark } 400c3e702aSMichael Clark 410c3e702aSMichael Clark target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, 420c3e702aSMichael Clark target_ulong csr) 430c3e702aSMichael Clark { 44c7b95171SMichael Clark target_ulong val = 0; 4557cb2083SAlistair Francis int ret = riscv_csrrw(env, csr, &val, src, -1); 4657cb2083SAlistair Francis 4757cb2083SAlistair Francis if (ret < 0) { 4857cb2083SAlistair Francis riscv_raise_exception(env, -ret, GETPC()); 49c7b95171SMichael Clark } 50c7b95171SMichael Clark return val; 510c3e702aSMichael Clark } 520c3e702aSMichael Clark 530c3e702aSMichael Clark target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, 540c3e702aSMichael Clark target_ulong csr, target_ulong rs1_pass) 550c3e702aSMichael Clark { 56c7b95171SMichael Clark target_ulong val = 0; 5757cb2083SAlistair Francis int ret = riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0); 5857cb2083SAlistair Francis 5957cb2083SAlistair Francis if (ret < 0) { 6057cb2083SAlistair Francis riscv_raise_exception(env, -ret, GETPC()); 610c3e702aSMichael Clark } 62c7b95171SMichael Clark return val; 630c3e702aSMichael Clark } 640c3e702aSMichael Clark 650c3e702aSMichael Clark target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, 660c3e702aSMichael Clark target_ulong csr, target_ulong rs1_pass) 670c3e702aSMichael Clark { 68c7b95171SMichael Clark target_ulong val = 0; 6957cb2083SAlistair Francis int ret = riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0); 7057cb2083SAlistair Francis 7157cb2083SAlistair Francis if (ret < 0) { 7257cb2083SAlistair Francis riscv_raise_exception(env, -ret, GETPC()); 730c3e702aSMichael Clark } 74c7b95171SMichael Clark return val; 750c3e702aSMichael Clark } 760c3e702aSMichael Clark 770c3e702aSMichael Clark #ifndef CONFIG_USER_ONLY 780c3e702aSMichael Clark 790c3e702aSMichael Clark target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) 800c3e702aSMichael Clark { 81*284d697cSYifei Jiang uint64_t mstatus; 82*284d697cSYifei Jiang target_ulong prev_priv, prev_virt; 83e3fba4baSAlistair Francis 840c3e702aSMichael Clark if (!(env->priv >= PRV_S)) { 85fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 860c3e702aSMichael Clark } 870c3e702aSMichael Clark 880c3e702aSMichael Clark target_ulong retpc = env->sepc; 890c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 90fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 910c3e702aSMichael Clark } 920c3e702aSMichael Clark 931a9540d1SAlistair Francis if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { 94fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 957f2b5ff1SMichael Clark } 967f2b5ff1SMichael Clark 97e39a8320SAlistair Francis if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 98e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTSR)) { 99e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 100e39a8320SAlistair Francis } 101e39a8320SAlistair Francis 102e3fba4baSAlistair Francis mstatus = env->mstatus; 103e3fba4baSAlistair Francis 104e3fba4baSAlistair Francis if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { 105e3fba4baSAlistair Francis /* We support Hypervisor extensions and virtulisation is disabled */ 106e3fba4baSAlistair Francis target_ulong hstatus = env->hstatus; 107e3fba4baSAlistair Francis 108e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 109e3fba4baSAlistair Francis prev_virt = get_field(hstatus, HSTATUS_SPV); 110e3fba4baSAlistair Francis 111f2d5850fSAlistair Francis hstatus = set_field(hstatus, HSTATUS_SPV, 0); 112f2d5850fSAlistair Francis mstatus = set_field(mstatus, MSTATUS_SPP, 0); 113e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SIE, 114e3fba4baSAlistair Francis get_field(mstatus, SSTATUS_SPIE)); 115e3fba4baSAlistair Francis mstatus = set_field(mstatus, SSTATUS_SPIE, 1); 116e3fba4baSAlistair Francis 117e3fba4baSAlistair Francis env->mstatus = mstatus; 118e3fba4baSAlistair Francis env->hstatus = hstatus; 119e3fba4baSAlistair Francis 120e3fba4baSAlistair Francis if (prev_virt) { 121e3fba4baSAlistair Francis riscv_cpu_swap_hypervisor_regs(env); 122e3fba4baSAlistair Francis } 123e3fba4baSAlistair Francis 124e3fba4baSAlistair Francis riscv_cpu_set_virt_enabled(env, prev_virt); 125e3fba4baSAlistair Francis } else { 126e3fba4baSAlistair Francis prev_priv = get_field(mstatus, MSTATUS_SPP); 127e3fba4baSAlistair Francis 1281a9540d1SAlistair Francis mstatus = set_field(mstatus, MSTATUS_SIE, 1290c3e702aSMichael Clark get_field(mstatus, MSTATUS_SPIE)); 130a37f21c2SYiting Wang mstatus = set_field(mstatus, MSTATUS_SPIE, 1); 1310c3e702aSMichael Clark mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); 132c7b95171SMichael Clark env->mstatus = mstatus; 133e3fba4baSAlistair Francis } 134e3fba4baSAlistair Francis 135e3fba4baSAlistair Francis riscv_cpu_set_mode(env, prev_priv); 1360c3e702aSMichael Clark 1370c3e702aSMichael Clark return retpc; 1380c3e702aSMichael Clark } 1390c3e702aSMichael Clark 1400c3e702aSMichael Clark target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) 1410c3e702aSMichael Clark { 1420c3e702aSMichael Clark if (!(env->priv >= PRV_M)) { 143fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 1440c3e702aSMichael Clark } 1450c3e702aSMichael Clark 1460c3e702aSMichael Clark target_ulong retpc = env->mepc; 1470c3e702aSMichael Clark if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { 148fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); 1490c3e702aSMichael Clark } 1500c3e702aSMichael Clark 151*284d697cSYifei Jiang uint64_t mstatus = env->mstatus; 1520c3e702aSMichael Clark target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); 153*284d697cSYifei 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); 1760c3e702aSMichael Clark 1779d0d1126SAlistair Francis if ((env->priv == PRV_S && 1789d0d1126SAlistair Francis get_field(env->mstatus, MSTATUS_TW)) || 1799d0d1126SAlistair Francis riscv_cpu_virt_enabled(env)) { 180e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 1817f2b5ff1SMichael Clark } else { 1820c3e702aSMichael Clark cs->halted = 1; 1830c3e702aSMichael Clark cs->exception_index = EXCP_HLT; 1840c3e702aSMichael Clark cpu_loop_exit(cs); 1850c3e702aSMichael Clark } 1867f2b5ff1SMichael Clark } 1870c3e702aSMichael Clark 1880c3e702aSMichael Clark void helper_tlb_flush(CPURISCVState *env) 1890c3e702aSMichael Clark { 1903109cd98SRichard Henderson CPUState *cs = env_cpu(env); 191b86f4167SJonathan Behrens if (!(env->priv >= PRV_S) || 192b86f4167SJonathan Behrens (env->priv == PRV_S && 193b86f4167SJonathan Behrens get_field(env->mstatus, MSTATUS_TVM))) { 194fb738839SMichael Clark riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 195e39a8320SAlistair Francis } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && 196e39a8320SAlistair Francis get_field(env->hstatus, HSTATUS_VTVM)) { 197e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 1987f2b5ff1SMichael Clark } else { 1990c3e702aSMichael Clark tlb_flush(cs); 2000c3e702aSMichael Clark } 2017f2b5ff1SMichael Clark } 2020c3e702aSMichael Clark 2032761db5fSAlistair Francis void helper_hyp_tlb_flush(CPURISCVState *env) 2042761db5fSAlistair Francis { 2052761db5fSAlistair Francis CPUState *cs = env_cpu(env); 2062761db5fSAlistair Francis 207e39a8320SAlistair Francis if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { 208e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 209e39a8320SAlistair Francis } 210e39a8320SAlistair Francis 2112761db5fSAlistair Francis if (env->priv == PRV_M || 2122761db5fSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { 2132761db5fSAlistair Francis tlb_flush(cs); 2142761db5fSAlistair Francis return; 2152761db5fSAlistair Francis } 2162761db5fSAlistair Francis 2172761db5fSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 2182761db5fSAlistair Francis } 2192761db5fSAlistair Francis 220e39a8320SAlistair Francis void helper_hyp_gvma_tlb_flush(CPURISCVState *env) 221e39a8320SAlistair Francis { 222e39a8320SAlistair Francis if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && 223e39a8320SAlistair Francis get_field(env->mstatus, MSTATUS_TVM)) { 224e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 225e39a8320SAlistair Francis } 226e39a8320SAlistair Francis 227e39a8320SAlistair Francis helper_hyp_tlb_flush(env); 228e39a8320SAlistair Francis } 229e39a8320SAlistair Francis 2308c5362acSAlistair Francis target_ulong helper_hyp_load(CPURISCVState *env, target_ulong address, 2318c5362acSAlistair Francis target_ulong attrs, target_ulong memop) 2328c5362acSAlistair Francis { 2338c5362acSAlistair Francis if (env->priv == PRV_M || 2348c5362acSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 2358c5362acSAlistair Francis (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 2368c5362acSAlistair Francis get_field(env->hstatus, HSTATUS_HU))) { 2378c5362acSAlistair Francis target_ulong pte; 2388c5362acSAlistair Francis 2398c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, true); 2408c5362acSAlistair Francis 2418c5362acSAlistair Francis switch (memop) { 2428c5362acSAlistair Francis case MO_SB: 2438c5362acSAlistair Francis pte = cpu_ldsb_data_ra(env, address, GETPC()); 2448c5362acSAlistair Francis break; 2458c5362acSAlistair Francis case MO_UB: 2468c5362acSAlistair Francis pte = cpu_ldub_data_ra(env, address, GETPC()); 2478c5362acSAlistair Francis break; 2488c5362acSAlistair Francis case MO_TESW: 2498c5362acSAlistair Francis pte = cpu_ldsw_data_ra(env, address, GETPC()); 2508c5362acSAlistair Francis break; 2518c5362acSAlistair Francis case MO_TEUW: 2528c5362acSAlistair Francis pte = cpu_lduw_data_ra(env, address, GETPC()); 2538c5362acSAlistair Francis break; 2548c5362acSAlistair Francis case MO_TESL: 2558c5362acSAlistair Francis pte = cpu_ldl_data_ra(env, address, GETPC()); 2568c5362acSAlistair Francis break; 2578c5362acSAlistair Francis case MO_TEUL: 2588c5362acSAlistair Francis pte = cpu_ldl_data_ra(env, address, GETPC()); 2598c5362acSAlistair Francis break; 2608c5362acSAlistair Francis case MO_TEQ: 2618c5362acSAlistair Francis pte = cpu_ldq_data_ra(env, address, GETPC()); 2628c5362acSAlistair Francis break; 2638c5362acSAlistair Francis default: 2648c5362acSAlistair Francis g_assert_not_reached(); 2658c5362acSAlistair Francis } 2668c5362acSAlistair Francis 2678c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, false); 2688c5362acSAlistair Francis 2698c5362acSAlistair Francis return pte; 2708c5362acSAlistair Francis } 2718c5362acSAlistair Francis 272e39a8320SAlistair Francis if (riscv_cpu_virt_enabled(env)) { 273e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 274e39a8320SAlistair Francis } else { 2758c5362acSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 276e39a8320SAlistair Francis } 2778c5362acSAlistair Francis return 0; 2788c5362acSAlistair Francis } 2798c5362acSAlistair Francis 2808c5362acSAlistair Francis void helper_hyp_store(CPURISCVState *env, target_ulong address, 2818c5362acSAlistair Francis target_ulong val, target_ulong attrs, target_ulong memop) 2828c5362acSAlistair Francis { 2838c5362acSAlistair Francis if (env->priv == PRV_M || 2848c5362acSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 2858c5362acSAlistair Francis (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 2868c5362acSAlistair Francis get_field(env->hstatus, HSTATUS_HU))) { 2878c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, true); 2888c5362acSAlistair Francis 2898c5362acSAlistair Francis switch (memop) { 2908c5362acSAlistair Francis case MO_SB: 2918c5362acSAlistair Francis case MO_UB: 2928c5362acSAlistair Francis cpu_stb_data_ra(env, address, val, GETPC()); 2938c5362acSAlistair Francis break; 2948c5362acSAlistair Francis case MO_TESW: 2958c5362acSAlistair Francis case MO_TEUW: 2968c5362acSAlistair Francis cpu_stw_data_ra(env, address, val, GETPC()); 2978c5362acSAlistair Francis break; 2988c5362acSAlistair Francis case MO_TESL: 2998c5362acSAlistair Francis case MO_TEUL: 3008c5362acSAlistair Francis cpu_stl_data_ra(env, address, val, GETPC()); 3018c5362acSAlistair Francis break; 3028c5362acSAlistair Francis case MO_TEQ: 3038c5362acSAlistair Francis cpu_stq_data_ra(env, address, val, GETPC()); 3048c5362acSAlistair Francis break; 3058c5362acSAlistair Francis default: 3068c5362acSAlistair Francis g_assert_not_reached(); 3078c5362acSAlistair Francis } 3088c5362acSAlistair Francis 3098c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, false); 3108c5362acSAlistair Francis 3118c5362acSAlistair Francis return; 3128c5362acSAlistair Francis } 3138c5362acSAlistair Francis 314e39a8320SAlistair Francis if (riscv_cpu_virt_enabled(env)) { 315e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 316e39a8320SAlistair Francis } else { 3178c5362acSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 3188c5362acSAlistair Francis } 319e39a8320SAlistair Francis } 3208c5362acSAlistair Francis 3218c5362acSAlistair Francis target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address, 3228c5362acSAlistair Francis target_ulong attrs, target_ulong memop) 3238c5362acSAlistair Francis { 3248c5362acSAlistair Francis if (env->priv == PRV_M || 3258c5362acSAlistair Francis (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || 3268c5362acSAlistair Francis (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && 3278c5362acSAlistair Francis get_field(env->hstatus, HSTATUS_HU))) { 3288c5362acSAlistair Francis target_ulong pte; 3298c5362acSAlistair Francis 3308c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, true); 3318c5362acSAlistair Francis 3328c5362acSAlistair Francis switch (memop) { 3338c5362acSAlistair Francis case MO_TEUW: 3348c5362acSAlistair Francis pte = cpu_lduw_data_ra(env, address, GETPC()); 3358c5362acSAlistair Francis break; 3361da46012SGeorg Kotheimer case MO_TEUL: 3371da46012SGeorg Kotheimer pte = cpu_ldl_data_ra(env, address, GETPC()); 3381da46012SGeorg Kotheimer break; 3398c5362acSAlistair Francis default: 3408c5362acSAlistair Francis g_assert_not_reached(); 3418c5362acSAlistair Francis } 3428c5362acSAlistair Francis 3438c5362acSAlistair Francis riscv_cpu_set_two_stage_lookup(env, false); 3448c5362acSAlistair Francis 3458c5362acSAlistair Francis return pte; 3468c5362acSAlistair Francis } 3478c5362acSAlistair Francis 348e39a8320SAlistair Francis if (riscv_cpu_virt_enabled(env)) { 349e39a8320SAlistair Francis riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); 350e39a8320SAlistair Francis } else { 3518c5362acSAlistair Francis riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); 352e39a8320SAlistair Francis } 3538c5362acSAlistair Francis return 0; 3548c5362acSAlistair Francis } 3558c5362acSAlistair Francis 3560c3e702aSMichael Clark #endif /* !CONFIG_USER_ONLY */ 357