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: 8+ 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-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.