From: Chao Liu <chao.liu@processmission.com>
To: Max Chou <max.chou@sifive.com>
Cc: qemu-devel@nongnu.org, qemu-riscv@nongnu.org,
richard.henderson@linaro.org,
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>
Subject: Re: [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test
Date: Fri, 11 Sep 2026 13:49:23 +0800 [thread overview]
Message-ID: <aqOSwMCX5UsGJA79@MacBook-Pro-4.local> (raw)
In-Reply-To: <20260909084154.223529-4-max.chou@sifive.com>
Hi Max,
On Wed, Sep 09, 2026 at 04:41:50PM +0800, Max Chou wrote:
> Add a bare-metal test for masked vector fault-only-first loads across
> locked NA4 PMP regions inside one page. The test covers masked-off
> elements, a faulting active element 0, and later active faults that
> shorten vl.
>
> Signed-off-by: Max Chou <max.chou@sifive.com>
> ---
> tests/tcg/riscv64/Makefile.softmmu-target | 13 ++
> tests/tcg/riscv64/rvv-ldst.inc | 91 ++++++++
> tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S | 259 ++++++++++++++++++++++
> 3 files changed, 363 insertions(+)
> create mode 100644 tests/tcg/riscv64/rvv-ldst.inc
> create mode 100644 tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S
>
> diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
> index f2c75abd57a..0fdf242f735 100644
> --- a/tests/tcg/riscv64/Makefile.softmmu-target
> +++ b/tests/tcg/riscv64/Makefile.softmmu-target
> @@ -85,5 +85,18 @@ run-test-vle32ff: test-vle32ff
> $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true $(QEMU_OPTS)$<)
> test-vle32ff: CFLAGS += -march=rv64gcv
>
> +RVV_LDST_MARCH = -march=rv64gcv
> +RVV_LDST_TESTS = test-rvv-ldst-ff-pmp
> +CLEANFILES += $(RVV_LDST_TESTS)
> +
> +$(RVV_LDST_TESTS): %: %.S rvv-ldst.inc $(LINK_SCRIPT)
> + $(CC) $(CFLAGS) $(RVV_LDST_MARCH) $< -Wa,--noexecstack -c -o $@.o
> + $(LD) $(LDFLAGS) $@.o -o $@
> +
> +EXTRA_RUNS += run-test-rvv-ldst-ff-pmp
> +
> +run-test-rvv-ldst-ff-pmp: test-rvv-ldst-ff-pmp
> + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)vlen=128$(comma)elen=64$(comma)vext_spec=v1.0$(comma)rvv_ta_all_1s=true$(comma)rvv_ma_all_1s=true $(QEMU_OPTS)$<)
> +
Just a heads-up, TCG tests are moving to Meson build, so this needs to be
based on:
https://lore.kernel.org/qemu-devel/20260818192309.22169-1-pierrick.bouvier@oss.qualcomm.com/
Thanks,
Chao
> # We don't currently support the multiarch system tests
> undefine MULTIARCH_TESTS
> diff --git a/tests/tcg/riscv64/rvv-ldst.inc b/tests/tcg/riscv64/rvv-ldst.inc
> new file mode 100644
> index 00000000000..061f330abef
> --- /dev/null
> +++ b/tests/tcg/riscv64/rvv-ldst.inc
> @@ -0,0 +1,91 @@
> +/*
> + * Common support for bare-metal RVV load/store regressions
> + *
> + * Register contract: these macros use t0, t1, t5 and t6 as scratch and
> + * keep the current case number in s11. ASSERT_EQ and CHECK_VELEM hold
> + * their expected value in t6 across a branch, so a trap handler that can
> + * run in between must leave t6 alone; use t5 and s5 for that instead.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> + .option norelax
> + .option norvc
> +
> + .macro RVV_ENABLE
> + li t0, 0x6600
> + csrs mstatus, t0
> + csrw vcsr, zero
> + .endm
> +
> + .macro ASSERT_EQ actual, expected
> + li t6, \expected
> + bne \actual, t6, fail
> + .endm
> +
> + .macro CASE number
> + li s11, \number
> + .endm
> +
> + .macro SEMI_EXIT
> + lla a1, semiargs
> + li t0, 0x20026
> + sd t0, 0(a1)
> + sd a0, 8(a1)
> + li a0, 0x20
> + .balign 16
> + slli zero, zero, 0x1f
> + ebreak
> + srai zero, zero, 0x7
> + j .
> + .endm
> +
> + .macro FAIL
> +fail:
> + mv a0, s11
> + bnez a0, 1f
> + li a0, 1
> +1:
> + j exit
> + .endm
> +
> + /* Pre-fill selected registers with a sentinel neither data nor 1s. */
> + .macro PREFILL vd=, vl=4, sew=e32, value=0x05050505
> + vsetivli zero, \vl, \sew, m1, ta, ma
> + li t0, \value
> + .ifb \vd
> + vmv.v.x v2, t0
> + vmv.v.x v3, t0
> + .else
> + vmv.v.x \vd, t0
> + .endif
> + .endm
> +
> + /* Set the low mask bits of v0 to \val. */
> + .macro SET_MASK val
> + vsetivli zero, 1, e8, m1, ta, ma
> + li t0, \val
> + vmv.s.x v0, t0
> + .endm
> +
> + /* Assert element \idx of \vsrc (e32) equals \expected. */
> + .macro CHECK_VELEM vsrc, idx, expected
> + vsetivli zero, 4, e32, m1, ta, ma
> + vslidedown.vi v8, \vsrc, \idx
> + vmv.x.s t0, v8
> + li t6, \expected
> + bne t0, t6, fail
> + .endm
> +
> + /* Assert that no trap has been taken since the last check. */
> + .macro CHECK_NO_TRAP
> + bne s2, s4, fail
> + .endm
> +
> + /* Assert that exactly one expected trap has been taken. */
> + .macro CHECK_TRAP
> + addi s4, s4, 1
> + bne s2, s4, fail
> + li s0, 0
> + li s1, 0
> + .endm
> diff --git a/tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S b/tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S
> new file mode 100644
> index 00000000000..80b48965625
> --- /dev/null
> +++ b/tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S
> @@ -0,0 +1,259 @@
> +/*
> + * RISC-V vector masked fault-only-first with PMP tests
> + *
> + * PMP permissions may change at NA4 (4-byte) granularity inside one
> + * target page, matching one e32 element exactly. A masked-off body
> + * element performs no memory access, so a read-denied PMP region under
> + * a masked-off element must not fault.
> + *
> + * Runs with rvv_ta_all_1s=true and rvv_ma_all_1s=true so that with a
> + * "ta, ma" vtype every masked-off and tail element must read back as
> + * all-1s, distinct from the 0x05050505 sentinel and the loaded data.
> + *
> + * PMP layout (locked entries, lowest number wins; everything outside
> + * the test page is unmatched and so fully accessible from M-mode):
> + * pmp0: NA4 buf_a+4, L, --- deny element 1 of buf_a
> + * pmp1: NA4 buf_b+0, L, --- deny element 0 of buf_b
> + * pmp2: NA4 buf_c+8, L, --- deny element 2 of buf_c
> + * pmp3: NAPOT test page, L, R lower-priority page allow
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> + #include "rvv-ldst.inc"
> +
> + .text
> + .global _start
> +_start:
> + RVV_ENABLE
> + lla t0, trap_handler
> + csrw mtvec, t0
> +
> + /*
> + * Trap handler protocol:
> + * s0: expected mcause (0: no trap expected)
> + * s1: expected mtval (-1 accepts any value)
> + * s2: traps taken
> + * s3: vstart seen at last trap
> + * s4: traps expected
> + * s5: mtval seen at last trap
> + */
> + li s0, 0
> + li s1, 0
> + li s2, 0
> + li s3, -1
> + li s4, 0
> +
> + /* Program the locked PMP entries; single locking cfg write last. */
> + lla t0, buf_a + 4
> + srli t0, t0, 2
> + csrw pmpaddr0, t0
> + lla t0, buf_b
> + srli t0, t0, 2
> + csrw pmpaddr1, t0
> + lla t0, buf_c + 8
> + srli t0, t0, 2
> + csrw pmpaddr2, t0
> + lla t0, pmp_page
> + srli t0, t0, 2
> + ori t0, t0, 0x1ff
> + csrw pmpaddr3, t0
> + li t0, 0x99909090
> + csrw pmpcfg0, t0
> +
> + /*
> + * Case 1: sanity: the NA4 deny is in effect for a scalar load.
> + */
> + CASE 1
> + li s0, 5
> + li s1, -1
> + lla t1, buf_a
> + lw t0, 4(t1)
> + CHECK_TRAP
> +
> + /*
> + * Case 2: denied bytes lie only under masked-off element 1: no trap.
> + * QEMU retains vl at 3 for this successful access.
> + */
> + CASE 2
> + PREFILL
> + SET_MASK 0b101
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_a
> + vle32ff.v v2, (a0), v0.t
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 3
> + csrr t0, vstart
> + bnez t0, fail
> + CHECK_VELEM v2, 0, 0x00aa0000
> + CHECK_VELEM v2, 1, -1
> + CHECK_VELEM v2, 2, 0x00aa0002
> + CHECK_VELEM v2, 3, -1
> +
> + /*
> + * Case 3: active element 0 denied: trap, vstart 0.
> + */
> + CASE 3
> + PREFILL
> + li s0, 5
> + li s1, -1
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_b
> + vle32ff.v v2, (a0)
> + CHECK_TRAP
> + bnez s3, fail
> +
> + /*
> + * Case 4: masked-off element 0 over denied bytes: no trap.
> + * retains vl at 3 and loads elements 1 and 2.
> + */
> + CASE 4
> + PREFILL
> + SET_MASK 0b110
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_b
> + vle32ff.v v2, (a0), v0.t
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 3
> + CHECK_VELEM v2, 0, -1
> + CHECK_VELEM v2, 1, 0x00bb0001
> + CHECK_VELEM v2, 2, 0x00bb0002
> + CHECK_VELEM v2, 3, -1
> +
> + /*
> + * Case 5: active element 2 denied, unmasked: no trap, vl 2.
> + */
> + CASE 5
> + PREFILL
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_c
> + vle32ff.v v2, (a0)
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 2
> + CHECK_VELEM v2, 0, 0x00cc0000
> + CHECK_VELEM v2, 1, 0x00cc0001
> + CHECK_VELEM v2, 2, -1
> + CHECK_VELEM v2, 3, -1
> +
> + /*
> + * Case 6: masked-off element 0, active element 2 denied: vl 2.
> + */
> + CASE 6
> + PREFILL
> + SET_MASK 0b110
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_c
> + vle32ff.v v2, (a0), v0.t
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 2
> + CHECK_VELEM v2, 0, -1
> + CHECK_VELEM v2, 1, 0x00cc0001
> + CHECK_VELEM v2, 2, -1
> + CHECK_VELEM v2, 3, -1
> +
> + /*
> + * Case 7: nf=2 segments, masked-off segment 0 covers the denied bytes at
> + * buf_b: no trap. retains vl at 3 and loads segments 1 and 2.
> + */
> + CASE 7
> + PREFILL
> + SET_MASK 0b110
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_b
> + vlseg2e32ff.v v2, (a0), v0.t
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 3
> + CHECK_VELEM v2, 0, -1
> + CHECK_VELEM v3, 0, -1
> + CHECK_VELEM v2, 1, 0x00bb0002
> + CHECK_VELEM v3, 1, 0x00bb0003
> + CHECK_VELEM v2, 2, 0x00bb0004
> + CHECK_VELEM v3, 2, 0x00bb0005
> + CHECK_VELEM v2, 3, -1
> + CHECK_VELEM v3, 3, -1
> +
> + /*
> + * Case 8: nf=2 segments, unmasked, field 0 of segment 1 denied
> + * at buf_c+8: no trap, vl truncates to 1, segment 0 loaded.
> + */
> + CASE 8
> + PREFILL
> + vsetivli zero, 3, e32, m1, ta, ma
> + lla a0, buf_c
> + vlseg2e32ff.v v2, (a0)
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 1
> + CHECK_VELEM v2, 0, 0x00cc0000
> + CHECK_VELEM v3, 0, 0x00cc0001
> + CHECK_VELEM v2, 1, -1
> + CHECK_VELEM v3, 1, -1
> + CHECK_VELEM v2, 2, -1
> + CHECK_VELEM v3, 2, -1
> + CHECK_VELEM v2, 3, -1
> + CHECK_VELEM v3, 3, -1
> +
> + /*
> + * Case 9: the denied masked-off element is the last element of the
> + * accessed range, so a range probe cannot miss it as an interior
> + * region: no trap. retains vl at 2.
> + */
> + CASE 9
> + PREFILL
> + SET_MASK 0b01
> + vsetivli zero, 2, e32, m1, ta, ma
> + lla a0, buf_a
> + vle32ff.v v2, (a0), v0.t
> + CHECK_NO_TRAP
> + csrr t0, vl
> + ASSERT_EQ t0, 2
> + CHECK_VELEM v2, 0, 0x00aa0000
> + CHECK_VELEM v2, 1, -1
> + CHECK_VELEM v2, 2, -1
> + CHECK_VELEM v2, 3, -1
> +
> + li a0, 0
> +exit:
> + SEMI_EXIT
> + FAIL
> +
> + .balign 4
> +trap_handler:
> + csrr t5, mcause
> + bne t5, s0, fail
> + csrr s5, mtval
> + li t5, -1
> + beq s1, t5, 1f
> + bne s5, s1, fail
> +1:
> + csrr s3, vstart
> + addi s2, s2, 1
> + csrw vstart, zero
> + csrr t5, mepc
> + addi t5, t5, 4
> + csrw mepc, t5
> + mret
> +
> + .data
> + .balign 16
> +semiargs: .space 16
> +
> + /* One dedicated page; the locked NAPOT entry grants R only. */
> + .balign 4096
> +pmp_page:
> +buf_a:
> + .word 0x00aa0000, 0x00aa0001, 0x00aa0002, 0x00aa0003
> + .word 0x00aa0004, 0x00aa0005
> + .skip 40
> +buf_b:
> + .word 0x00bb0000, 0x00bb0001, 0x00bb0002, 0x00bb0003
> + .word 0x00bb0004, 0x00bb0005
> + .skip 40
> +buf_c:
> + .word 0x00cc0000, 0x00cc0001, 0x00cc0002, 0x00cc0003
> + .word 0x00cc0004, 0x00cc0005
> + .skip 3944
> --
> 2.43.0
>
next prev parent reply other threads:[~2026-09-11 5:49 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 8:41 [PATCH 0/6] tests/tcg/riscv64: Add system mode rvv ld/st tests Max Chou
2026-09-09 8:41 ` [PATCH 1/6] target/riscv: Match PMP entries lying inside the checked range Max Chou
2026-09-09 8:41 ` [PATCH 2/6] target/riscv: rvv: Probe unit-stride accesses by the first element Max Chou
2026-09-09 8:41 ` [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test Max Chou
2026-09-11 5:49 ` Chao Liu [this message]
2026-09-15 9:34 ` Max Chou
2026-09-09 8:41 ` [PATCH 4/6] tests/tcg/riscv64: Add vector unit-stride " Max Chou
2026-09-09 8:41 ` [PATCH 5/6] tests/tcg/riscv64: Add vector fault-only-first page probe test Max Chou
2026-09-09 8:41 ` [PATCH 6/6] tests/tcg/riscv64: Add vector segment PMP region spanning test Max Chou
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aqOSwMCX5UsGJA79@MacBook-Pro-4.local \
--to=chao.liu@processmission.com \
--cc=alistair.francis@wdc.com \
--cc=daniel.barboza@oss.qualcomm.com \
--cc=liwei1518@gmail.com \
--cc=max.chou@sifive.com \
--cc=palmer@dabbelt.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).