[PATCH 6/7] platform: virt: add QEMU virt WorldGuard hwiso tests

Raymond Mao raymondmaoca at gmail.com
Tue May 19 13:33:30 PDT 2026


From: Raymond Mao <raymond.mao at riscstar.com>

Add QEMU virt WorldGuard mechanism-specific tests for boot-time
checker programming and runtime CSR state, and verify the expected
root / domain at 0 / domain at 1 behavior through the HWISO test callback
API.

Signed-off-by: Raymond Mao <raymond.mao at riscstar.com>
---
 platform/generic/objects.mk                   |   1 +
 .../generic/virt/qemu_virt_wgchecker_test.c   | 321 ++++++++++++++++++
 2 files changed, 322 insertions(+)
 create mode 100644 platform/generic/virt/qemu_virt_wgchecker_test.c

diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 80bd65ea..be58873a 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -23,6 +23,7 @@ platform-objs-y += platform_override_modules.o
 platform-objs-y += worldguard.o
 platform-objs-y += wgchecker2.o
 platform-objs-y += virt/qemu_virt_worldguard.o
+platform-objs-$(CONFIG_SBIUNIT) += virt/qemu_virt_wgchecker_test.o
 
 # Blobs to build
 FW_TEXT_START=0x80000000
diff --git a/platform/generic/virt/qemu_virt_wgchecker_test.c b/platform/generic/virt/qemu_virt_wgchecker_test.c
new file mode 100644
index 00000000..0eeaa3a0
--- /dev/null
+++ b/platform/generic/virt/qemu_virt_wgchecker_test.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao at riscstar.com>
+ */
+#include <libfdt.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hwiso.h>
+#include <sbi/sbi_hwiso_test.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_unit_test.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <wgchecker2.h>
+#include <worldguard.h>
+
+struct qemu_virt_wg_slot_expect {
+	bool check_addr;
+	u64 addr;
+	u64 perm;
+	u32 cfg;
+};
+
+struct qemu_virt_wg_checker_expect {
+	const char *path;
+	u32 slot_count;
+	struct qemu_virt_wg_slot_expect *slots;
+	struct qemu_virt_wg_slot_expect last_slot;
+};
+
+struct qemu_virt_wg_expect {
+	u32 wid;
+	u32 widlist_mask;
+	bool check_slwid;
+	u32 slwid;
+};
+
+static u64 qemu_virt_wg_mmio_read64(unsigned long addr)
+{
+#if __riscv_xlen != 32
+	return readq((void *)addr);
+#else
+	return readl((void *)addr) | ((u64)readl((void *)(addr + 4)) << 32);
+#endif
+}
+
+static struct qemu_virt_wg_slot_expect qemu_virt_wg_dram_slots[] = {
+	{
+		.check_addr = true,
+		.addr = 0x20000000ULL,
+		.perm = 0,
+		.cfg = WGCHECKER2_SLOT_CFG_A_OFF,
+	},
+	{
+		.check_addr = true,
+		.addr = 0x30000000ULL,
+		.perm = 0xcf,
+		.cfg = WGCHECKER2_SLOT_CFG_A_TOR,
+	},
+	{
+		.check_addr = true,
+		.addr = 0x30400000ULL,
+		.perm = 0xcc,
+		.cfg = WGCHECKER2_SLOT_CFG_A_TOR,
+	},
+	{
+		.check_addr = true,
+		.addr = 0x40000000ULL,
+		.perm = 0xcf,
+		.cfg = WGCHECKER2_SLOT_CFG_A_TOR,
+	},
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+};
+
+static struct qemu_virt_wg_slot_expect qemu_virt_wg_zero_slots[] = {
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+	{ .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 },
+};
+
+static const struct qemu_virt_wg_checker_expect qemu_virt_wg_checker_expects[] = {
+	{
+		.path = "/soc/wgchecker at 6000000",
+		.slot_count = 16,
+		.slots = qemu_virt_wg_dram_slots,
+		.last_slot = { .perm = 0, .cfg = 0 },
+	},
+	{
+		.path = "/soc/wgchecker at 6001000",
+		.slot_count = 16,
+		.slots = qemu_virt_wg_zero_slots,
+		.last_slot = { .perm = 0xc3, .cfg = WGCHECKER2_SLOT_CFG_A_TOR },
+	},
+	{
+		.path = "/soc/wgchecker at 6002000",
+		.slot_count = 1,
+		.slots = NULL,
+		.last_slot = { .perm = 0xc0, .cfg = WGCHECKER2_SLOT_CFG_A_TOR },
+	},
+};
+
+static const struct qemu_virt_wg_expect qemu_virt_wg_root_expect = {
+	.wid = 3,
+	.widlist_mask = 0,
+	.check_slwid = false,
+};
+
+static const struct qemu_virt_wg_expect qemu_virt_wg_domain0_expect = {
+	.wid = 0,
+	.widlist_mask = 0xb,
+	.check_slwid = true,
+	.slwid = 0,
+};
+
+static const struct qemu_virt_wg_expect qemu_virt_wg_domain1_expect = {
+	.wid = 1,
+	.widlist_mask = 0xa,
+	.check_slwid = true,
+	.slwid = 1,
+};
+
+static struct sbi_domain *qemu_virt_wg_find_domain(const char *name)
+{
+	u32 i;
+	struct sbi_domain *dom;
+
+	sbi_domain_for_each(i, dom) {
+		if (!sbi_strcmp(dom->name, name))
+			return dom;
+	}
+
+	return NULL;
+}
+
+static const struct qemu_virt_wg_expect *
+qemu_virt_wg_domain_expect(const struct sbi_domain *dom)
+{
+	if (dom == &root)
+		return &qemu_virt_wg_root_expect;
+	if (dom && !sbi_strcmp(dom->name, "root"))
+		return &qemu_virt_wg_root_expect;
+	if (!dom)
+		return NULL;
+	if (!sbi_strcmp(dom->name, "domain at 0"))
+		return &qemu_virt_wg_domain0_expect;
+	if (!sbi_strcmp(dom->name, "domain at 1"))
+		return &qemu_virt_wg_domain1_expect;
+
+	return NULL;
+}
+
+static void qemu_virt_wg_assert_checker(struct sbiunit_test_case *test,
+					const struct qemu_virt_wg_checker_expect *expect)
+{
+	void *fdt = fdt_get_address();
+	u64 base, size, addr, perm;
+	u32 cfg, slot;
+	int node, rc;
+
+	node = fdt_path_offset(fdt, expect->path);
+	SBIUNIT_ASSERT(test, node >= 0);
+
+	rc = fdt_get_node_addr_size(fdt, node, 0, &base, &size);
+	SBIUNIT_ASSERT_EQ(test, rc, 0);
+	(void)size;
+
+	SBIUNIT_ASSERT_EQ(test,
+				  readl((void *)(unsigned long)
+					(base + WGCHECKER2_MMIO_NSLOTS)),
+			  expect->slot_count);
+	SBIUNIT_ASSERT_EQ(test,
+				  qemu_virt_wg_mmio_read64(base + WGCHECKER2_MMIO_ERRCAUSE),
+			  0UL);
+	SBIUNIT_ASSERT_EQ(test,
+				  qemu_virt_wg_mmio_read64(base + WGCHECKER2_MMIO_ERRADDR),
+			  0UL);
+
+	for (slot = 1; slot < expect->slot_count; slot++) {
+		addr = qemu_virt_wg_mmio_read64(
+				base + WGCHECKER2_MMIO_SLOT_BASE +
+				slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+				WGCHECKER2_MMIO_SLOT_ADDR);
+		perm = qemu_virt_wg_mmio_read64(
+				base + WGCHECKER2_MMIO_SLOT_BASE +
+				slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+				WGCHECKER2_MMIO_SLOT_PERM);
+		cfg = readl((void *)(unsigned long)
+				    (base + WGCHECKER2_MMIO_SLOT_BASE +
+				     slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+				     WGCHECKER2_MMIO_SLOT_CFG)) &
+			      WGCHECKER2_SLOT_CFG_A_MASK;
+
+		if (expect->slots[slot - 1].check_addr)
+			SBIUNIT_ASSERT_EQ(test, addr, expect->slots[slot - 1].addr);
+		SBIUNIT_ASSERT_EQ(test, perm, expect->slots[slot - 1].perm);
+		SBIUNIT_ASSERT_EQ(test, cfg, expect->slots[slot - 1].cfg);
+	}
+
+	perm = qemu_virt_wg_mmio_read64(
+			base + WGCHECKER2_MMIO_SLOT_BASE +
+			expect->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+			WGCHECKER2_MMIO_SLOT_PERM);
+	cfg = readl((void *)(unsigned long)
+		    (base + WGCHECKER2_MMIO_SLOT_BASE +
+		     expect->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+		     WGCHECKER2_MMIO_SLOT_CFG)) &
+		      WGCHECKER2_SLOT_CFG_A_MASK;
+	SBIUNIT_ASSERT_EQ(test, perm, expect->last_slot.perm);
+	SBIUNIT_ASSERT_EQ(test, cfg, expect->last_slot.cfg);
+}
+
+static void qemu_virt_wg_assert_state(struct sbiunit_test_case *test,
+				      const struct sbi_domain *dom, void *ctx)
+{
+	struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+	const struct qemu_virt_wg_expect *expect;
+
+	(void)ctx;
+
+	if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG))
+		return;
+	expect = qemu_virt_wg_domain_expect(dom);
+	if (!expect)
+		return;
+
+	SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MLWID), expect->wid);
+
+	if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG))
+		return;
+
+	SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MWIDDELEG), expect->widlist_mask);
+	if (!expect->check_slwid)
+		return;
+
+	SBIUNIT_ASSERT_EQ(test, csr_read(CSR_SLWID), expect->slwid);
+}
+
+static void qemu_virt_wg_assert_quiesced(struct sbiunit_test_case *test,
+					 const struct sbi_domain *dom, void *ctx)
+{
+	struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+
+	(void)dom;
+	(void)ctx;
+
+	if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG))
+		return;
+
+	SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MLWID), qemu_virt_wg_root_expect.wid);
+
+	if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG))
+		return;
+
+	SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MWIDDELEG), 0UL);
+}
+
+static void qemu_virt_wg_boot_test(struct sbiunit_test_case *test)
+{
+	struct sbi_domain *dom0, *dom1;
+	u32 i;
+
+	for (i = 0;
+	     i < (sizeof(qemu_virt_wg_checker_expects) /
+		  sizeof(qemu_virt_wg_checker_expects[0]));
+	     i++)
+		qemu_virt_wg_assert_checker(test, &qemu_virt_wg_checker_expects[i]);
+
+	SBIUNIT_ASSERT_EQ(test, worldguard_test_check_runtime_state(true), 0);
+	SBIUNIT_ASSERT_EQ(test, wgchecker2_checker_count(), 3);
+
+	dom0 = qemu_virt_wg_find_domain("domain at 0");
+	dom1 = qemu_virt_wg_find_domain("domain at 1");
+	SBIUNIT_ASSERT_NE(test, dom0, NULL);
+	SBIUNIT_ASSERT_NE(test, dom1, NULL);
+	SBIUNIT_ASSERT_EQ(test,
+			  worldguard_test_check_domain_state(
+				dom0, true, qemu_virt_wg_domain0_expect.wid,
+				qemu_virt_wg_domain0_expect.widlist_mask),
+			  0);
+	SBIUNIT_ASSERT_EQ(test,
+			  worldguard_test_check_domain_state(
+				dom1, true, qemu_virt_wg_domain1_expect.wid,
+				qemu_virt_wg_domain1_expect.widlist_mask),
+			  0);
+	SBIUNIT_ASSERT_EQ(test,
+			  worldguard_test_check_domain_state(&root, false, 0, 0),
+			  0);
+}
+
+const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops = {
+	.boot_test = qemu_virt_wg_boot_test,
+	.domain_state_test = qemu_virt_wg_assert_state,
+	.domain_quiesce_test = qemu_virt_wg_assert_quiesced,
+};
-- 
2.25.1




More information about the opensbi mailing list