.../riscv/tcg/insn_trans/trans_rvzawrs.c.inc | 2 + tests/tcg/riscv64/Makefile.softmmu-target | 4 + tests/tcg/riscv64/test-zawrs-stval.S | 87 +++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 tests/tcg/riscv64/test-zawrs-stval.S
helper_wrs_nto() may raise an illegal or virtual-instruction exception.
However, trans_wrs_nto() does not save the decoded opcode before calling
the helper. As a result, exception unwinding leaves env->bins stale and
trap handling reports an incorrect value in tval.
Call decode_save_opc() before the helper so the faulting instruction
encoding is restored for trap handling.
Add a TCG test that delegates the mstatus.TW-triggered illegal-instruction
exception to S-mode and checks scause, sepc and stval.
Fixes: b62e0ce76098 ("target/riscv: Raise exceptions on wrs.nto")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4080
Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
---
.../riscv/tcg/insn_trans/trans_rvzawrs.c.inc | 2 +
tests/tcg/riscv64/Makefile.softmmu-target | 4 +
tests/tcg/riscv64/test-zawrs-stval.S | 87 +++++++++++++++++++
3 files changed, 93 insertions(+)
create mode 100644 tests/tcg/riscv64/test-zawrs-stval.S
diff --git a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
index 0eef033..ecebec0 100644
--- a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
+++ b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
@@ -54,6 +54,8 @@ static bool trans_wrs_nto(DisasContext *ctx, arg_wrs_nto *a)
* exception, as handled by the wrs.nto helper.
*/
#ifndef CONFIG_USER_ONLY
+ /* Save the opcode in case the helper raises an exception. */
+ decode_save_opc(ctx, 0);
gen_helper_wrs_nto(tcg_env);
#endif
diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index 6a219c3..979df2c 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -20,6 +20,10 @@ EXTRA_RUNS += run-issue1060
run-issue1060: issue1060
$(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
+EXTRA_RUNS += run-test-zawrs-stval
+run-test-zawrs-stval: test-zawrs-stval
+ $(call run-test, $<, $(QEMU) -cpu max $(QEMU_OPTS)$<)
+
EXTRA_RUNS += run-test-mepc-masking
run-test-mepc-masking: test-mepc-masking
$(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
diff --git a/tests/tcg/riscv64/test-zawrs-stval.S b/tests/tcg/riscv64/test-zawrs-stval.S
new file mode 100644
index 0000000..42a9901
--- /dev/null
+++ b/tests/tcg/riscv64/test-zawrs-stval.S
@@ -0,0 +1,87 @@
+/*
+ * Test that WRS.NTO writes the instruction encoding to stval when
+ * mstatus.TW causes an illegal-instruction exception in S-mode.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ .option norvc
+
+ .text
+ .global _start
+_start:
+ /* Unexpected traps to M-mode fail the test. */
+ lla t0, machine_trap
+ csrw mtvec, t0
+
+ /* Delegate illegal-instruction exceptions to S-mode. */
+ li t0, 1 << 2
+ csrs medeleg, t0
+
+ lla t0, supervisor_trap
+ csrw stvec, t0
+
+ /* Give S-mode read, write and execute access to all memory. */
+ li t0, -1
+ csrw pmpaddr0, t0
+ li t0, 0xf
+ csrw pmpcfg0, t0
+
+ /* Set mstatus.TW and select S-mode for mret. */
+ li t0, 3 << 11
+ csrc mstatus, t0
+ li t0, (1 << 21) | (1 << 11)
+ csrs mstatus, t0
+
+ lla t0, supervisor_start
+ csrw mepc, t0
+ mret
+
+supervisor_start:
+ /* WRS.NTO must trap because mstatus.TW is set. */
+ li a0, 1
+wrs_nto:
+ .word 0x00d00073
+ j _exit
+
+machine_trap:
+ li a0, 2
+ j _exit
+
+supervisor_trap:
+ li a0, 3
+
+ csrr t0, scause
+ li t1, 2
+ bne t0, t1, _exit
+
+ csrr t0, sepc
+ lla t1, wrs_nto
+ bne t0, t1, _exit
+
+ csrr t1, stval
+ lwu t2, 0(t0)
+ bne t1, t2, _exit
+ li t2, 0x00d00073
+ bne t1, t2, _exit
+
+ li a0, 0
+
+_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
On Tue, 2026-09-01 at 17:16 +0800, Zephyr Li wrote:
> helper_wrs_nto() may raise an illegal or virtual-instruction
> exception.
> However, trans_wrs_nto() does not save the decoded opcode before
> calling
> the helper. As a result, exception unwinding leaves env->bins stale
> and
> trap handling reports an incorrect value in tval.
>
> Call decode_save_opc() before the helper so the faulting instruction
> encoding is restored for trap handling.
>
> Add a TCG test that delegates the mstatus.TW-triggered illegal-
> instruction
> exception to S-mode and checks scause, sepc and stval.
>
> Fixes: b62e0ce76098 ("target/riscv: Raise exceptions on wrs.nto")
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4080
> Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
Thanks!
Applied to riscv-to-apply.next
Alistair
> ---
> .../riscv/tcg/insn_trans/trans_rvzawrs.c.inc | 2 +
> tests/tcg/riscv64/Makefile.softmmu-target | 4 +
> tests/tcg/riscv64/test-zawrs-stval.S | 87
> +++++++++++++++++++
> 3 files changed, 93 insertions(+)
> create mode 100644 tests/tcg/riscv64/test-zawrs-stval.S
>
> diff --git a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> index 0eef033..ecebec0 100644
> --- a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> +++ b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> @@ -54,6 +54,8 @@ static bool trans_wrs_nto(DisasContext *ctx,
> arg_wrs_nto *a)
> * exception, as handled by the wrs.nto helper.
> */
> #ifndef CONFIG_USER_ONLY
> + /* Save the opcode in case the helper raises an exception. */
> + decode_save_opc(ctx, 0);
> gen_helper_wrs_nto(tcg_env);
> #endif
>
> diff --git a/tests/tcg/riscv64/Makefile.softmmu-target
> b/tests/tcg/riscv64/Makefile.softmmu-target
> index 6a219c3..979df2c 100644
> --- a/tests/tcg/riscv64/Makefile.softmmu-target
> +++ b/tests/tcg/riscv64/Makefile.softmmu-target
> @@ -20,6 +20,10 @@ EXTRA_RUNS += run-issue1060
> run-issue1060: issue1060
> $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
>
> +EXTRA_RUNS += run-test-zawrs-stval
> +run-test-zawrs-stval: test-zawrs-stval
> + $(call run-test, $<, $(QEMU) -cpu max $(QEMU_OPTS)$<)
> +
> EXTRA_RUNS += run-test-mepc-masking
> run-test-mepc-masking: test-mepc-masking
> $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
> diff --git a/tests/tcg/riscv64/test-zawrs-stval.S
> b/tests/tcg/riscv64/test-zawrs-stval.S
> new file mode 100644
> index 0000000..42a9901
> --- /dev/null
> +++ b/tests/tcg/riscv64/test-zawrs-stval.S
> @@ -0,0 +1,87 @@
> +/*
> + * Test that WRS.NTO writes the instruction encoding to stval when
> + * mstatus.TW causes an illegal-instruction exception in S-mode.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> + .option norvc
> +
> + .text
> + .global _start
> +_start:
> + /* Unexpected traps to M-mode fail the test. */
> + lla t0, machine_trap
> + csrw mtvec, t0
> +
> + /* Delegate illegal-instruction exceptions to S-mode. */
> + li t0, 1 << 2
> + csrs medeleg, t0
> +
> + lla t0, supervisor_trap
> + csrw stvec, t0
> +
> + /* Give S-mode read, write and execute access to all memory.
> */
> + li t0, -1
> + csrw pmpaddr0, t0
> + li t0, 0xf
> + csrw pmpcfg0, t0
> +
> + /* Set mstatus.TW and select S-mode for mret. */
> + li t0, 3 << 11
> + csrc mstatus, t0
> + li t0, (1 << 21) | (1 << 11)
> + csrs mstatus, t0
> +
> + lla t0, supervisor_start
> + csrw mepc, t0
> + mret
> +
> +supervisor_start:
> + /* WRS.NTO must trap because mstatus.TW is set. */
> + li a0, 1
> +wrs_nto:
> + .word 0x00d00073
> + j _exit
> +
> +machine_trap:
> + li a0, 2
> + j _exit
> +
> +supervisor_trap:
> + li a0, 3
> +
> + csrr t0, scause
> + li t1, 2
> + bne t0, t1, _exit
> +
> + csrr t0, sepc
> + lla t1, wrs_nto
> + bne t0, t1, _exit
> +
> + csrr t1, stval
> + lwu t2, 0(t0)
> + bne t1, t2, _exit
> + li t2, 0x00d00073
> + bne t1, t2, _exit
> +
> + li a0, 0
> +
> +_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
On Tue, 2026-09-01 at 17:16 +0800, Zephyr Li wrote:
> helper_wrs_nto() may raise an illegal or virtual-instruction
> exception.
> However, trans_wrs_nto() does not save the decoded opcode before
> calling
> the helper. As a result, exception unwinding leaves env->bins stale
> and
> trap handling reports an incorrect value in tval.
>
> Call decode_save_opc() before the helper so the faulting instruction
> encoding is restored for trap handling.
>
> Add a TCG test that delegates the mstatus.TW-triggered illegal-
> instruction
> exception to S-mode and checks scause, sepc and stval.
>
> Fixes: b62e0ce76098 ("target/riscv: Raise exceptions on wrs.nto")
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4080
> Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> .../riscv/tcg/insn_trans/trans_rvzawrs.c.inc | 2 +
> tests/tcg/riscv64/Makefile.softmmu-target | 4 +
> tests/tcg/riscv64/test-zawrs-stval.S | 87
> +++++++++++++++++++
> 3 files changed, 93 insertions(+)
> create mode 100644 tests/tcg/riscv64/test-zawrs-stval.S
>
> diff --git a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> index 0eef033..ecebec0 100644
> --- a/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> +++ b/target/riscv/tcg/insn_trans/trans_rvzawrs.c.inc
> @@ -54,6 +54,8 @@ static bool trans_wrs_nto(DisasContext *ctx,
> arg_wrs_nto *a)
> * exception, as handled by the wrs.nto helper.
> */
> #ifndef CONFIG_USER_ONLY
> + /* Save the opcode in case the helper raises an exception. */
> + decode_save_opc(ctx, 0);
> gen_helper_wrs_nto(tcg_env);
> #endif
>
> diff --git a/tests/tcg/riscv64/Makefile.softmmu-target
> b/tests/tcg/riscv64/Makefile.softmmu-target
> index 6a219c3..979df2c 100644
> --- a/tests/tcg/riscv64/Makefile.softmmu-target
> +++ b/tests/tcg/riscv64/Makefile.softmmu-target
> @@ -20,6 +20,10 @@ EXTRA_RUNS += run-issue1060
> run-issue1060: issue1060
> $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
>
> +EXTRA_RUNS += run-test-zawrs-stval
> +run-test-zawrs-stval: test-zawrs-stval
> + $(call run-test, $<, $(QEMU) -cpu max $(QEMU_OPTS)$<)
> +
> EXTRA_RUNS += run-test-mepc-masking
> run-test-mepc-masking: test-mepc-masking
> $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
> diff --git a/tests/tcg/riscv64/test-zawrs-stval.S
> b/tests/tcg/riscv64/test-zawrs-stval.S
> new file mode 100644
> index 0000000..42a9901
> --- /dev/null
> +++ b/tests/tcg/riscv64/test-zawrs-stval.S
> @@ -0,0 +1,87 @@
> +/*
> + * Test that WRS.NTO writes the instruction encoding to stval when
> + * mstatus.TW causes an illegal-instruction exception in S-mode.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> + .option norvc
> +
> + .text
> + .global _start
> +_start:
> + /* Unexpected traps to M-mode fail the test. */
> + lla t0, machine_trap
> + csrw mtvec, t0
> +
> + /* Delegate illegal-instruction exceptions to S-mode. */
> + li t0, 1 << 2
> + csrs medeleg, t0
> +
> + lla t0, supervisor_trap
> + csrw stvec, t0
> +
> + /* Give S-mode read, write and execute access to all memory.
> */
> + li t0, -1
> + csrw pmpaddr0, t0
> + li t0, 0xf
> + csrw pmpcfg0, t0
> +
> + /* Set mstatus.TW and select S-mode for mret. */
> + li t0, 3 << 11
> + csrc mstatus, t0
> + li t0, (1 << 21) | (1 << 11)
> + csrs mstatus, t0
> +
> + lla t0, supervisor_start
> + csrw mepc, t0
> + mret
> +
> +supervisor_start:
> + /* WRS.NTO must trap because mstatus.TW is set. */
> + li a0, 1
> +wrs_nto:
> + .word 0x00d00073
> + j _exit
> +
> +machine_trap:
> + li a0, 2
> + j _exit
> +
> +supervisor_trap:
> + li a0, 3
> +
> + csrr t0, scause
> + li t1, 2
> + bne t0, t1, _exit
> +
> + csrr t0, sepc
> + lla t1, wrs_nto
> + bne t0, t1, _exit
> +
> + csrr t1, stval
> + lwu t2, 0(t0)
> + bne t1, t2, _exit
> + li t2, 0x00d00073
> + bne t1, t2, _exit
> +
> + li a0, 0
> +
> +_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
© 2016 - 2026 Red Hat, Inc.