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


Reply via email to