* [PATCH v4 2/7] target/riscv: Add big-endian CPU property
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
@ 2026-03-20 15:05 ` Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 1/7] target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks Djordje Todorovic
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:05 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
Add a "big-endian" boolean property to the RISC-V CPU configuration,
defaulting to false (little-endian). This property allows machine
models to configure individual HARTs for big-endian data operation.
The RISC-V ISA supports big-endian data accesses via the mstatus
SBE/MBE/UBE bits, while instructions remain always little-endian.
This property provides the configuration interface; subsequent
patches will connect it to the CPU state and translation logic.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
---
target/riscv/cpu.c | 1 +
target/riscv/cpu_cfg_fields.h.inc | 1 +
2 files changed, 2 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index e56470a374..4537305dfe 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -2641,6 +2641,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = {
static const Property riscv_cpu_properties[] = {
DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
+ DEFINE_PROP_BOOL("big-endian", RISCVCPU, cfg.big_endian, false),
{.name = "pmu-mask", .info = &prop_pmu_mask},
{.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */
diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc
index 70ec650abf..51436daabf 100644
--- a/target/riscv/cpu_cfg_fields.h.inc
+++ b/target/riscv/cpu_cfg_fields.h.inc
@@ -154,6 +154,7 @@ BOOL_FIELD(ext_xmipscbop)
BOOL_FIELD(ext_xmipscmov)
BOOL_FIELD(ext_xmipslsp)
+BOOL_FIELD(big_endian)
BOOL_FIELD(mmu)
BOOL_FIELD(pmp)
BOOL_FIELD(debug)
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 1/7] target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 2/7] target/riscv: Add big-endian CPU property Djordje Todorovic
@ 2026-03-20 15:05 ` Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 3/7] target/riscv: Set endianness MSTATUS bits at CPU reset Djordje Todorovic
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:05 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
Add the RISC-V privileged ISA defined bit positions for the Supervisor
Big-Endian (SBE, bit 36) and Machine Big-Endian (MBE, bit 37) fields
in the mstatus register. These are used alongside the existing
MSTATUS_UBE (bit 6) to control data endianness at each privilege level.
The MSTATUS_UBE definition was already present, but SBE and MBE were
missing.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
target/riscv/cpu_bits.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index b62dd82fe7..c2a3ee4bf3 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -628,6 +628,8 @@
#define MSTATUS_TSR 0x00400000 /* since: priv-1.10 */
#define MSTATUS_SPELP 0x00800000 /* zicfilp */
#define MSTATUS_SDT 0x01000000
+#define MSTATUS_SBE 0x1000000000ULL
+#define MSTATUS_MBE 0x2000000000ULL
#define MSTATUS_MPELP 0x020000000000 /* zicfilp */
#define MSTATUS_GVA 0x4000000000ULL
#define MSTATUS_MPV 0x8000000000ULL
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 0/7] Add RISC-V big-endian target support
@ 2026-03-20 15:05 Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 2/7] target/riscv: Add big-endian CPU property Djordje Todorovic
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:05 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
- Addressed comments from v3
- Rebased on top of master
- Then rebased on top of patch set by Philippe
"target/riscv: Forbid to use legacy native endianness API"
Djordje Todorovic (7):
target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks
target/riscv: Add big-endian CPU property
target/riscv: Set endianness MSTATUS bits at CPU reset
target/riscv: Implement runtime data endianness via MSTATUS bits
hw/riscv: Make boot code endianness-aware at runtime
target/riscv: Fix page table walk endianness for big-endian harts
target/riscv: Support runtime endianness in virtio via sysemu callback
hw/riscv/boot.c | 83 ++++++++++++++++++++++++++-----
include/hw/riscv/boot.h | 2 +
target/riscv/cpu.c | 22 ++++++--
target/riscv/cpu.h | 28 +++++++++++
target/riscv/cpu_bits.h | 2 +
target/riscv/cpu_cfg_fields.h.inc | 1 +
target/riscv/cpu_helper.c | 28 ++++++++---
target/riscv/internals.h | 9 +---
target/riscv/tcg/tcg-cpu.c | 9 +++-
target/riscv/translate.c | 12 ++---
10 files changed, 156 insertions(+), 40 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 3/7] target/riscv: Set endianness MSTATUS bits at CPU reset
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 2/7] target/riscv: Add big-endian CPU property Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 1/7] target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks Djordje Todorovic
@ 2026-03-20 15:05 ` Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits Djordje Todorovic
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:05 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
When the big-endian CPU property is enabled, set the MSTATUS_UBE
(User Big-Endian), MSTATUS_SBE (Supervisor Big-Endian), and
MSTATUS_MBE (Machine Big-Endian) bits during CPU reset.
This configures all privilege levels for big-endian data access,
matching the RISC-V privileged specification's endianness control
mechanism. Instructions remain little-endian regardless.
Also update the disassembler comment to clarify that
BFD_ENDIAN_LITTLE is correct because RISC-V instructions are
always little-endian per the ISA specification.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
---
target/riscv/cpu.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 4537305dfe..eed5afd27e 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -716,6 +716,9 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type)
env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 1);
}
}
+ if (cpu->cfg.big_endian) {
+ env->mstatus |= MSTATUS_UBE | MSTATUS_SBE | MSTATUS_MBE;
+ }
env->mcause = 0;
env->miclaim = MIP_SGEIP;
env->pc = env->resetvec;
@@ -803,11 +806,8 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info)
info->target_info = &cpu->cfg;
/*
- * 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.
+ * RISC-V instructions are always little-endian, regardless of the
+ * data endianness configured via MSTATUS UBE/SBE/MBE bits.
*/
info->endian = BFD_ENDIAN_LITTLE;
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
` (2 preceding siblings ...)
2026-03-20 15:05 ` [PATCH v4 3/7] target/riscv: Set endianness MSTATUS bits at CPU reset Djordje Todorovic
@ 2026-03-20 15:06 ` Djordje Todorovic
2026-03-26 3:22 ` Richard Henderson
2026-03-20 15:06 ` [PATCH v4 6/7] target/riscv: Fix page table walk endianness for big-endian harts Djordje Todorovic
` (3 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:06 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 6/7] target/riscv: Fix page table walk endianness for big-endian harts
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
` (3 preceding siblings ...)
2026-03-20 15:06 ` [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits Djordje Todorovic
@ 2026-03-20 15:06 ` Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 5/7] hw/riscv: Make boot code endianness-aware at runtime Djordje Todorovic
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:06 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
The page table walker reads PTEs using address_space_ldl/ldq which use
compile-time native endianness (always LE for RISC-V). However, when a
big-endian kernel writes PTEs via normal store instructions, they are
stored in big-endian byte order. The walker then misinterprets the PTE
values, causing page faults and a hang when the kernel enables the MMU.
The RISC-V privileged specification states that implicit data memory
accesses to supervisor-level memory management data structures follow
the hart's endianness setting (MSTATUS SBE/MBE bits).
Fix both PTE reads and atomic A/D bit updates to use the explicit _le
or _be memory access variants based on the hart's runtime endianness.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
target/riscv/cpu_helper.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index c28832e0e3..b3d33da13e 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1365,9 +1365,13 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
}
if (riscv_cpu_mxl(env) == MXL_RV32) {
- pte = address_space_ldl_le(cs->as, pte_addr, attrs, &res);
+ pte = riscv_cpu_data_is_big_endian(env)
+ ? address_space_ldl_be(cs->as, pte_addr, attrs, &res)
+ : address_space_ldl_le(cs->as, pte_addr, attrs, &res);
} else {
- pte = address_space_ldq_le(cs->as, pte_addr, attrs, &res);
+ pte = riscv_cpu_data_is_big_endian(env)
+ ? address_space_ldq_be(cs->as, pte_addr, attrs, &res)
+ : address_space_ldq_le(cs->as, pte_addr, attrs, &res);
}
if (res != MEMTX_OK) {
@@ -1566,12 +1570,24 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
if (memory_region_is_ram(mr)) {
target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1);
target_ulong old_pte;
+ bool be = riscv_cpu_data_is_big_endian(env);
if (riscv_cpu_sxl(env) == MXL_RV32) {
- old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), cpu_to_le32(updated_pte));
- old_pte = le32_to_cpu(old_pte);
+ uint32_t cmp = be ? cpu_to_be32(pte)
+ : cpu_to_le32(pte);
+ uint32_t val = be ? cpu_to_be32(updated_pte)
+ : cpu_to_le32(updated_pte);
+ old_pte = qatomic_cmpxchg((uint32_t *)pte_pa,
+ cmp, val);
+ old_pte = be ? be32_to_cpu(old_pte)
+ : le32_to_cpu(old_pte);
} else {
- old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), cpu_to_le64(updated_pte));
- old_pte = le64_to_cpu(old_pte);
+ target_ulong cmp = be ? cpu_to_be64(pte)
+ : cpu_to_le64(pte);
+ target_ulong val = be ? cpu_to_be64(updated_pte)
+ : cpu_to_le64(updated_pte);
+ old_pte = qatomic_cmpxchg(pte_pa, cmp, val);
+ old_pte = be ? be64_to_cpu(old_pte)
+ : le64_to_cpu(old_pte);
}
if (old_pte != pte) {
goto restart;
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 5/7] hw/riscv: Make boot code endianness-aware at runtime
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
` (4 preceding siblings ...)
2026-03-20 15:06 ` [PATCH v4 6/7] target/riscv: Fix page table walk endianness for big-endian harts Djordje Todorovic
@ 2026-03-20 15:06 ` Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback Djordje Todorovic
2026-03-20 15:51 ` [PATCH v4 0/7] Add RISC-V big-endian target support Philippe Mathieu-Daudé
7 siblings, 0 replies; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:06 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
Add riscv_is_big_endian() helper that checks the hart's big-endian CPU
property and use it throughout the boot code:
- ELF loading: pass ELFDATA2MSB or ELFDATA2LSB based on endianness
- Firmware dynamic info: use cpu_to_be* or cpu_to_le* based on endianness
- Reset vector: instructions (entries 0-5) remain always little-endian,
data words (entries 6-9) use target data endianness. For RV64 BE, the
hi/lo word pairs within each dword are swapped since LD reads as BE.
This is part of the runtime big-endian support series which avoids
separate BE binaries by handling endianness as a CPU property.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
---
hw/riscv/boot.c | 83 +++++++++++++++++++++++++++++++++++------
include/hw/riscv/boot.h | 2 +
2 files changed, 73 insertions(+), 12 deletions(-)
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index e5490beda0..fdff2a7ddd 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -40,6 +40,28 @@ bool riscv_is_32bit(RISCVHartArrayState *harts)
return mcc->def->misa_mxl_max == MXL_RV32;
}
+bool riscv_is_big_endian(RISCVHartArrayState *harts)
+{
+ return harts->harts[0].cfg.big_endian;
+}
+
+/*
+ * Convert a pair of 32-bit words forming a 64-bit dword to target data
+ * endianness. For big-endian, the hi/lo word order is swapped since LD
+ * interprets bytes as BE.
+ */
+static void riscv_boot_data_dword(uint32_t *data, bool big_endian)
+{
+ if (big_endian) {
+ uint32_t tmp = data[0];
+ data[0] = cpu_to_be32(data[1]);
+ data[1] = cpu_to_be32(tmp);
+ } else {
+ data[0] = cpu_to_le32(data[0]);
+ data[1] = cpu_to_le32(data[1]);
+ }
+}
+
/*
* Return the per-socket PLIC hart topology configuration string
* (caller must free with g_free())
@@ -72,6 +94,7 @@ void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts)
info->kernel_size = 0;
info->initrd_size = 0;
info->is_32bit = riscv_is_32bit(harts);
+ info->is_big_endian = riscv_is_big_endian(harts);
}
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
@@ -247,8 +270,10 @@ void riscv_load_kernel(MachineState *machine,
*/
kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL,
&info->image_low_addr, &info->image_high_addr,
- NULL, ELFDATA2LSB, EM_RISCV,
- 1, 0, NULL, true, sym_cb);
+ NULL,
+ info->is_big_endian ? ELFDATA2MSB
+ : ELFDATA2LSB,
+ EM_RISCV, 1, 0, NULL, true, sym_cb);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
goto out;
@@ -391,21 +416,32 @@ void riscv_rom_copy_firmware_info(MachineState *machine,
struct fw_dynamic_info64 dinfo64;
void *dinfo_ptr = NULL;
size_t dinfo_len;
+ bool big_endian = riscv_is_big_endian(harts);
if (riscv_is_32bit(harts)) {
- dinfo32.magic = cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE);
- dinfo32.version = cpu_to_le32(FW_DYNAMIC_INFO_VERSION);
- dinfo32.next_mode = cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S);
- dinfo32.next_addr = cpu_to_le32(kernel_entry);
+ dinfo32.magic = big_endian ? cpu_to_be32(FW_DYNAMIC_INFO_MAGIC_VALUE)
+ : cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE);
+ dinfo32.version = big_endian ? cpu_to_be32(FW_DYNAMIC_INFO_VERSION)
+ : cpu_to_le32(FW_DYNAMIC_INFO_VERSION);
+ dinfo32.next_mode = big_endian
+ ? cpu_to_be32(FW_DYNAMIC_INFO_NEXT_MODE_S)
+ : cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S);
+ dinfo32.next_addr = big_endian ? cpu_to_be32(kernel_entry)
+ : cpu_to_le32(kernel_entry);
dinfo32.options = 0;
dinfo32.boot_hart = 0;
dinfo_ptr = &dinfo32;
dinfo_len = sizeof(dinfo32);
} else {
- dinfo64.magic = cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE);
- dinfo64.version = cpu_to_le64(FW_DYNAMIC_INFO_VERSION);
- dinfo64.next_mode = cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S);
- dinfo64.next_addr = cpu_to_le64(kernel_entry);
+ dinfo64.magic = big_endian ? cpu_to_be64(FW_DYNAMIC_INFO_MAGIC_VALUE)
+ : cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE);
+ dinfo64.version = big_endian ? cpu_to_be64(FW_DYNAMIC_INFO_VERSION)
+ : cpu_to_le64(FW_DYNAMIC_INFO_VERSION);
+ dinfo64.next_mode = big_endian
+ ? cpu_to_be64(FW_DYNAMIC_INFO_NEXT_MODE_S)
+ : cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S);
+ dinfo64.next_addr = big_endian ? cpu_to_be64(kernel_entry)
+ : cpu_to_le64(kernel_entry);
dinfo64.options = 0;
dinfo64.boot_hart = 0;
dinfo_ptr = &dinfo64;
@@ -474,10 +510,33 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */
}
- /* copy in the reset vector in little_endian byte order */
- for (i = 0; i < ARRAY_SIZE(reset_vec); i++) {
+ /* RISC-V instructions are always little-endian */
+ for (i = 0; i < 6; i++) {
reset_vec[i] = cpu_to_le32(reset_vec[i]);
}
+
+ /*
+ * Data words (addresses at entries 6-9) must match the firmware's data
+ * endianness.
+ */
+ if (riscv_is_32bit(harts)) {
+ for (i = 6; i < ARRAY_SIZE(reset_vec); i++) {
+ if (riscv_is_big_endian(harts)) {
+ reset_vec[i] = cpu_to_be32(reset_vec[i]);
+ } else {
+ reset_vec[i] = cpu_to_le32(reset_vec[i]);
+ }
+ }
+ } else {
+ /*
+ * For RV64, each pair of 32-bit words forms a dword. For big-endian,
+ * the hi/lo word order within each dword must be swapped since LD
+ * interprets bytes as BE.
+ */
+ for (i = 6; i < ARRAY_SIZE(reset_vec); i += 2) {
+ riscv_boot_data_dword(reset_vec + i, riscv_is_big_endian(harts));
+ }
+ }
rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
rom_base, &address_space_memory);
riscv_rom_copy_firmware_info(machine, harts,
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index f00b3ca122..a54c2b397d 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -36,9 +36,11 @@ typedef struct RISCVBootInfo {
ssize_t initrd_size;
bool is_32bit;
+ bool is_big_endian;
} RISCVBootInfo;
bool riscv_is_32bit(RISCVHartArrayState *harts);
+bool riscv_is_big_endian(RISCVHartArrayState *harts);
char *riscv_plic_hart_config_string(int hart_count);
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
` (5 preceding siblings ...)
2026-03-20 15:06 ` [PATCH v4 5/7] hw/riscv: Make boot code endianness-aware at runtime Djordje Todorovic
@ 2026-03-20 15:06 ` Djordje Todorovic
2026-03-26 3:23 ` Richard Henderson
2026-03-20 15:51 ` [PATCH v4 0/7] Add RISC-V big-endian target support Philippe Mathieu-Daudé
7 siblings, 1 reply; 11+ messages in thread
From: Djordje Todorovic @ 2026-03-20 15:06 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com,
Djordje Todorovic
Implement the .virtio_is_big_endian SysemuCPUOps callback for RISC-V.
This makes cpu_virtio_is_big_endian() return the correct endianness
based on the hart's MSTATUS MBE/SBE/UBE bits, which is used by
virtio_current_cpu_endian() during guest-initiated device resets.
For bi-endian RISC-V targets, this ensures legacy virtio devices
correctly detect the guest's runtime data endianness, matching
how ARM handles its bi-endian support.
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
---
target/riscv/cpu.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index eed5afd27e..f813acf579 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -32,6 +32,7 @@
#include "migration/vmstate.h"
#include "fpu/softfloat-helpers.h"
#include "system/device_tree.h"
+#include "system/hw_accel.h"
#include "system/kvm.h"
#include "system/tcg.h"
#include "kvm/kvm_riscv.h"
@@ -2713,11 +2714,21 @@ static int64_t riscv_get_arch_id(CPUState *cs)
#include "hw/core/sysemu-cpu-ops.h"
+static bool riscv_cpu_virtio_is_big_endian(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ cpu_synchronize_state(cs);
+ return riscv_cpu_data_is_big_endian(env);
+}
+
static const struct SysemuCPUOps riscv_sysemu_ops = {
.has_work = riscv_cpu_has_work,
.get_phys_page_debug = riscv_cpu_get_phys_page_debug,
.write_elf64_note = riscv_cpu_write_elf64_note,
.write_elf32_note = riscv_cpu_write_elf32_note,
+ .internal_is_big_endian = riscv_cpu_virtio_is_big_endian,
.legacy_vmsd = &vmstate_riscv_cpu,
};
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v4 0/7] Add RISC-V big-endian target support
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
` (6 preceding siblings ...)
2026-03-20 15:06 ` [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback Djordje Todorovic
@ 2026-03-20 15:51 ` Philippe Mathieu-Daudé
7 siblings, 0 replies; 11+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-03-20 15:51 UTC (permalink / raw)
To: Djordje Todorovic, qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
alistair23@gmail.com, thuth@redhat.com
Hi,
On 20/3/26 16:05, Djordje Todorovic wrote:
> - Addressed comments from v3
> - Rebased on top of master
> - Then rebased on top of patch set by Philippe
> "target/riscv: Forbid to use legacy native endianness API"
>
> Djordje Todorovic (7):
> target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks
> target/riscv: Add big-endian CPU property
> target/riscv: Set endianness MSTATUS bits at CPU reset
> target/riscv: Implement runtime data endianness via MSTATUS bits
> hw/riscv: Make boot code endianness-aware at runtime
> target/riscv: Fix page table walk endianness for big-endian harts
> target/riscv: Support runtime endianness in virtio via sysemu callback
Thanks for this v4. What guest do you use to test it? Would it be
possible to add a functional test, so we can check for regressions?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits
2026-03-20 15:06 ` [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits Djordje Todorovic
@ 2026-03-26 3:22 ` Richard Henderson
0 siblings, 0 replies; 11+ messages in thread
From: Richard Henderson @ 2026-03-26 3:22 UTC (permalink / raw)
To: Djordje Todorovic, qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com
On 3/21/26 01:06, Djordje Todorovic wrote:
> +/*
> + * 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
> +}
This ifdef means ...
> --- 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
... you don't need this one.
> 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;
Better to store MemOp here.
> } 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;
> }
... so that this expression is only computed once ...
>
> #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;
... here.
r~
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback
2026-03-20 15:06 ` [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback Djordje Todorovic
@ 2026-03-26 3:23 ` Richard Henderson
0 siblings, 0 replies; 11+ messages in thread
From: Richard Henderson @ 2026-03-26 3:23 UTC (permalink / raw)
To: Djordje Todorovic, qemu-devel@nongnu.org
Cc: qemu-riscv@nongnu.org, cfu@mips.com, mst@redhat.com,
marcel.apfelbaum@gmail.com, dbarboza@ventanamicro.com,
philmd@linaro.org, alistair23@gmail.com, thuth@redhat.com
On 3/21/26 01:06, Djordje Todorovic wrote:
> Implement the .virtio_is_big_endian SysemuCPUOps callback for RISC-V.
> This makes cpu_virtio_is_big_endian() return the correct endianness
> based on the hart's MSTATUS MBE/SBE/UBE bits, which is used by
> virtio_current_cpu_endian() during guest-initiated device resets.
>
> For bi-endian RISC-V targets, this ensures legacy virtio devices
> correctly detect the guest's runtime data endianness, matching
> how ARM handles its bi-endian support.
>
> Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
> ---
> target/riscv/cpu.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
This is only for legacy virtio.
RISC-V does not have legacy big-endian, therefore this should not exist.
r~
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-03-26 3:24 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-20 15:05 [PATCH v4 0/7] Add RISC-V big-endian target support Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 2/7] target/riscv: Add big-endian CPU property Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 1/7] target/riscv: Define MSTATUS_SBE and MSTATUS_MBE bit masks Djordje Todorovic
2026-03-20 15:05 ` [PATCH v4 3/7] target/riscv: Set endianness MSTATUS bits at CPU reset Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 4/7] target/riscv: Implement runtime data endianness via MSTATUS bits Djordje Todorovic
2026-03-26 3:22 ` Richard Henderson
2026-03-20 15:06 ` [PATCH v4 6/7] target/riscv: Fix page table walk endianness for big-endian harts Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 5/7] hw/riscv: Make boot code endianness-aware at runtime Djordje Todorovic
2026-03-20 15:06 ` [PATCH v4 7/7] target/riscv: Support runtime endianness in virtio via sysemu callback Djordje Todorovic
2026-03-26 3:23 ` Richard Henderson
2026-03-20 15:51 ` [PATCH v4 0/7] Add RISC-V big-endian target support Philippe Mathieu-Daudé
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox