xref: /qemu/target/riscv/op_helper.c (revision a974879b4581b58369a1e5e01d8ce6736764c679)
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