All of lore.kernel.org
 help / color / mirror / Atom feed
From: Max Chou <max.chou@sifive.com>
To: qemu-devel@nongnu.org, qemu-riscv@nongnu.org,
	richard.henderson@linaro.org
Cc: 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>,
	Max Chou <max.chou@sifive.com>
Subject: [PATCH 4/6] tests/tcg/riscv64: Add vector unit-stride PMP test
Date: Wed,  9 Sep 2026 16:41:51 +0800	[thread overview]
Message-ID: <20260909084154.223529-5-max.chou@sifive.com> (raw)
In-Reply-To: <20260909084154.223529-1-max.chou@sifive.com>

Add a bare-metal test for locked NA4 PMP permissions lying inside
unit-stride access ranges. The test covers unmasked and masked reads
and writes, and checks that an interior deny is reported with the
exact faulting mtval and vstart rather than merely trapping somewhere
in the range.

Signed-off-by: Max Chou <max.chou@sifive.com>
---
 tests/tcg/riscv64/Makefile.softmmu-target |   7 +-
 tests/tcg/riscv64/test-rvv-ldst-us-pmp.S  | 281 ++++++++++++++++++++++
 2 files changed, 286 insertions(+), 2 deletions(-)
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-us-pmp.S

diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index 0fdf242f735..9e0e0490753 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -86,17 +86,20 @@ run-test-vle32ff: test-vle32ff
 test-vle32ff: CFLAGS += -march=rv64gcv
 
 RVV_LDST_MARCH = -march=rv64gcv
-RVV_LDST_TESTS = test-rvv-ldst-ff-pmp
+RVV_LDST_TESTS = test-rvv-ldst-ff-pmp test-rvv-ldst-us-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
+EXTRA_RUNS += run-test-rvv-ldst-ff-pmp run-test-rvv-ldst-us-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)$<)
 
+run-test-rvv-ldst-us-pmp: test-rvv-ldst-us-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)$<)
+
 # We don't currently support the multiarch system tests
 undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-rvv-ldst-us-pmp.S b/tests/tcg/riscv64/test-rvv-ldst-us-pmp.S
new file mode 100644
index 00000000000..521667628da
--- /dev/null
+++ b/tests/tcg/riscv64/test-rvv-ldst-us-pmp.S
@@ -0,0 +1,281 @@
+/*
+ * RISC-V vector unit-stride with PMP tests
+ *
+ * Runs with rvv_ta_all_1s=true and rvv_ma_all_1s=true so that with a
+ * "ta, ma" vtype QEMU fills every masked-off and tail element with
+ * 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 word 1 of buf_a
+ *   pmp1: NA4   buf_b+8,   L, ---   deny word 2 of buf_b
+ *   pmp2: NA4   buf_c+4,   L, R--   word 1 of buf_c readable, no write
+ *   pmp3: NAPOT test page, L, RW    lower-priority page allow
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+    #include "rvv-ldst.inc"
+
+    /* Assert the word at \sym+\off (readable) still equals \expected. */
+    .macro  CHECK_WORD sym, off, expected
+    lla     t1, \sym
+    lw      t0, \off(t1)
+    li      t6, \expected
+    bne     t0, t6, fail
+    .endm
+
+    .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: any mtval accepted)
+     *   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
+    li      s5, -1
+
+    /* 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 + 8
+    srli    t0, t0, 2
+    csrw    pmpaddr1, t0
+    lla     t0, buf_c + 4
+    srli    t0, t0, 2
+    csrw    pmpaddr2, t0
+    lla     t0, pmp_page
+    srli    t0, t0, 2
+    ori     t0, t0, 0x1ff
+    csrw    pmpaddr3, t0
+    li      t0, 0x000000009b919090
+    csrw    pmpcfg0, t0
+
+    /*
+     * Case 1: sanity: the NA4 read deny traps a scalar load.
+     */
+    CASE    1
+    li      s0, 5
+    li      s1, -1
+    lla     t1, buf_a
+    lw      t0, 4(t1)
+    CHECK_TRAP
+
+    /*
+     * Case 2: sanity: the read-only NA4 word traps a scalar store and
+     * still reads back, proving stores can be verified via loads.
+     */
+    CASE    2
+    li          s0, 7
+    li          s1, -1
+    lla         t1, buf_c
+    sw          t1, 4(t1)
+    CHECK_TRAP
+    CHECK_WORD  buf_c, 4, 0x00cc0001
+
+    /*
+     * Case 3: unmasked vle32.v, vl 3, read deny strictly inside the
+     * range at buf_a+4 (element 1): element 1 is active, so the access
+     * must raise a load access fault.  A probe that samples only the range
+     * endpoints sees just the page allow and loads the denied word silently.
+     */
+    CASE    3
+    PREFILL
+    li          s0, 5
+    li          s1, -1
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_a
+    vle32.v     v2, (a0)
+    CHECK_TRAP
+
+    /*
+     * Case 4: unmasked vle32.v with the denied word first in the
+     * range: element 0 faults and vstart is zero.
+     */
+    CASE    4
+    PREFILL
+    li          s0, 5
+    li          s1, -1
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_a + 4
+    vle32.v     v2, (a0)
+    CHECK_TRAP
+    bnez        s3, fail
+
+    /*
+     * Case 5: unmasked vle32.v with the denied word last in the range
+     * at buf_b+8 (element 2): must trap.
+     */
+    CASE    5
+    PREFILL
+    li          s0, 5
+    li          s1, -1
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_b
+    vle32.v     v2, (a0)
+    CHECK_TRAP
+
+    /*
+     * Case 6: unmasked vse32.v, vl 3, write deny strictly inside the
+     * range at buf_c+4 (element 1): the protected word must remain
+     * unmodified.  Element 0 must have been stored and element 2 must
+     * not, so that a "store every element whose own probe succeeds"
+     * implementation is rejected rather than passing on the protected
+     * word alone.
+     */
+    CASE    6
+    vsetivli    zero, 4, e32, m1, ta, ma
+    lla         a0, st_data_a
+    vle32.v     v4, (a0)
+    li          s0, 7
+    li          s1, -1
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_c
+    vse32.v     v4, (a0)
+    CHECK_TRAP
+    CHECK_WORD  buf_c, 0, 0x11111111
+    CHECK_WORD  buf_c, 4, 0x00cc0001
+    CHECK_WORD  buf_c, 8, 0x00cc0002
+
+    /*
+     * Case 7: masked vle32.v with the read deny only under masked-off
+     * element 1: no access is performed there, so no trap and the
+     * mask-agnostic all-1s fill applies.
+     */
+    CASE    7
+    PREFILL
+    SET_MASK    0b101
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_a
+    vle32.v     v2, (a0), v0.t
+    CHECK_NO_TRAP
+    CHECK_VELEM v2, 0, 0x00aa0000
+    CHECK_VELEM v2, 1, -1
+    CHECK_VELEM v2, 2, 0x00aa0002
+    CHECK_VELEM v2, 3, -1
+
+    /*
+     * Case 8: masked vse32.v with the write deny only under masked-off
+     * element 1: no trap, elements 0 and 2 stored, the protected word
+     * untouched.
+     */
+    CASE    8
+    vsetivli    zero, 4, e32, m1, ta, ma
+    lla         a0, st_data_b
+    vle32.v     v4, (a0)
+    SET_MASK    0b101
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_c
+    vse32.v     v4, (a0), v0.t
+    CHECK_NO_TRAP
+    CHECK_WORD  buf_c, 0, 0x44444444
+    CHECK_WORD  buf_c, 4, 0x00cc0001
+    CHECK_WORD  buf_c, 8, 0x66666666
+
+    /*
+     * Case 9: like case 3 (read deny at element 1 of 3), the mtval should be
+     * the denied word's own address and vstart should be 1, not the range
+     * base and 0.
+     */
+    CASE    9
+    PREFILL
+    li          s0, 5
+    lla         s1, buf_a + 4
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_a
+    vle32.v     v2, (a0)
+    CHECK_TRAP
+    ASSERT_EQ   s3, 1
+
+    /*
+     * Case 10: like case 5 (read deny at element 2, the last of 3),
+     * confirming precise reporting also holds when the denied element
+     * is not the first one probed.
+     */
+    CASE    10
+    PREFILL
+    li          s0, 5
+    lla         s1, buf_b + 8
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_b
+    vle32.v     v2, (a0)
+    CHECK_TRAP
+    ASSERT_EQ   s3, 2
+
+    /*
+     * Case 11: like case 6 (write deny at element 1 of 3), on the
+     * store side.  buf_c+8 still holds the 0x66666666 that case 8
+     * stored, so checking it here proves element 2 was not written
+     * after the trap.
+     */
+    CASE    11
+    vsetivli    zero, 4, e32, m1, ta, ma
+    lla         a0, st_data_a
+    vle32.v     v4, (a0)
+    li          s0, 7
+    lla         s1, buf_c + 4
+    vsetivli    zero, 3, e32, m1, ta, ma
+    lla         a0, buf_c
+    vse32.v     v4, (a0)
+    CHECK_TRAP
+    ASSERT_EQ   s3, 1
+    CHECK_WORD  buf_c, 0, 0x11111111
+    CHECK_WORD  buf_c, 4, 0x00cc0001
+    CHECK_WORD  buf_c, 8, 0x66666666
+
+    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
+
+    /* Store source data; outside every PMP region. */
+st_data_a: .word    0x11111111, 0x22222222, 0x33333333, 0x77777777
+st_data_b: .word    0x44444444, 0x55555555, 0x66666666, 0x88888888
+
+    /* One dedicated page governed by the locked NAPOT RW entry. */
+    .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



  parent reply	other threads:[~2026-09-09  8:44 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
2026-09-09  8:41 ` Max Chou [this message]
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=20260909084154.223529-5-max.chou@sifive.com \
    --to=max.chou@sifive.com \
    --cc=alistair.francis@wdc.com \
    --cc=chao.liu@processmission.com \
    --cc=daniel.barboza@oss.qualcomm.com \
    --cc=liwei1518@gmail.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.