On Tue, 2026-09-15 at 10:49 +0800, Zephyr Li wrote:
> 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]>
Thanks!
Applied to riscv-to-apply.next
Alistair
> ---
> 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