[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