target/riscv/cpu.c | 4 ++
target/riscv/cpu.h | 5 +++
target/riscv/csr.c | 107 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 116 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index a182e8c61f..1dbeac0509 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1141,6 +1141,10 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType
type)
env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false);
}
+ if (riscv_cpu_cfg(env)->ext_smwg && env->wg_reset) {
+ env->wg_reset(env);
+ }
+
if (kvm_enabled()) {
kvm_riscv_reset_vcpu(cpu);
}
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 6dfc260a07..7bffe62f70 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -501,6 +501,11 @@ struct CPUArchState {
uint64_t rnmi_irqvec;
uint64_t rnmi_excpvec;
+ /* RISC-V WorldGuard */
+ target_ulong mlwid;
+ target_ulong slwid;
+ target_ulong mwiddeleg;
+
/* machine specific WorldGuard callback */
void (*wg_reset)(CPURISCVState *env);
void (*wid_to_mem_attrs)(MemTxAttrs *attrs, uint32_t wid);
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 7948188356..614df37d00 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -5388,6 +5388,109 @@ static int write_mnstatus(CPURISCVState *env, int
csrno, target_ulong val)
return RISCV_EXCP_NONE;
}
+/* RISC-V Worldguard */
+static RISCVException worldguard_umode(CPURISCVState *env, int csrno)
+{
+ if (!riscv_cpu_cfg(env)->ext_smwg) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ return umode(env, csrno);
+}
+
+static RISCVException worldguard_sumode(CPURISCVState *env, int csrno)
+{
+ RISCVException ret;
+
+ if (!riscv_cpu_cfg(env)->ext_sswg) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ ret = smode(env, csrno);
+
+ if (ret != RISCV_EXCP_NONE) {
+ return ret;
+ }
+
+ return umode(env, csrno);
+}
+
+static RISCVException rmw_mlwid(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ CPUState *cs = env_cpu(env);
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ target_ulong new_mlwid = (env->mlwid & ~wr_mask) | (new_val & wr_mask);
+
+ if (ret_val) {
+ *ret_val = env->mlwid;
+ }
+
+ g_assert(cpu->cfg.mwidlist);
+ if (!(BIT(new_mlwid) & cpu->cfg.mwidlist)) {
+ /* Set WID to lowest legal value if writing illegal value (WARL) */
+ new_mlwid = find_first_bit((unsigned long *)&cpu->cfg.mwidlist, 32);
+ }
+
+ if (env->mlwid != new_mlwid) {
+ env->mlwid = new_mlwid;
+ tlb_flush(cs);
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_slwid(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ target_ulong new_slwid = (env->slwid & ~wr_mask) | (new_val & wr_mask);
+
+ if (!env->mwiddeleg) {
+ /*
+ * When mwiddeleg CSR is zero, access to slwid raises an illegal
+ * instruction exception.
+ */
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ if (ret_val) {
+ *ret_val = env->slwid;
+ }
+
+ if (!(BIT(new_slwid) & env->mwiddeleg)) {
+ /* Set WID to lowest legal value if writing illegal value (WARL) */
+ new_slwid = find_first_bit(
+ (unsigned long *)&env->mwiddeleg, TARGET_LONG_BITS);
+ }
+
+ if (env->slwid != new_slwid) {
+ env->slwid = new_slwid;
+ tlb_flush(env_cpu(env));
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException rmw_mwiddeleg(CPURISCVState *env, int csrno,
+ target_ulong *ret_val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ CPUState *cs = env_cpu(env);
+ RISCVCPU *cpu = RISCV_CPU(cs);
+
+ if (ret_val) {
+ *ret_val = env->mwiddeleg;
+ }
+
+ env->mwiddeleg = (env->mwiddeleg & ~wr_mask) | (new_val & wr_mask);
+
+ /* Core wgMarker can only have WID value in mwidlist. */
+ env->mwiddeleg &= cpu->cfg.mwidlist;
+
+ return RISCV_EXCP_NONE;
+}
#endif
/* Crypto Extension */
@@ -6465,5 +6568,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
.min_priv_ver = PRIV_VERSION_1_12_0 },
+ /* RISC-V WorldGuard */
+ [CSR_MLWID] = { "mlwid", worldguard_umode, NULL, NULL, rmw_mlwid
},
+ [CSR_SLWID] = { "slwid", worldguard_sumode, NULL, NULL, rmw_slwid
},
+ [CSR_MWIDDELEG] = { "mwiddeleg", worldguard_sumode, NULL, NULL,
rmw_mwiddeleg },
#endif /* !CONFIG_USER_ONLY */
};