[PATCH v8 11/11] target/riscv: Add system test for Smmpt34

LIU Zhiwei <[email protected]> Fri, 24 Jul 2026 04:03:25 +0800
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Add SMMPT system-test coverage for Smmpt34, the RV32-only memory
protection scheme (2-level MPT with 4-byte entries).

Smmpt34 requires a 32-bit CPU, but qemu-system-riscv64 can run a 32-bit
CPU (-cpu rv32), so the test lives in tests/tcg/riscv64 alongside the
RV64 Smmpt tests and shares the same harness (smmpt-common.S). It only
differs in the table geometry: 4-byte MPTEs, two radix levels, a 10-bit
level-0 index and the RV32 mmpt CSR layout (MODE in bits[31:30]). It
exercises the same permission and structural checks as the RV64 tests
(all XWR encodings for load and store, V=0 and reserved-bit leaves, and
NAPOT leaves with a valid and a reserved G field), using the same access
addresses. It is built for RV32 and run on qemu-system-riscv64 with
-cpu rv32.

Signed-off-by: LIU Zhiwei <[email protected]>
---
 tests/tcg/riscv64/test-smmpt34.S | 97 ++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 tests/tcg/riscv64/test-smmpt34.S

diff --git a/tests/tcg/riscv64/test-smmpt34.S b/tests/tcg/riscv64/test-smmpt34.S
new file mode 100644
index 0000000000..92c8320999
--- /dev/null
+++ b/tests/tcg/riscv64/test-smmpt34.S
@@ -0,0 +1,97 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Test for the Smmpt34 (Supervisor Memory Protection Table) extension,
+ * per SMMTT specification v0.4.9.  Smmpt34 is the RV32-only scheme.
+ *
+ * Although Smmpt34 requires a 32-bit CPU, the program is run on
+ * qemu-system-riscv64 with a 32-bit CPU (-cpu rv32), which lives in the
+ * same target as the RV64 Smmpt tests.  It is therefore kept here in
+ * tests/tcg/riscv64 alongside the other Smmpt tests and shares the same
+ * harness (smmpt-common.S).  Build it for RV32 and run it as:
+ *
+ *   $CC -march=rv32ima_zicsr -mabi=ilp32 -I tests/tcg/riscv64 \
+ *       tests/tcg/riscv64/test-smmpt34.S -Wa,--noexecstack -c -o smmpt34.o
+ *   $LD -m elf32lriscv -T tests/tcg/riscv64/semihost.ld smmpt34.o -o smmpt34
+ *   qemu-system-riscv64 -cpu rv32,x-smmpt=true -M virt -display none \
+ *       -semihosting -device loader,file=smmpt34
+ *
+ * Smmpt34 uses a 2-level MPT with 4-byte entries (8 pages per leaf,
+ * pi = SPA[14:12]).  The supervisor physical address is partitioned as
+ * pn[1] = SPA[33:25] (9-bit root index), pn[0] = SPA[24:15] (10-bit level-0
+ * index) and range offset SPA[14:0].  For the RAM addresses used here
+ * (0x8xxx_xxxx, < 4 GiB) pn[1] resolves to 64.  See smmpt-common.S for the
+ * MPRV trick, the shared address layout and the exit-code convention.
+ *
+ * This test reuses the same shared harness and the same access addresses as
+ * the RV64 tests; only the table geometry (4-byte entries, 2 levels, 10-bit
+ * level-0 index) and the RV32 mmpt layout differ.
+ *
+ * MPT layout (Smmpt34, 2 levels, 4-byte entries):
+ *
+ *   L1 (root) @ 0x8040_0000 : entry[64]  -> non-leaf, PPN(L0)=0x80410
+ *   L0        @ 0x8041_0000 :
+ *     entry[160] -> non-NAPOT leaf (LEAF_ALL_XWR)  covering 0x8050_0000+
+ *     entry[162] -> V=0 (invalid)                  covering 0x8051_0000
+ *     entry[164] -> leaf with a reserved bit set    covering 0x8052_0000
+ *     entry[192] -> NAPOT leaf, G=6, XWR=RW         covering 0x8060_0000
+ *     entry[256] -> NAPOT leaf, reserved G=0        covering 0x8080_0000
+ *
+ *   mmpt = MODE(1=Smmpt34)<<30 | PPN(0x80400)
+ */
+
+#include "smmpt-common.S"
+
+	.option	norvc
+
+	.text
+	.global	_start
+_start:
+	/* Install the M-mode trap handler */
+	lla	t0, mtrap
+	csrw	mtvec, t0
+	csrw	medeleg, zero
+
+	/* PMP entry 0: NAPOT covering the whole space, RWX. */
+	li	t0, -1
+	csrw	pmpaddr0, t0
+	li	t0, 0x1f
+	csrw	pmpcfg0, t0
+
+	/* Build the MPT tables (M-mode stores bypass the MPT). */
+	/* L1[64] = non-leaf -> L0 (PPN 0x80410): (0x80410 << 10) | V */
+	li	t0, 0x80400000
+	li	t1, 0x20104001
+	sw	t1, 0x100(t0)		/* 64 * 4 = 0x100 */
+
+	li	t0, 0x80410000
+	/* L0[160] = non-NAPOT leaf carrying all XWR encodings (pi0..pi7) */
+	li	t1, LEAF_ALL_XWR
+	sw	t1, 0x280(t0)		/* 160 * 4 = 0x280 */
+	/* L0[162] = 0: invalid entry (V=0) */
+	sw	x0, 0x288(t0)		/* 162 * 4 = 0x288 */
+	/* L0[164] = leaf with reserved bit 3 set (V|L|rsv|XWR[pi0]=RW) */
+	li	t1, 0x30B
+	sw	t1, 0x290(t0)		/* 164 * 4 = 0x290 */
+	/* L0[192] = NAPOT leaf: V|L|N | XWR=RW(0x300) | G=6(0x6000) */
+	li	t1, 0x6307
+	sw	t1, 0x300(t0)		/* 192 * 4 = 0x300 */
+	/* L0[256] = NAPOT leaf with reserved G=0: V|L|N | XWR=RW | G=0 */
+	li	t1, 0x307
+	sw	t1, 0x400(t0)		/* 256 * 4 = 0x400 */
+
+	/* Program mmpt: MODE=1 (Smmpt34) at bits[31:30], PPN = 0x80400 */
+	li	t0, 0x40080400
+	csrw	0x382, t0		/* CSR_MMPT */
+
+	/* mfence.pa must not fault in M-mode */
+	TEST_MFENCE_PA 1
+
+	/* Run the shared permission and structural checks */
+	RUN_LEAF_CHECKS
+
+	/* All tests passed */
+	li	a0, 0
+	j	_exit
+
+	SMMPT_HARNESS
-- 
2.43.0