@@ -317,6 +317,7 @@ int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
bool riscv_cpu_fp_enabled(CPURISCVState *env);
+bool riscv_cpu_vector_enabled(CPURISCVState *env);
bool riscv_cpu_virt_enabled(CPURISCVState *env);
void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env);
@@ -415,6 +416,9 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
if (riscv_cpu_fp_enabled(env)) {
flags |= env->mstatus & MSTATUS_FS;
}
+ if (riscv_cpu_vector_enabled(env)) {
+ flags |= env->mstatus & MSTATUS_VS;
+ }
#endif
*pflags = flags;
}
@@ -376,6 +376,7 @@
#define MSTATUS_SPP 0x00000100
#define MSTATUS_MPP 0x00001800
#define MSTATUS_FS 0x00006000
+#define MSTATUS_VS 0x00000600
#define MSTATUS_XS 0x00018000
#define MSTATUS_MPRV 0x00020000
#define MSTATUS_PUM 0x00040000 /* until: priv-1.9.1 */
@@ -108,6 +108,19 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env)
return false;
}
+/* Return true is vector support is currently enabled */
+bool riscv_cpu_vector_enabled(CPURISCVState *env)
+{
+ if (env->mstatus & MSTATUS_VS) {
+ if (riscv_cpu_virt_enabled(env) && !(env->mstatus_hs & MSTATUS_VS)) {
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
{
target_ulong mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS |
@@ -180,6 +180,7 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
return -1;
}
env->mstatus |= MSTATUS_FS;
+ env->mstatus |= MSTATUS_VS;
#endif
env->frm = (val & FSR_RD) >> FSR_RD_SHIFT;
if (vs(env, csrno) >= 0) {
@@ -210,6 +211,13 @@ static int read_vxrm(CPURISCVState *env, int csrno, target_ulong *val)
static int write_vxrm(CPURISCVState *env, int csrno, target_ulong val)
{
+#if !defined(CONFIG_USER_ONLY)
+ if (!env->debugger && !riscv_cpu_vector_enabled(env)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_VS;
+#endif
+
env->vxrm = val;
return 0;
}
@@ -222,6 +230,13 @@ static int read_vxsat(CPURISCVState *env, int csrno, target_ulong *val)
static int write_vxsat(CPURISCVState *env, int csrno, target_ulong val)
{
+#if !defined(CONFIG_USER_ONLY)
+ if (!env->debugger && !riscv_cpu_vector_enabled(env)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_VS;
+#endif
+
env->vxsat = val;
return 0;
}
@@ -234,6 +249,13 @@ static int read_vstart(CPURISCVState *env, int csrno, target_ulong *val)
static int write_vstart(CPURISCVState *env, int csrno, target_ulong val)
{
+#if !defined(CONFIG_USER_ONLY)
+ if (!env->debugger && !riscv_cpu_vector_enabled(env)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_VS;
+#endif
+
env->vstart = val;
return 0;
}
@@ -420,7 +442,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR |
- MSTATUS_TW;
+ MSTATUS_TW | MSTATUS_VS;
#if defined(TARGET_RISCV64)
/*
* RV32: MPV and MTL are not in mstatus. The current plan is to
@@ -432,6 +454,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
mstatus = (mstatus & ~mask) | (val & mask);
dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
+ ((mstatus & MSTATUS_VS) == MSTATUS_VS) |
((mstatus & MSTATUS_XS) == MSTATUS_XS);
mstatus = set_field(mstatus, MSTATUS_SD, dirty);
env->mstatus = mstatus;
@@ -23,6 +23,7 @@ static bool trans_vsetvl(DisasContext *ctx, arg_vsetvl *a)
{
TCGv s1, s2, dst;
+ REQUIRE_RVV;
if (!has_ext(ctx, RVV)) {
return false;
}
@@ -48,6 +49,7 @@ static bool trans_vsetvl(DisasContext *ctx, arg_vsetvl *a)
tcg_temp_free(s1);
tcg_temp_free(s2);
tcg_temp_free(dst);
+ mark_vs_dirty(s);
return true;
}
@@ -55,6 +57,7 @@ static bool trans_vsetvli(DisasContext *ctx, arg_vsetvli *a)
{
TCGv s1, s2, dst;
+ REQUIRE_RVV;
if (!has_ext(ctx, RVV)) {
return false;
}
@@ -78,6 +81,7 @@ static bool trans_vsetvli(DisasContext *ctx, arg_vsetvli *a)
tcg_temp_free(s1);
tcg_temp_free(s2);
tcg_temp_free(dst);
+ mark_vs_dirty(s);
return true;
}
@@ -235,6 +239,7 @@ static bool ld_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq)
{ NULL, NULL,
gen_helper_vlwu_v_w, gen_helper_vlwu_v_d } }
};
+ bool ret;
fn = fns[a->vm][seq][s->sew];
if (fn == NULL) {
@@ -245,7 +250,9 @@ static bool ld_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq)
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
data = FIELD_DP32(data, VDATA, NF, a->nf);
- return ldst_us_trans(a->rd, a->rs1, data, fn, s);
+ ret = ldst_us_trans(a->rd, a->rs1, data, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
static bool ld_us_check(DisasContext *s, arg_r2nfvm* a)
@@ -372,6 +379,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t seq)
{ NULL, NULL,
gen_helper_vlswu_v_w, gen_helper_vlswu_v_d },
};
+ bool ret;
fn = fns[seq][s->sew];
if (fn == NULL) {
@@ -382,7 +390,9 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t seq)
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
data = FIELD_DP32(data, VDATA, NF, a->nf);
- return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ ret = ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
static bool ld_stride_check(DisasContext *s, arg_rnfvm* a)
@@ -500,6 +510,7 @@ static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t seq)
{ NULL, NULL,
gen_helper_vlxwu_v_w, gen_helper_vlxwu_v_d },
};
+ bool ret;
fn = fns[seq][s->sew];
if (fn == NULL) {
@@ -510,7 +521,9 @@ static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t seq)
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
data = FIELD_DP32(data, VDATA, NF, a->nf);
- return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ ret = ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
static bool ld_index_check(DisasContext *s, arg_rnfvm* a)
@@ -622,6 +635,7 @@ static bool ldff_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq)
{ NULL, NULL,
gen_helper_vlwuff_v_w, gen_helper_vlwuff_v_d }
};
+ bool ret;
fn = fns[seq][s->sew];
if (fn == NULL) {
@@ -632,7 +646,9 @@ static bool ldff_op(DisasContext *s, arg_r2nfvm *a, uint8_t seq)
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
data = FIELD_DP32(data, VDATA, NF, a->nf);
- return ldff_trans(a->rd, a->rs1, data, fn, s);
+ ret = ldff_trans(a->rd, a->rs1, data, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
GEN_VEXT_TRANS(vlbff_v, 0, r2nfvm, ldff_op, ld_us_check)
@@ -719,6 +735,7 @@ static bool amo_op(DisasContext *s, arg_rwdvm *a, uint8_t seq)
gen_helper_vamomaxud_v_d
};
#endif
+ bool ret;
if (tb_cflags(s->base.tb) & CF_PARALLEL) {
gen_helper_exit_atomic(cpu_env);
@@ -741,7 +758,9 @@ static bool amo_op(DisasContext *s, arg_rwdvm *a, uint8_t seq)
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
data = FIELD_DP32(data, VDATA, WD, a->wd);
- return amo_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ ret = amo_trans(a->rd, a->rs1, a->rs2, data, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
/*
* There are two rules check here.
@@ -823,6 +842,7 @@ do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn,
vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2),
cpu_env, 0, s->vlen / 8, data, fn);
}
+ mark_vs_dirty(s);
gen_set_label(over);
return true;
}
@@ -896,6 +916,7 @@ static inline bool
do_opivx_gvec(DisasContext *s, arg_rmrr *a, GVecGen2sFn *gvec_fn,
gen_helper_opivx *fn)
{
+ bool ret;
if (!opivx_check(s, a)) {
return false;
}
@@ -911,9 +932,12 @@ do_opivx_gvec(DisasContext *s, arg_rmrr *a, GVecGen2sFn *gvec_fn,
tcg_temp_free_i64(src1);
tcg_temp_free(tmp);
+ mark_vs_dirty(s);
return true;
}
- return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s);
+ ret = opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s);
+ mark_vs_dirty(s);
+ return ret;
}
/* OPIVX with GVEC IR */
@@ -1035,6 +1059,7 @@ static inline bool
do_opivi_gvec(DisasContext *s, arg_rmrr *a, GVecGen2iFn *gvec_fn,
gen_helper_opivx *fn, int zx)
{
+ bool ret;
if (!opivx_check(s, a)) {
return false;
}
@@ -1047,10 +1072,12 @@ do_opivi_gvec(DisasContext *s, arg_rmrr *a, GVecGen2iFn *gvec_fn,
gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2),
sextract64(a->rs1, 0, 5), MAXSZ(s), MAXSZ(s));
}
+ ret = true;
} else {
- return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s, zx);
+ ret = opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s, zx);
}
- return true;
+ mark_vs_dirty(s);
+ return ret;
}
/* OPIVI with GVEC IR */
@@ -1111,6 +1138,7 @@ static bool do_opivv_widen(DisasContext *s, arg_rmrr *a,
vreg_ofs(s, a->rs2),
cpu_env, 0, s->vlen / 8,
data, fn);
+ mark_vs_dirty(s);
gen_set_label(over);
return true;
}
@@ -1198,6 +1226,7 @@ static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a,
vreg_ofs(s, a->rs1),
vreg_ofs(s, a->rs2),
cpu_env, 0, s->vlen / 8, data, fn);
+ mark_vs_dirty(s);
gen_set_label(over);
return true;
}
@@ -1276,6 +1305,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \
vreg_ofs(s, a->rs1), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -1407,6 +1437,7 @@ do_opivx_gvec_shift(DisasContext *s, arg_rmrr *a, GVecGen2sFn32 *gvec_fn,
tcg_temp_free_i32(src1);
tcg_temp_free(tmp);
+ mark_vs_dirty(s);
return true;
}
return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s);
@@ -1465,6 +1496,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \
vreg_ofs(s, a->rs1), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -1830,6 +1862,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \
vreg_ofs(s, a->rs1), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -1942,6 +1975,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \
vreg_ofs(s, a->rs1), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -2016,6 +2050,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \
vreg_ofs(s, a->rs1), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -2130,6 +2165,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \
tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -2270,6 +2306,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \
tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -2318,6 +2355,7 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \
tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \
vreg_ofs(s, a->rs2), cpu_env, 0, \
s->vlen / 8, data, fns[s->sew - 1]); \
+ mark_vs_dirty(s); \
gen_set_label(over); \
return true; \
} \
@@ -2824,6 +2862,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a)
tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd),
MAXSZ(s), MAXSZ(s), dest);
tcg_temp_free_i64(dest);
+ mark_vs_dirty(s);
} else {
static gen_helper_opivx * const fns[4] = {
gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h,
@@ -2850,6 +2889,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a)
endian_ofs(s, a->rs2, a->rs1),
MAXSZ(s), MAXSZ(s));
}
+ mark_vs_dirty(s);
} else {
static gen_helper_opivx * const fns[4] = {
gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h,
@@ -2886,6 +2926,7 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a)
tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0),
vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2),
cpu_env, 0, s->vlen / 8, data, fns[s->sew]);
+ mark_vs_dirty(s);
gen_set_label(over);
return true;
}
@@ -47,6 +47,7 @@ typedef struct DisasContext {
bool virt_enabled;
uint32_t opcode;
uint32_t mstatus_fs;
+ uint32_t mstatus_vs;
uint32_t misa;
uint32_t mem_idx;
/* Remember the rounding mode encoded in the previous fp instruction,
@@ -416,6 +417,37 @@ static void mark_fs_dirty(DisasContext *ctx)
static inline void mark_fs_dirty(DisasContext *ctx) { }
#endif
+#ifndef CONFIG_USER_ONLY
+/* The states of mstatus_vs are:
+ * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty
+ * We will have already diagnosed disabled state,
+ * and need to turn initial/clean into dirty.
+ */
+static void mark_vs_dirty(DisasContext *ctx)
+{
+ TCGv tmp;
+ if (ctx->mstatus_vs == MSTATUS_VS) {
+ return;
+ }
+ /* Remember the state change for the rest of the TB. */
+ ctx->mstatus_vs = MSTATUS_VS;
+
+ tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS | MSTATUS_SD);
+ tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+
+ if (ctx->virt_enabled) {
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs));
+ tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS | MSTATUS_SD);
+ tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs));
+ }
+ tcg_temp_free(tmp);
+}
+#else
+static inline void mark_vs_dirty(DisasContext *ctx) { }
+#endif
+
#if !defined(TARGET_RISCV64)
static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
int rs1, target_long imm)