[PATCH] target/riscv: report WRS.NTO opcode in tval on illegal instruction

Zephyr Li posted 1 patch 3 weeks, 4 days ago
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Chao Liu <chao.liu@processmission.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
[PATCH] target/riscv: report WRS.NTO opcode in tval on illegal instruction
Posted by Zephyr Li 3 weeks, 4 days ago
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
Re: [PATCH] target/riscv: report WRS.NTO opcode in tval on illegal instruction
Posted by Alistair Francis 4 days, 14 hours ago
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
Re: [PATCH] target/riscv: report WRS.NTO opcode in tval on illegal instruction
Posted by Alistair Francis 4 days, 14 hours ago
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