Implement runtime big-endian data support by reading the MSTATUS
UBE/SBE/MBE bits to determine data endianness per privilege level.
The key changes are:
- Add riscv_cpu_data_is_big_endian() helper in cpu.h that checks
the appropriate MSTATUS endianness bit based on current privilege
level (MBE for M-mode, SBE for S-mode, UBE for U-mode).
- Update mo_endian() in translate.c to return MO_BE or MO_LE based
on a new 'big_endian' field in DisasContext, rather than the
previous hardcoded MO_TE.
- Update mo_endian_env() in op_helper.c to call the new helper,
giving hypervisor load/store helpers correct runtime endianness.
- Pack the endianness flag into cs_base bit 32 (alongside misa_ext
in bits 0-25) in riscv_get_tb_cpu_state(), ensuring translation
blocks are correctly separated by data endianness.
Note: instruction fetches continue to use MO_LE unconditionally
(from the previous patch), as RISC-V instructions are always
little-endian per the ISA specification.
---
target/riscv/cpu.h | 24 ++++++++++++++++++++++++
target/riscv/op_helper.c | 9 +--------
target/riscv/tcg/tcg-cpu.c | 9 ++++++++-
target/riscv/translate.c | 12 ++++--------
4 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 35d1f6362c..c644462dfa 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -703,6 +703,12 @@ FIELD(TB_FLAGS, BCFI_ENABLED, 28, 1)
FIELD(TB_FLAGS, PM_PMM, 29, 2)
FIELD(TB_FLAGS, PM_SIGNEXTEND, 31, 1)
+/*
+ * cs_base carries misa_ext (bits 0-25) plus additional flags.
+ * Bit 32 is used for data endianness since TB_FLAGS has no free bits.
+ */
+#define TB_CSBASE_BIG_ENDIAN (1ULL << 32)
+
#ifdef TARGET_RISCV32
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
#else
@@ -729,6 +735,24 @@ static inline int cpu_address_mode(CPURISCVState *env)
return mode;
}
+/*
+ * Return true if data accesses are big-endian for the current privilege
+ * level, based on the MSTATUS MBE/SBE/UBE bits.
+ */
+static inline bool riscv_cpu_data_is_big_endian(CPURISCVState *env)
+{
+ switch (env->priv) {
+ case PRV_M:
+ return env->mstatus & MSTATUS_MBE;
+ case PRV_S:
+ return env->mstatus & MSTATUS_SBE;
+ case PRV_U:
+ return env->mstatus & MSTATUS_UBE;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode)
{
RISCVMXL xl = env->misa_mxl;
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 6ccc127c30..18f7773a38 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -31,14 +31,7 @@
#ifndef CONFIG_USER_ONLY
static inline MemOp mo_endian_env(CPURISCVState *env)
{
- /*
- * A couple of bits in MSTATUS set the endianness:
- * - MSTATUS_UBE (User-mode),
- * - MSTATUS_SBE (Supervisor-mode),
- * - MSTATUS_MBE (Machine-mode)
- * but we don't implement that yet.
- */
- return MO_TE;
+ return riscv_cpu_data_is_big_endian(env) ? MO_BE : MO_LE;
}
#endif
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index 988b2d905f..e0f8fa2734 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -189,10 +189,17 @@ static TCGTBCPUState riscv_get_tb_cpu_state(CPUState *cs)
flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env));
flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext);
+ uint64_t cs_base = env->misa_ext;
+#ifndef CONFIG_USER_ONLY
+ if (riscv_cpu_data_is_big_endian(env)) {
+ cs_base |= TB_CSBASE_BIG_ENDIAN;
+ }
+#endif
+
return (TCGTBCPUState){
.pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc,
.flags = flags,
- .cs_base = env->misa_ext,
+ .cs_base = cs_base,
};
}
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 413911f7f9..d7f1f8e466 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -119,6 +119,8 @@ typedef struct DisasContext {
bool fcfi_lp_expected;
/* zicfiss extension, if shadow stack was enabled during TB gen */
bool bcfi_enabled;
+ /* Data endianness from MSTATUS UBE/SBE/MBE */
+ bool big_endian;
} DisasContext;
static inline bool has_ext(DisasContext *ctx, uint32_t ext)
@@ -128,14 +130,7 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext)
static inline MemOp mo_endian(DisasContext *ctx)
{
- /*
- * A couple of bits in MSTATUS set the endianness:
- * - MSTATUS_UBE (User-mode),
- * - MSTATUS_SBE (Supervisor-mode),
- * - MSTATUS_MBE (Machine-mode)
- * but we don't implement that yet.
- */
- return MO_TE;
+ return ctx->big_endian ? MO_BE : MO_LE;
}
#ifdef TARGET_RISCV32
@@ -1346,6 +1341,7 @@ static void riscv_tr_init_disas_context(DisasContextBase
*dcbase, CPUState *cs)
ctx->zero = tcg_constant_tl(0);
ctx->virt_inst_excp = false;
ctx->decoders = cpu->decoders;
+ ctx->big_endian = ctx->base.tb->cs_base & TB_CSBASE_BIG_ENDIAN;
}
static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
--
2.34.1