All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] tests/tcg/riscv64: Add system mode rvv ld/st tests
@ 2026-09-09  8:41 Max Chou
  2026-09-09  8:41 ` [PATCH 1/6] target/riscv: Match PMP entries lying inside the checked range Max Chou
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

Hi Richard,

This patchset adds several rvv load/store tests with PMP permission
settings, as we discussed during your v1 submission.

While building the tests, I noticed that pmp_hart_has_privs has a scope
issue that yields unexpected results. This affects both the vector
fault-only-first load and vector unit-stride ld/st instructions.
I therefore attempted to correct pmp_hart_has_privs and
vext_ldst_us_notail, adding matching tests.

Hope these system mode tests and fixes can help you.

PS: I tried applying v2
<20260821040404.268123-1-richard.henderson@linaro.org> to the master and
riscv-to-apply.next branches, but both conflicted.

Thanks,
rnax

Based-on: <20260821040404.268123-1-richard.henderson@linaro.org>

Max Chou (6):
  target/riscv: Match PMP entries lying inside the checked range
  target/riscv: rvv: Probe unit-stride accesses by the first element
  tests/tcg/riscv64: Add vector masked fault-only-first PMP test
  tests/tcg/riscv64: Add vector unit-stride PMP test
  tests/tcg/riscv64: Add vector fault-only-first page probe test
  tests/tcg/riscv64: Add vector segment PMP region spanning test

 target/riscv/tcg/pmp.c                    |  31 +--
 target/riscv/tcg/vector_helper.c          |  45 +++-
 tests/tcg/riscv64/Makefile.softmmu-target |  38 +++
 tests/tcg/riscv64/rvv-ldst.inc            |  91 +++++++
 tests/tcg/riscv64/test-rvv-ldst-ff-page.S |  83 +++++++
 tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S  | 259 ++++++++++++++++++++
 tests/tcg/riscv64/test-rvv-ldst-seg-pmp.S | 141 +++++++++++
 tests/tcg/riscv64/test-rvv-ldst-us-pmp.S  | 281 ++++++++++++++++++++++
 8 files changed, 943 insertions(+), 26 deletions(-)
 create mode 100644 tests/tcg/riscv64/rvv-ldst.inc
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-ff-page.S
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-ff-pmp.S
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-seg-pmp.S
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-us-pmp.S

-- 
2.43.0



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] target/riscv: Match PMP entries lying inside the checked range
  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 ` Max Chou
  2026-09-09  8:41 ` [PATCH 2/6] target/riscv: rvv: Probe unit-stride accesses by the first element Max Chou
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

pmp_hart_has_privs decides the permissions of a byte range by testing
only the two endpoint bytes against each PMP entry. An active entry
lying strictly between the endpoints matches neither byte and is
skipped, so a lower-priority entry silently grants an access that the
higher-priority entry must deny.

Replace the endpoint sampling with interval tests, mirroring the
predicate pmp_get_tlb_size already uses.

Signed-off-by: Max Chou <max.chou@sifive.com>
---
 target/riscv/tcg/pmp.c | 31 ++++++++++---------------------
 1 file changed, 10 insertions(+), 21 deletions(-)

diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c
index 41b55519a8e..94224920d8d 100644
--- a/target/riscv/tcg/pmp.c
+++ b/target/riscv/tcg/pmp.c
@@ -299,20 +299,6 @@ void pmp_update_rule_nums(CPURISCVState *env)
     }
 }
 
-static int pmp_is_in_range(CPURISCVState *env, int pmp_index, hwaddr addr)
-{
-    int result = 0;
-
-    if ((addr >= env->pmp_state.addr[pmp_index].sa) &&
-        (addr <= env->pmp_state.addr[pmp_index].ea)) {
-        result = 1;
-    } else {
-        result = 0;
-    }
-
-    return result;
-}
-
 /*
  * Check if the address has required RWX privs when no PMP entry is matched.
  */
@@ -387,8 +373,8 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
 {
     int i = 0;
     int pmp_size = 0;
-    hwaddr s = 0;
-    hwaddr e = 0;
+    hwaddr last = 0;
+    bool size_known = size != 0;
     uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions;
 
     /* Short cut if no rules */
@@ -414,12 +400,15 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
      * 1.10 draft priv spec states there is an implicit order
      * from low to high
      */
+    last = addr + pmp_size - 1;
+
     for (i = 0; i < pmp_regions; i++) {
-        s = pmp_is_in_range(env, i, addr);
-        e = pmp_is_in_range(env, i, addr + pmp_size - 1);
+        hwaddr sa = env->pmp_state.addr[i].sa;
+        hwaddr ea = env->pmp_state.addr[i].ea;
+        bool contains = (sa <= addr) && (last <= ea);
+        bool overlaps = (addr <= ea) && (sa <= last);
 
-        /* partially inside */
-        if ((s + e) == 1) {
+        if (size_known && overlaps && !contains) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "pmp violation - access is partially inside\n");
             *allowed_privs = 0;
@@ -430,7 +419,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr,
         const uint8_t a_field =
             pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
 
-        if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
+        if (contains && (PMP_AMATCH_OFF != a_field)) {
             /*
              * If the PMP entry is not off and the address is in range,
              * do the priv check
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/6] target/riscv: rvv: Probe unit-stride accesses by the first element
  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 ` Max Chou
  2026-09-09  8:41 ` [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test Max Chou
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

Probe only the first access of the range with probe_access_full and
inspect the resulting lg_page_size: when the page is subdivided, fall
back to the per-element TLB path. Pages with uniform permissions keep
the direct host fast path.

Signed-off-by: Max Chou <max.chou@sifive.com>
---
 target/riscv/tcg/vector_helper.c | 45 ++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c
index efe10156daa..569da995482 100644
--- a/target/riscv/tcg/vector_helper.c
+++ b/target/riscv/tcg/vector_helper.c
@@ -406,6 +406,41 @@ static void vext_test_alignment(CPURISCVState *env, vaddr addr, uint32_t esz,
     }
 }
 
+static void *vext_probe_host_page(CPURISCVState *env, target_ulong addr,
+                                  target_ulong probe_bytes, uint32_t msize,
+                                  MMUAccessType access_type, int mmu_index,
+                                  uintptr_t ra)
+{
+#ifdef CONFIG_USER_ONLY
+    return probe_access(env, addr, probe_bytes, access_type, mmu_index, ra);
+#else
+    CPUTLBEntryFull *full;
+    void *host;
+    int flags;
+
+    flags = probe_access_full(env, addr, MIN(msize, probe_bytes),
+                              access_type, mmu_index, false, &host, &full, ra);
+    if (flags || full->lg_page_size < TARGET_PAGE_BITS) {
+        return NULL;
+    }
+
+    if (access_type == MMU_DATA_STORE) {
+        /*
+         * The permissions are uniform across the page, so probing the
+         * first access has validated the whole range. It has only
+         * marked MIN(msize, probe_bytes) bytes as dirty, though, while
+         * the caller writes probe_bytes through the returned host
+         * pointer. Probe the whole range as well, so that
+         * notdirty_write invalidates every translation block that it
+         * overlaps and the migration dirty bitmap covers all of it.
+         */
+        return probe_access(env, addr, probe_bytes, access_type,
+                            mmu_index, ra);
+    }
+    return host;
+#endif
+}
+
 static void
 vext_ldst_us_notail(void *vd, target_ulong base, CPURISCVState *env,
                     uint32_t log2_esz, uint32_t nf, uint32_t evl,
@@ -448,9 +483,9 @@ vext_ldst_us_notail(void *vd, target_ulong base, CPURISCVState *env,
     page_split = -(addr | TARGET_PAGE_MASK);
 
     /* Validate the first page is accessible. */
-    host = probe_access(env, adjust_addr(env, addr),
-                        MIN(last, last_in_page) - addr + 1,
-                        access_type, mmu_index, ra);
+    host = vext_probe_host_page(env, adjust_addr(env, addr),
+                                MIN(last, last_in_page) - addr + 1,
+                                msize, access_type, mmu_index, ra);
 
     /* Get number of complete elements in the first page. */
     elems = MIN(page_split / msize, evl - i);
@@ -490,8 +525,8 @@ vext_ldst_us_notail(void *vd, target_ulong base, CPURISCVState *env,
     /* Validate the second page is accessible. */
     assert(i < evl);
     elems = evl - i;
-    host = probe_access(env, adjust_addr(env, addr), elems * msize,
-                        access_type, mmu_index, ra);
+    host = vext_probe_host_page(env, adjust_addr(env, addr), elems * msize,
+                                msize, access_type, mmu_index, ra);
 
     if (host) {
         vext_page_ldst_us_host(vd, host, i, evl, nf, log2_esz,
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test
  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 ` Max Chou
  2026-09-11  5:49   ` Chao Liu
  2026-09-09  8:41 ` [PATCH 4/6] tests/tcg/riscv64: Add vector unit-stride " Max Chou
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

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)$<)
+
 # 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



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/6] tests/tcg/riscv64: Add vector unit-stride PMP test
  2026-09-09  8:41 [PATCH 0/6] tests/tcg/riscv64: Add system mode rvv ld/st tests Max Chou
                   ` (2 preceding siblings ...)
  2026-09-09  8:41 ` [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test Max Chou
@ 2026-09-09  8:41 ` 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
  5 siblings, 0 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

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



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/6] tests/tcg/riscv64: Add vector fault-only-first page probe test
  2026-09-09  8:41 [PATCH 0/6] tests/tcg/riscv64: Add system mode rvv ld/st tests Max Chou
                   ` (3 preceding siblings ...)
  2026-09-09  8:41 ` [PATCH 4/6] tests/tcg/riscv64: Add vector unit-stride " Max Chou
@ 2026-09-09  8:41 ` Max Chou
  2026-09-09  8:41 ` [PATCH 6/6] tests/tcg/riscv64: Add vector segment PMP region spanning test Max Chou
  5 siblings, 0 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

Add a bare-metal test for vector fault-only-first loads whose
first-element probe crosses a page boundary. The test covers a body
access that crosses the boundary after the first element, and a first
element that itself straddles the boundary, checking that both cases
report a full vl and vstart of 0 rather than faulting on a mapped page.

Signed-off-by: Max Chou <max.chou@sifive.com>
---
 tests/tcg/riscv64/Makefile.softmmu-target |  9 ++-
 tests/tcg/riscv64/test-rvv-ldst-ff-page.S | 83 +++++++++++++++++++++++
 2 files changed, 90 insertions(+), 2 deletions(-)
 create mode 100644 tests/tcg/riscv64/test-rvv-ldst-ff-page.S

diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index 9e0e0490753..93c4e58e0c2 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -86,14 +86,16 @@ run-test-vle32ff: test-vle32ff
 test-vle32ff: CFLAGS += -march=rv64gcv
 
 RVV_LDST_MARCH = -march=rv64gcv
-RVV_LDST_TESTS = test-rvv-ldst-ff-pmp test-rvv-ldst-us-pmp
+RVV_LDST_TESTS = test-rvv-ldst-ff-pmp test-rvv-ldst-us-pmp \
+	test-rvv-ldst-ff-page
 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-us-pmp
+EXTRA_RUNS += run-test-rvv-ldst-ff-pmp run-test-rvv-ldst-us-pmp \
+	run-test-rvv-ldst-ff-page
 
 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)$<)
@@ -101,5 +103,8 @@ run-test-rvv-ldst-ff-pmp: test-rvv-ldst-ff-pmp
 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)$<)
 
+run-test-rvv-ldst-ff-page: test-rvv-ldst-ff-page
+	$(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)vlen=128$(comma)elen=64$(comma)vext_spec=v1.0 $(QEMU_OPTS)$<)
+
 # We don't currently support the multiarch system tests
 undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-rvv-ldst-ff-page.S b/tests/tcg/riscv64/test-rvv-ldst-ff-page.S
new file mode 100644
index 00000000000..63b1e0ac8a8
--- /dev/null
+++ b/tests/tcg/riscv64/test-rvv-ldst-ff-page.S
@@ -0,0 +1,83 @@
+/*
+ * RISC-V vector fault-only-first probe page-crossing tests
+ *
+ * A first-element probe validates the mapping only for the bytes that
+ * the first (fault-only) element itself touches, then lets the
+ * remaining elements of the vector body fault normally.  An
+ * implementation that instead probes the whole multi-element access
+ * range as one host operation aborts as soon as that range crosses a
+ * page boundary, even though every page involved is in fact mapped.
+ *
+ * Case 1 crosses the page boundary only in the vector body, after the
+ * first element.  Case 2 crosses it inside the first element itself,
+ * so the first-element probe must span both pages it touches.
+ *
+ * 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
+
+    /*
+     * Case 1: the body crosses the page boundary; the first element does not.
+     */
+    CASE    1
+    li              t0, 3
+    vsetvli         t1, t0, e8, m1, tu, mu
+    lla             a0, cross_segment
+    vlseg2e8ff.v    v2, (a0)
+    csrr            t0, vl
+    ASSERT_EQ       t0, 3
+    csrr            t0, vstart
+    ASSERT_EQ       t0, 0
+    lla             a0, output
+    vsseg2e8.v      v2, (a0)
+    lbu             t0, 5(a0)
+    ASSERT_EQ       t0, 0x66
+
+    /*
+     * Case 2: the first segment element itself straddles the page boundary.
+     */
+    CASE    2
+    li              t0, 3
+    vsetvli         t1, t0, e8, m1, tu, mu
+    lla             a0, straddle_segment
+    vlseg2e8ff.v    v2, (a0)
+    csrr            t0, vl
+    ASSERT_EQ       t0, 3
+    csrr            t0, vstart
+    ASSERT_EQ       t0, 0
+    lla             a0, output
+    vsseg2e8.v      v2, (a0)
+    lbu             t0, 5(a0)
+    ASSERT_EQ       t0, 0xf6
+    li              a0, 0
+exit:
+    SEMI_EXIT
+    FAIL
+
+    /*
+     * No trap is expected: a spurious fault from the page-crossing probe
+     * is the very thing under test.  Report the case number rather than
+     * vectoring to the reset value of mtvec and hanging until the
+     * harness timeout.
+     */
+    .balign 4
+trap_handler:
+    j               fail
+
+    .data
+    .balign 16
+semiargs: .space 16
+    .balign 4096
+    .space 4094
+cross_segment: .byte 0x11, 0x22, 0x33, 0x44, 0x55, 0x66
+output: .space 6
+    .balign 4096
+    .space 4095
+straddle_segment: .byte 0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/6] tests/tcg/riscv64: Add vector segment PMP region spanning test
  2026-09-09  8:41 [PATCH 0/6] tests/tcg/riscv64: Add system mode rvv ld/st tests Max Chou
                   ` (4 preceding siblings ...)
  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 ` Max Chou
  5 siblings, 0 replies; 8+ messages in thread
From: Max Chou @ 2026-09-09  8:41 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv, richard.henderson
  Cc: Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Max Chou

A segment load performs nf independent eew-sized accesses, so PMP checks
each field on its own.  Probing the whole nf * eew segment as one range
instead reports a fault whenever a PMP boundary falls inside a segment.

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

diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index 93c4e58e0c2..aea1e7fcb15 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -87,7 +87,8 @@ test-vle32ff: CFLAGS += -march=rv64gcv
 
 RVV_LDST_MARCH = -march=rv64gcv
 RVV_LDST_TESTS = test-rvv-ldst-ff-pmp test-rvv-ldst-us-pmp \
-	test-rvv-ldst-ff-page
+	test-rvv-ldst-ff-page \
+	test-rvv-ldst-seg-pmp
 CLEANFILES += $(RVV_LDST_TESTS)
 
 $(RVV_LDST_TESTS): %: %.S rvv-ldst.inc $(LINK_SCRIPT)
@@ -95,7 +96,8 @@ $(RVV_LDST_TESTS): %: %.S rvv-ldst.inc $(LINK_SCRIPT)
 	$(LD) $(LDFLAGS) $@.o -o $@
 
 EXTRA_RUNS += run-test-rvv-ldst-ff-pmp run-test-rvv-ldst-us-pmp \
-	run-test-rvv-ldst-ff-page
+	run-test-rvv-ldst-ff-page \
+	run-test-rvv-ldst-seg-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)$<)
@@ -106,5 +108,20 @@ run-test-rvv-ldst-us-pmp: test-rvv-ldst-us-pmp
 run-test-rvv-ldst-ff-page: test-rvv-ldst-ff-page
 	$(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)vlen=128$(comma)elen=64$(comma)vext_spec=v1.0 $(QEMU_OPTS)$<)
 
+run-test-rvv-ldst-seg-pmp: test-rvv-ldst-seg-pmp
+	$(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)vlen=128$(comma)elen=64$(comma)vext_spec=v1.0 $(QEMU_OPTS)$<)
+
+PMP_TESTS = test-pmp-interior-entry
+CLEANFILES += $(PMP_TESTS)
+
+$(PMP_TESTS): %: %.S rvv-ldst.inc $(LINK_SCRIPT)
+	$(CC) $(CFLAGS) -march=rv64gc_zicboz $< -Wa,--noexecstack -c -o $@.o
+	$(LD) $(LDFLAGS) $@.o -o $@
+
+EXTRA_RUNS += run-test-pmp-interior-entry
+
+run-test-pmp-interior-entry: test-pmp-interior-entry
+	$(call run-test, $<, $(QEMU) -cpu rv64$(comma)zicboz=true$(comma)cboz_blocksize=64 $(QEMU_OPTS)$<)
+
 # We don't currently support the multiarch system tests
 undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-rvv-ldst-seg-pmp.S b/tests/tcg/riscv64/test-rvv-ldst-seg-pmp.S
new file mode 100644
index 00000000000..9b3703ba338
--- /dev/null
+++ b/tests/tcg/riscv64/test-rvv-ldst-seg-pmp.S
@@ -0,0 +1,141 @@
+/*
+ * RISC-V vector segment load spanning two PMP regions
+ *
+ * A segment load performs nf independent eew-sized accesses, so each
+ * field is checked against PMP on its own. A probe that presents the
+ * whole nf * eew segment as one range instead reports a fault whenever
+ * a PMP boundary falls inside the segment, even though every access the
+ * instruction actually performs is permitted.
+ *
+ * PMP layout (locked entries, lowest number wins; everything outside
+ * the test page is unmatched and so fully accessible from M-mode):
+ *   pmp0: NA4   buf+0,     L, R--   field 0 readable
+ *   pmp1: NA4   buf+4,     L, R--   field 1 readable, adjacent to pmp0
+ *   pmp2: NAPOT test page, L, RW-   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: 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
+
+    /* Program the locked PMP entries; single locking cfg write last. */
+    lla     t0, buf
+    srli    t0, t0, 2
+    csrw    pmpaddr0, t0
+    lla     t0, buf + 4
+    srli    t0, t0, 2
+    csrw    pmpaddr1, t0
+    lla     t0, pmp_page
+    srli    t0, t0, 2
+    ori     t0, t0, 0x1ff
+    csrw    pmpaddr2, t0
+    li      t0, 0x9b9191
+    csrw    pmpcfg0, t0
+
+    /* Case 1: a 4-byte load fully inside pmp0 is granted. */
+    CASE    1
+    lla         t1, buf
+    lw          t0, 0(t1)
+    CHECK_NO_TRAP
+    ASSERT_EQ   t0, 0x00dd0000
+
+    /* Case 2: a 4-byte load fully inside pmp1 is granted. */
+    CASE    2
+    lla         t1, buf
+    lw          t0, 4(t1)
+    CHECK_NO_TRAP
+    ASSERT_EQ   t0, 0x00dd0001
+
+    /*
+     * Case 3: control.  One 8-byte access covers both entries and is
+     * contained by neither, so it must fault however permissive the
+     * two entries are on their own.
+     */
+    CASE    3
+    li          s0, 5
+    lla         s1, buf
+    lla         t1, buf
+    ld          t0, 0(t1)
+    CHECK_TRAP
+
+    /*
+     * Case 4: vlseg2e32.v performs one 4-byte access per field, each
+     * contained by its own entry, so the segment must load.  A probe
+     * covering the whole 8-byte segment reports a load access fault
+     * here instead.
+     */
+    CASE    4
+    PREFILL
+    vsetivli    zero, 1, e32, m1, ta, ma
+    lla         a0, buf
+    vlseg2e32.v v2, (a0)
+    CHECK_NO_TRAP
+    CHECK_VELEM v2, 0, 0x00dd0000
+    CHECK_VELEM v3, 0, 0x00dd0001
+
+    /*
+     * Case 5: the same on the fault-only-first path, which must not
+     * truncate vl either.
+     */
+    CASE    5
+    PREFILL
+    vsetivli    zero, 1, e32, m1, ta, ma
+    lla         a0, buf
+    vlseg2e32ff.v v2, (a0)
+    csrr        t2, vl
+    CHECK_NO_TRAP
+    ASSERT_EQ   t2, 1
+    CHECK_VELEM v2, 0, 0x00dd0000
+    CHECK_VELEM v3, 0, 0x00dd0001
+
+    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 governed by the locked NAPOT RW entry. */
+    .balign 4096
+pmp_page:
+buf:
+    .word   0x00dd0000, 0x00dd0001, 0x00dd0002, 0x00dd0003
+    .skip   4080
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/6] tests/tcg/riscv64: Add vector masked fault-only-first PMP test
  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
  0 siblings, 0 replies; 8+ messages in thread
From: Chao Liu @ 2026-09-11  5:49 UTC (permalink / raw)
  To: Max Chou
  Cc: qemu-devel, qemu-riscv, richard.henderson, Palmer Dabbelt,
	Alistair Francis, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei

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
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-11  5:49 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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

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.