qemu/target/riscv/op_helper.c
<<
>>
Prefs
   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 "cpu.h"
  22#include "qemu/main-loop.h"
  23#include "exec/exec-all.h"
  24#include "exec/helper-proto.h"
  25
  26/* Exceptions processing helpers */
  27G_NORETURN void riscv_raise_exception(CPURISCVState *env,
  28                                      uint32_t exception, uintptr_t pc)
  29{
  30    CPUState *cs = env_cpu(env);
  31    cs->exception_index = exception;
  32    cpu_loop_exit_restore(cs, pc);
  33}
  34
  35void helper_raise_exception(CPURISCVState *env, uint32_t exception)
  36{
  37    riscv_raise_exception(env, exception, 0);
  38}
  39
  40target_ulong helper_csrr(CPURISCVState *env, int csr)
  41{
  42    /*
  43     * The seed CSR must be accessed with a read-write instruction. A
  44     * read-only instruction such as CSRRS/CSRRC with rs1=x0 or CSRRSI/
  45     * CSRRCI with uimm=0 will raise an illegal instruction exception.
  46     */
  47    if (csr == CSR_SEED) {
  48        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
  49    }
  50
  51    target_ulong val = 0;
  52    RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0);
  53
  54    if (ret != RISCV_EXCP_NONE) {
  55        riscv_raise_exception(env, ret, GETPC());
  56    }
  57    return val;
  58}
  59
  60void helper_csrw(CPURISCVState *env, int csr, target_ulong src)
  61{
  62    target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1;
  63    RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask);
  64
  65    if (ret != RISCV_EXCP_NONE) {
  66        riscv_raise_exception(env, ret, GETPC());
  67    }
  68}
  69
  70target_ulong helper_csrrw(CPURISCVState *env, int csr,
  71                          target_ulong src, target_ulong write_mask)
  72{
  73    target_ulong val = 0;
  74    RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask);
  75
  76    if (ret != RISCV_EXCP_NONE) {
  77        riscv_raise_exception(env, ret, GETPC());
  78    }
  79    return val;
  80}
  81
  82target_ulong helper_csrr_i128(CPURISCVState *env, int csr)
  83{
  84    Int128 rv = int128_zero();
  85    RISCVException ret = riscv_csrrw_i128(env, csr, &rv,
  86                                          int128_zero(),
  87                                          int128_zero());
  88
  89    if (ret != RISCV_EXCP_NONE) {
  90        riscv_raise_exception(env, ret, GETPC());
  91    }
  92
  93    env->retxh = int128_gethi(rv);
  94    return int128_getlo(rv);
  95}
  96
  97void helper_csrw_i128(CPURISCVState *env, int csr,
  98                      target_ulong srcl, target_ulong srch)
  99{
 100    RISCVException ret = riscv_csrrw_i128(env, csr, NULL,
 101                                          int128_make128(srcl, srch),
 102                                          UINT128_MAX);
 103
 104    if (ret != RISCV_EXCP_NONE) {
 105        riscv_raise_exception(env, ret, GETPC());
 106    }
 107}
 108
 109target_ulong helper_csrrw_i128(CPURISCVState *env, int csr,
 110                       target_ulong srcl, target_ulong srch,
 111                       target_ulong maskl, target_ulong maskh)
 112{
 113    Int128 rv = int128_zero();
 114    RISCVException ret = riscv_csrrw_i128(env, csr, &rv,
 115                                          int128_make128(srcl, srch),
 116                                          int128_make128(maskl, maskh));
 117
 118    if (ret != RISCV_EXCP_NONE) {
 119        riscv_raise_exception(env, ret, GETPC());
 120    }
 121
 122    env->retxh = int128_gethi(rv);
 123    return int128_getlo(rv);
 124}
 125
 126#ifndef CONFIG_USER_ONLY
 127
 128target_ulong helper_sret(CPURISCVState *env)
 129{
 130    uint64_t mstatus;
 131    target_ulong prev_priv, prev_virt;
 132
 133    if (!(env->priv >= PRV_S)) {
 134        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 135    }
 136
 137    target_ulong retpc = env->sepc;
 138    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
 139        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
 140    }
 141
 142    if (get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) {
 143        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 144    }
 145
 146    if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
 147        get_field(env->hstatus, HSTATUS_VTSR)) {
 148        riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
 149    }
 150
 151    mstatus = env->mstatus;
 152
 153    if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) {
 154        /* We support Hypervisor extensions and virtulisation is disabled */
 155        target_ulong hstatus = env->hstatus;
 156
 157        prev_priv = get_field(mstatus, MSTATUS_SPP);
 158        prev_virt = get_field(hstatus, HSTATUS_SPV);
 159
 160        hstatus = set_field(hstatus, HSTATUS_SPV, 0);
 161        mstatus = set_field(mstatus, MSTATUS_SPP, 0);
 162        mstatus = set_field(mstatus, SSTATUS_SIE,
 163                            get_field(mstatus, SSTATUS_SPIE));
 164        mstatus = set_field(mstatus, SSTATUS_SPIE, 1);
 165
 166        env->mstatus = mstatus;
 167        env->hstatus = hstatus;
 168
 169        if (prev_virt) {
 170            riscv_cpu_swap_hypervisor_regs(env);
 171        }
 172
 173        riscv_cpu_set_virt_enabled(env, prev_virt);
 174    } else {
 175        prev_priv = get_field(mstatus, MSTATUS_SPP);
 176
 177        mstatus = set_field(mstatus, MSTATUS_SIE,
 178                            get_field(mstatus, MSTATUS_SPIE));
 179        mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
 180        mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
 181        env->mstatus = mstatus;
 182    }
 183
 184    riscv_cpu_set_mode(env, prev_priv);
 185
 186    return retpc;
 187}
 188
 189target_ulong helper_mret(CPURISCVState *env)
 190{
 191    if (!(env->priv >= PRV_M)) {
 192        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 193    }
 194
 195    target_ulong retpc = env->mepc;
 196    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
 197        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
 198    }
 199
 200    uint64_t mstatus = env->mstatus;
 201    target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
 202
 203    if (riscv_feature(env, RISCV_FEATURE_PMP) &&
 204        !pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
 205        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 206    }
 207
 208    target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV);
 209    mstatus = set_field(mstatus, MSTATUS_MIE,
 210                        get_field(mstatus, MSTATUS_MPIE));
 211    mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
 212    mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
 213    mstatus = set_field(mstatus, MSTATUS_MPV, 0);
 214    env->mstatus = mstatus;
 215    riscv_cpu_set_mode(env, prev_priv);
 216
 217    if (riscv_has_ext(env, RVH)) {
 218        if (prev_virt) {
 219            riscv_cpu_swap_hypervisor_regs(env);
 220        }
 221
 222        riscv_cpu_set_virt_enabled(env, prev_virt);
 223    }
 224
 225    return retpc;
 226}
 227
 228void helper_wfi(CPURISCVState *env)
 229{
 230    CPUState *cs = env_cpu(env);
 231    bool rvs = riscv_has_ext(env, RVS);
 232    bool prv_u = env->priv == PRV_U;
 233    bool prv_s = env->priv == PRV_S;
 234
 235    if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) ||
 236        (rvs && prv_u && !riscv_cpu_virt_enabled(env))) {
 237        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 238    } else if (riscv_cpu_virt_enabled(env) && (prv_u ||
 239        (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) {
 240        riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
 241    } else {
 242        cs->halted = 1;
 243        cs->exception_index = EXCP_HLT;
 244        cpu_loop_exit(cs);
 245    }
 246}
 247
 248void helper_tlb_flush(CPURISCVState *env)
 249{
 250    CPUState *cs = env_cpu(env);
 251    if (!(env->priv >= PRV_S) ||
 252        (env->priv == PRV_S &&
 253         get_field(env->mstatus, MSTATUS_TVM))) {
 254        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 255    } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) &&
 256               get_field(env->hstatus, HSTATUS_VTVM)) {
 257        riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
 258    } else {
 259        tlb_flush(cs);
 260    }
 261}
 262
 263void helper_hyp_tlb_flush(CPURISCVState *env)
 264{
 265    CPUState *cs = env_cpu(env);
 266
 267    if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) {
 268        riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC());
 269    }
 270
 271    if (env->priv == PRV_M ||
 272        (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) {
 273        tlb_flush(cs);
 274        return;
 275    }
 276
 277    riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 278}
 279
 280void helper_hyp_gvma_tlb_flush(CPURISCVState *env)
 281{
 282    if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) &&
 283        get_field(env->mstatus, MSTATUS_TVM)) {
 284        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
 285    }
 286
 287    helper_hyp_tlb_flush(env);
 288}
 289
 290target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address)
 291{
 292    int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
 293
 294    return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC());
 295}
 296
 297target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address)
 298{
 299    int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK;
 300
 301    return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC());
 302}
 303
 304#endif /* !CONFIG_USER_ONLY */
 305