The RV32 stateen CSRs are split between the low-half CSR and the
corresponding xH CSR, but the current implementation still handles some
upper-half bits through the low-half write paths and also accepts the
xH CSRs on RV64.
Fix this by:
- rejecting mstateen*h and hstateen*h accesses on RV64
- keeping the RV64-only writable bits in the low-half write paths
- handling the RV32 upper-half writable bits in write_mstateen0h() and
write_hstateen0h()
- dropping unsupported writable bits from write_sstateen0()
Signed-off-by: Bruno Sa <bruno.vilaca.sa@gmail.com>
---
target/riscv/csr.c | 112 +++++++++++++++++++++++++++++++--------------
1 file changed, 77 insertions(+), 35 deletions(-)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index d322bdbd47..015deca6dc 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -497,6 +497,15 @@ static RISCVException mstateen(CPURISCVState *env, int csrno)
return any(env, csrno);
}
+static RISCVException mstateen_32(CPURISCVState *env, int csrno)
+{
+ if (riscv_cpu_mxl(env) != MXL_RV32) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ return mstateen(env, csrno);
+}
+
static RISCVException hstateen_pred(CPURISCVState *env, int csrno, int base)
{
if (!riscv_cpu_cfg(env)->ext_smstateen) {
@@ -528,6 +537,10 @@ static RISCVException hstateen(CPURISCVState *env, int csrno)
static RISCVException hstateenh(CPURISCVState *env, int csrno)
{
+ if (riscv_cpu_mxl(env) != MXL_RV32) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
return hstateen_pred(env, csrno, CSR_HSTATEEN0H);
}
@@ -3403,25 +3416,27 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno,
wr_mask |= SMSTATEEN0_FCSR;
}
- if (env->priv_ver >= PRIV_VERSION_1_13_0) {
- wr_mask |= SMSTATEEN0_P1P13;
- }
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ if (env->priv_ver >= PRIV_VERSION_1_13_0) {
+ wr_mask |= SMSTATEEN0_P1P13;
+ }
- if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
- wr_mask |= SMSTATEEN0_SVSLCT;
- }
+ if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
+ wr_mask |= SMSTATEEN0_SVSLCT;
+ }
- /*
- * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
- * implemented. However, that information is with MachineState and we can't
- * figure that out in csr.c. Just enable if Smaia is available.
- */
- if (riscv_cpu_cfg(env)->ext_smaia) {
- wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
- }
+ /*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Smaia is available.
+ */
+ if (riscv_cpu_cfg(env)->ext_smaia) {
+ wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+ }
- if (riscv_cpu_cfg(env)->ext_ssctr) {
- wr_mask |= SMSTATEEN0_CTR;
+ if (riscv_cpu_cfg(env)->ext_ssctr) {
+ wr_mask |= SMSTATEEN0_CTR;
+ }
}
return write_mstateen(env, csrno, wr_mask, new_val);
@@ -3463,6 +3478,19 @@ static RISCVException write_mstateen0h(CPURISCVState *env, int csrno,
wr_mask |= SMSTATEEN0_P1P13;
}
+ if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
+ wr_mask |= SMSTATEEN0_SVSLCT;
+ }
+
+ /*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Smaia is available.
+ */
+ if (riscv_cpu_cfg(env)->ext_smaia) {
+ wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+ }
+
if (riscv_cpu_cfg(env)->ext_ssctr) {
wr_mask |= SMSTATEEN0_CTR;
}
@@ -3507,22 +3535,23 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno,
if (!riscv_has_ext(env, RVF)) {
wr_mask |= SMSTATEEN0_FCSR;
}
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
+ wr_mask |= SMSTATEEN0_SVSLCT;
+ }
- if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
- wr_mask |= SMSTATEEN0_SVSLCT;
- }
-
- /*
- * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
- * implemented. However, that information is with MachineState and we can't
- * figure that out in csr.c. Just enable if Ssaia is available.
- */
- if (riscv_cpu_cfg(env)->ext_ssaia) {
- wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
- }
+ /*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Ssaia is available.
+ */
+ if (riscv_cpu_cfg(env)->ext_ssaia) {
+ wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+ }
- if (riscv_cpu_cfg(env)->ext_ssctr) {
- wr_mask |= SMSTATEEN0_CTR;
+ if (riscv_cpu_cfg(env)->ext_ssctr) {
+ wr_mask |= SMSTATEEN0_CTR;
+ }
}
return write_hstateen(env, csrno, wr_mask, new_val);
@@ -3564,6 +3593,19 @@ static RISCVException write_hstateen0h(CPURISCVState *env, int csrno,
{
uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG;
+ if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
+ wr_mask |= SMSTATEEN0_SVSLCT;
+ }
+
+ /*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Ssaia is available.
+ */
+ if (riscv_cpu_cfg(env)->ext_ssaia) {
+ wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+ }
+
if (riscv_cpu_cfg(env)->ext_ssctr) {
wr_mask |= SMSTATEEN0_CTR;
}
@@ -3613,7 +3655,7 @@ static RISCVException write_sstateen(CPURISCVState *env, int csrno,
static RISCVException write_sstateen0(CPURISCVState *env, int csrno,
target_ulong new_val)
{
- uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG;
+ uint64_t wr_mask = 0;
if (!riscv_has_ext(env, RVF)) {
wr_mask |= SMSTATEEN0_FCSR;
@@ -5861,25 +5903,25 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Smstateen extension CSRs */
[CSR_MSTATEEN0] = { "mstateen0", mstateen, read_mstateen, write_mstateen0,
.min_priv_ver = PRIV_VERSION_1_12_0 },
- [CSR_MSTATEEN0H] = { "mstateen0h", mstateen, read_mstateenh,
+ [CSR_MSTATEEN0H] = { "mstateen0h", mstateen_32, read_mstateenh,
write_mstateen0h,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MSTATEEN1] = { "mstateen1", mstateen, read_mstateen,
write_mstateen_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
- [CSR_MSTATEEN1H] = { "mstateen1h", mstateen, read_mstateenh,
+ [CSR_MSTATEEN1H] = { "mstateen1h", mstateen_32, read_mstateenh,
write_mstateenh_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MSTATEEN2] = { "mstateen2", mstateen, read_mstateen,
write_mstateen_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
- [CSR_MSTATEEN2H] = { "mstateen2h", mstateen, read_mstateenh,
+ [CSR_MSTATEEN2H] = { "mstateen2h", mstateen_32, read_mstateenh,
write_mstateenh_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MSTATEEN3] = { "mstateen3", mstateen, read_mstateen,
write_mstateen_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
- [CSR_MSTATEEN3H] = { "mstateen3h", mstateen, read_mstateenh,
+ [CSR_MSTATEEN3H] = { "mstateen3h", mstateen_32, read_mstateenh,
write_mstateenh_1_3,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HSTATEEN0] = { "hstateen0", hstateen, read_hstateen, write_hstateen0,
--
2.43.0
On Fri, Apr 10, 2026 at 3:27 AM Bruno Sa <bruno.vilaca.sa@gmail.com> wrote:
>
> The RV32 stateen CSRs are split between the low-half CSR and the
> corresponding xH CSR, but the current implementation still handles some
> upper-half bits through the low-half write paths and also accepts the
> xH CSRs on RV64.
>
> Fix this by:
> - rejecting mstateen*h and hstateen*h accesses on RV64
> - keeping the RV64-only writable bits in the low-half write paths
> - handling the RV32 upper-half writable bits in write_mstateen0h() and
> write_hstateen0h()
> - dropping unsupported writable bits from write_sstateen0()
>
> Signed-off-by: Bruno Sa <bruno.vilaca.sa@gmail.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/csr.c | 112 +++++++++++++++++++++++++++++++--------------
> 1 file changed, 77 insertions(+), 35 deletions(-)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index d322bdbd47..015deca6dc 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -497,6 +497,15 @@ static RISCVException mstateen(CPURISCVState *env, int csrno)
> return any(env, csrno);
> }
>
> +static RISCVException mstateen_32(CPURISCVState *env, int csrno)
> +{
> + if (riscv_cpu_mxl(env) != MXL_RV32) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
> +
> + return mstateen(env, csrno);
> +}
> +
> static RISCVException hstateen_pred(CPURISCVState *env, int csrno, int base)
> {
> if (!riscv_cpu_cfg(env)->ext_smstateen) {
> @@ -528,6 +537,10 @@ static RISCVException hstateen(CPURISCVState *env, int csrno)
>
> static RISCVException hstateenh(CPURISCVState *env, int csrno)
> {
> + if (riscv_cpu_mxl(env) != MXL_RV32) {
> + return RISCV_EXCP_ILLEGAL_INST;
> + }
> +
> return hstateen_pred(env, csrno, CSR_HSTATEEN0H);
> }
>
> @@ -3403,25 +3416,27 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno,
> wr_mask |= SMSTATEEN0_FCSR;
> }
>
> - if (env->priv_ver >= PRIV_VERSION_1_13_0) {
> - wr_mask |= SMSTATEEN0_P1P13;
> - }
> + if (riscv_cpu_mxl(env) == MXL_RV64) {
> + if (env->priv_ver >= PRIV_VERSION_1_13_0) {
> + wr_mask |= SMSTATEEN0_P1P13;
> + }
>
> - if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
> - wr_mask |= SMSTATEEN0_SVSLCT;
> - }
> + if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
> + wr_mask |= SMSTATEEN0_SVSLCT;
> + }
>
> - /*
> - * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> - * implemented. However, that information is with MachineState and we can't
> - * figure that out in csr.c. Just enable if Smaia is available.
> - */
> - if (riscv_cpu_cfg(env)->ext_smaia) {
> - wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> - }
> + /*
> + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> + * implemented. However, that information is with MachineState and we can't
> + * figure that out in csr.c. Just enable if Smaia is available.
> + */
> + if (riscv_cpu_cfg(env)->ext_smaia) {
> + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> + }
>
> - if (riscv_cpu_cfg(env)->ext_ssctr) {
> - wr_mask |= SMSTATEEN0_CTR;
> + if (riscv_cpu_cfg(env)->ext_ssctr) {
> + wr_mask |= SMSTATEEN0_CTR;
> + }
> }
>
> return write_mstateen(env, csrno, wr_mask, new_val);
> @@ -3463,6 +3478,19 @@ static RISCVException write_mstateen0h(CPURISCVState *env, int csrno,
> wr_mask |= SMSTATEEN0_P1P13;
> }
>
> + if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) {
> + wr_mask |= SMSTATEEN0_SVSLCT;
> + }
> +
> + /*
> + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> + * implemented. However, that information is with MachineState and we can't
> + * figure that out in csr.c. Just enable if Smaia is available.
> + */
> + if (riscv_cpu_cfg(env)->ext_smaia) {
> + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> + }
> +
> if (riscv_cpu_cfg(env)->ext_ssctr) {
> wr_mask |= SMSTATEEN0_CTR;
> }
> @@ -3507,22 +3535,23 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno,
> if (!riscv_has_ext(env, RVF)) {
> wr_mask |= SMSTATEEN0_FCSR;
> }
> + if (riscv_cpu_mxl(env) == MXL_RV64) {
> + if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
> + wr_mask |= SMSTATEEN0_SVSLCT;
> + }
>
> - if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
> - wr_mask |= SMSTATEEN0_SVSLCT;
> - }
> -
> - /*
> - * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> - * implemented. However, that information is with MachineState and we can't
> - * figure that out in csr.c. Just enable if Ssaia is available.
> - */
> - if (riscv_cpu_cfg(env)->ext_ssaia) {
> - wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> - }
> + /*
> + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> + * implemented. However, that information is with MachineState and we can't
> + * figure that out in csr.c. Just enable if Ssaia is available.
> + */
> + if (riscv_cpu_cfg(env)->ext_ssaia) {
> + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> + }
>
> - if (riscv_cpu_cfg(env)->ext_ssctr) {
> - wr_mask |= SMSTATEEN0_CTR;
> + if (riscv_cpu_cfg(env)->ext_ssctr) {
> + wr_mask |= SMSTATEEN0_CTR;
> + }
> }
>
> return write_hstateen(env, csrno, wr_mask, new_val);
> @@ -3564,6 +3593,19 @@ static RISCVException write_hstateen0h(CPURISCVState *env, int csrno,
> {
> uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG;
>
> + if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) {
> + wr_mask |= SMSTATEEN0_SVSLCT;
> + }
> +
> + /*
> + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
> + * implemented. However, that information is with MachineState and we can't
> + * figure that out in csr.c. Just enable if Ssaia is available.
> + */
> + if (riscv_cpu_cfg(env)->ext_ssaia) {
> + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
> + }
> +
> if (riscv_cpu_cfg(env)->ext_ssctr) {
> wr_mask |= SMSTATEEN0_CTR;
> }
> @@ -3613,7 +3655,7 @@ static RISCVException write_sstateen(CPURISCVState *env, int csrno,
> static RISCVException write_sstateen0(CPURISCVState *env, int csrno,
> target_ulong new_val)
> {
> - uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG;
> + uint64_t wr_mask = 0;
>
> if (!riscv_has_ext(env, RVF)) {
> wr_mask |= SMSTATEEN0_FCSR;
> @@ -5861,25 +5903,25 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> /* Smstateen extension CSRs */
> [CSR_MSTATEEN0] = { "mstateen0", mstateen, read_mstateen, write_mstateen0,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> - [CSR_MSTATEEN0H] = { "mstateen0h", mstateen, read_mstateenh,
> + [CSR_MSTATEEN0H] = { "mstateen0h", mstateen_32, read_mstateenh,
> write_mstateen0h,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> [CSR_MSTATEEN1] = { "mstateen1", mstateen, read_mstateen,
> write_mstateen_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> - [CSR_MSTATEEN1H] = { "mstateen1h", mstateen, read_mstateenh,
> + [CSR_MSTATEEN1H] = { "mstateen1h", mstateen_32, read_mstateenh,
> write_mstateenh_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> [CSR_MSTATEEN2] = { "mstateen2", mstateen, read_mstateen,
> write_mstateen_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> - [CSR_MSTATEEN2H] = { "mstateen2h", mstateen, read_mstateenh,
> + [CSR_MSTATEEN2H] = { "mstateen2h", mstateen_32, read_mstateenh,
> write_mstateenh_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> [CSR_MSTATEEN3] = { "mstateen3", mstateen, read_mstateen,
> write_mstateen_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> - [CSR_MSTATEEN3H] = { "mstateen3h", mstateen, read_mstateenh,
> + [CSR_MSTATEEN3H] = { "mstateen3h", mstateen_32, read_mstateenh,
> write_mstateenh_1_3,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> [CSR_HSTATEEN0] = { "hstateen0", hstateen, read_hstateen, write_hstateen0,
> --
> 2.43.0
>
>
© 2016 - 2026 Red Hat, Inc.