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.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
---
target/riscv/cpu.h | 28 ++++++++++++++++++++++++++++
target/riscv/internals.h | 9 +--------
target/riscv/tcg/tcg-cpu.c | 9 ++++++++-
target/riscv/translate.c | 12 ++++--------
4 files changed, 41 insertions(+), 17 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 35d1f6362c..ef870d05b3 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
@@ -718,6 +724,28 @@ static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env)
return &env_archcpu(env)->cfg;
}
+/*
+ * 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)
+{
+#if defined(CONFIG_USER_ONLY)
+ return false;
+#else
+ 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();
+ }
+#endif
+}
+
#if !defined(CONFIG_USER_ONLY)
static inline int cpu_address_mode(CPURISCVState *env)
{
diff --git a/target/riscv/internals.h b/target/riscv/internals.h
index 460346dd6d..e2f0334da8 100644
--- a/target/riscv/internals.h
+++ b/target/riscv/internals.h
@@ -64,14 +64,7 @@ static inline bool mmuidx_2stage(int mmu_idx)
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_LE;
+ return riscv_cpu_data_is_big_endian(env) ? MO_BE : MO_LE;
}
/* share data between vector helpers and decode code */
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index 3407191c22..fa42197e98 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 5df5b73849..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_LE;
+ 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