aboutsummaryrefslogtreecommitdiff
path: root/target/riscv/csr.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/riscv/csr.c')
-rw-r--r--target/riscv/csr.c103
1 files changed, 79 insertions, 24 deletions
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5e7e7d16b8..960d2b0aa9 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -56,9 +56,15 @@ static int fs(CPURISCVState *env, int csrno)
static int ctr(CPURISCVState *env, int csrno)
{
#if !defined(CONFIG_USER_ONLY)
- target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
- env->priv == PRV_S ? env->mcounteren : -1U;
- if (!(ctr_en & (1 << (csrno & 31)))) {
+ uint32_t ctr_en = ~0u;
+
+ if (env->priv < PRV_M) {
+ ctr_en &= env->mcounteren;
+ }
+ if (env->priv < PRV_S) {
+ ctr_en &= env->scounteren;
+ }
+ if (!(ctr_en & (1u << (csrno & 31)))) {
return -1;
}
#endif
@@ -90,7 +96,7 @@ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
return -1;
}
#endif
- *val = cpu_riscv_get_fflags(env);
+ *val = riscv_cpu_get_fflags(env);
return 0;
}
@@ -102,7 +108,7 @@ static int write_fflags(CPURISCVState *env, int csrno, target_ulong val)
}
env->mstatus |= MSTATUS_FS;
#endif
- cpu_riscv_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT));
+ riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT));
return 0;
}
@@ -136,7 +142,7 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val)
return -1;
}
#endif
- *val = (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT)
+ *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT)
| (env->frm << FSR_RD_SHIFT);
return 0;
}
@@ -150,7 +156,7 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
env->mstatus |= MSTATUS_FS;
#endif
env->frm = (val & FSR_RD) >> FSR_RD_SHIFT;
- cpu_riscv_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT);
+ riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT);
return 0;
}
@@ -305,7 +311,8 @@ 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_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR |
+ MSTATUS_TW;
}
/* silenty discard mstatus.mpp writes for unsupported modes */
@@ -317,18 +324,6 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
mstatus = (mstatus & ~mask) | (val & mask);
- /* Note: this is a workaround for an issue where mstatus.FS
- does not report dirty after floating point operations
- that modify floating point state. This workaround is
- technically compliant with the RISC-V Privileged
- specification as it is legal to return only off, or dirty.
- at the expense of extra floating point save/restore. */
-
- /* FP is always dirty or off */
- if (mstatus & MSTATUS_FS) {
- mstatus |= MSTATUS_FS;
- }
-
int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
((mstatus & MSTATUS_XS) == MSTATUS_XS);
mstatus = set_field(mstatus, MSTATUS_SD, dirty);
@@ -343,6 +338,58 @@ static int read_misa(CPURISCVState *env, int csrno, target_ulong *val)
return 0;
}
+static int write_misa(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (!riscv_feature(env, RISCV_FEATURE_MISA)) {
+ /* drop write to misa */
+ return 0;
+ }
+
+ /* 'I' or 'E' must be present */
+ if (!(val & (RVI | RVE))) {
+ /* It is not, drop write to misa */
+ return 0;
+ }
+
+ /* 'E' excludes all other extensions */
+ if (val & RVE) {
+ /* when we support 'E' we can do "val = RVE;" however
+ * for now we just drop writes if 'E' is present.
+ */
+ return 0;
+ }
+
+ /* Mask extensions that are not supported by this hart */
+ val &= env->misa_mask;
+
+ /* Mask extensions that are not supported by QEMU */
+ val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+
+ /* 'D' depends on 'F', so clear 'D' if 'F' is not present */
+ if ((val & RVD) && !(val & RVF)) {
+ val &= ~RVD;
+ }
+
+ /* Suppress 'C' if next instruction is not aligned
+ * TODO: this should check next_pc
+ */
+ if ((val & RVC) && (GETPC() & ~3) != 0) {
+ val &= ~RVC;
+ }
+
+ /* misa.MXL writes are not supported by QEMU */
+ val = (env->misa & MISA_MXL) | (val & ~MISA_MXL);
+
+ /* flush translation cache */
+ if (val != env->misa) {
+ tb_flush(CPU(riscv_env_get_cpu(env)));
+ }
+
+ env->misa = val;
+
+ return 0;
+}
+
static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->medeleg;
@@ -654,7 +701,11 @@ static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
*val = 0;
} else if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- *val = env->satp;
+ if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+ return -1;
+ } else {
+ *val = env->satp;
+ }
} else {
*val = env->sptbr;
}
@@ -675,8 +726,12 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val)
validate_vm(env, get_field(val, SATP_MODE)) &&
((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
{
- tlb_flush(CPU(riscv_env_get_cpu(env)));
- env->satp = val;
+ if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+ return -1;
+ } else {
+ tlb_flush(CPU(riscv_env_get_cpu(env)));
+ env->satp = val;
+ }
}
return 0;
}
@@ -813,7 +868,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Machine Trap Setup */
[CSR_MSTATUS] = { any, read_mstatus, write_mstatus },
- [CSR_MISA] = { any, read_misa },
+ [CSR_MISA] = { any, read_misa, write_misa },
[CSR_MIDELEG] = { any, read_mideleg, write_mideleg },
[CSR_MEDELEG] = { any, read_medeleg, write_medeleg },
[CSR_MIE] = { any, read_mie, write_mie },