vsepc is the VS-mode counterpart of sepc and is a WARL register, but
unlike mepc and sepc its accessors do not mask the low address bits
according to IALIGN. As a result, values with invalid low bits can be
observed through vsepc.
Apply get_xepc_mask() to both vsepc reads and writes, matching the
existing mepc/sepc handling.
Add a TCG regression test covering both IALIGN=16 and IALIGN=32.
Fixes: 8747c9eeb2aa ("target/riscv: Add Hypervisor virtual CSRs accesses")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4420
Signed-off-by: Zephyr Li <[email protected]>
---
target/riscv/tcg/csr.c | 4 +-
tests/tcg/riscv64/system/meson.build | 11 ++++++
tests/tcg/riscv64/test-vsepc-masking.S | 54 ++++++++++++++++++++++++++
3 files changed, 67 insertions(+), 2 deletions(-)
create mode 100644 tests/tcg/riscv64/test-vsepc-masking.S
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index bd4b6dc114..274c5cfbf4 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -5299,14 +5299,14 @@ static RISCVException write_vsscratch(CPURISCVState
*env, int csrno,
static RISCVException read_vsepc(CPURISCVState *env, int csrno,
target_ulong *val)
{
- *val = env->vsepc;
+ *val = env->vsepc & get_xepc_mask(env);
return RISCV_EXCP_NONE;
}
static RISCVException write_vsepc(CPURISCVState *env, int csrno,
target_ulong val, uintptr_t ra)
{
- env->vsepc = val;
+ env->vsepc = val & get_xepc_mask(env);
return RISCV_EXCP_NONE;
}
diff --git a/tests/tcg/riscv64/system/meson.build
b/tests/tcg/riscv64/system/meson.build
index 8604c2a45a..5d633a266a 100644
--- a/tests/tcg/riscv64/system/meson.build
+++ b/tests/tcg/riscv64/system/meson.build
@@ -24,6 +24,17 @@ tests += {
'test-crc32.S': setup + {'qemu_args': ['-cpu', 'rv64,xlrbr=true',
qemu_args]},
'test-minstret-ecall.S': setup + {'qemu_args': ['-icount', 'shift=1',
qemu_args]},
'test-misa-w.S': setup + {'qemu_args': ['-cpu',
'rv64,x-misa-w=true,c=true,v=true', qemu_args]},
+ 'test-vsepc-masking.S': setup + {
+ 'test_name': 'test-vsepc-masking-c',
+ 'qemu_args': ['-cpu', 'rv64,h=true,c=true', qemu_args],
+ },
+}
+
+tests += {
+ 'test-vsepc-masking.S': {
+ 'test_name': 'test-vsepc-masking-no-c',
+ 'qemu_args': ['-cpu', 'rv64,h=true,c=false,zca=false', qemu_args],
+ },
}
tests += {
diff --git a/tests/tcg/riscv64/test-vsepc-masking.S
b/tests/tcg/riscv64/test-vsepc-masking.S
new file mode 100644
index 0000000000..e5460f8aee
--- /dev/null
+++ b/tests/tcg/riscv64/test-vsepc-masking.S
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#define MISA_C (1 << 2)
+
+ .option norvc
+ .text
+ .global _start
+_start:
+ csrr s0, misa
+ andi s0, s0, MISA_C
+
+ lla s1, test_addr
+ ori t0, s1, 3
+ csrw vsepc, t0
+ csrr t1, vsepc
+
+ beqz s0, ialign32
+ addi t2, s1, 2 /* IALIGN=16: bit 0 reads as zero. */
+ j check
+
+ialign32:
+ /* IALIGN=32: bits [1:0] read as zero. */
+ mv t2, s1
+
+check:
+ li a0, 2
+ bne t1, t2, _exit
+
+ li a0, 0
+ j _exit
+
+ .balign 4
+test_addr:
+ nop
+
+/* Exit code in a0. */
+_exit:
+ lla a1, semiargs
+ li t0, 0x20026 /* ADP_Stopped_ApplicationExit */
+ sd t0, 0(a1)
+ sd a0, 8(a1)
+ li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */
+
+ /* Semihosting call sequence. */
+ .balign 16
+ slli zero, zero, 0x1f
+ ebreak
+ srai zero, zero, 0x7
+ j .
+
+ .data
+ .balign 16
+semiargs:
+ .space 16
--
2.43.0