* [PATCH 1/7] hart: add WorldGuard CSR IDs and hart extension flags
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 2/7] docs: document hwiso WorldGuard DT bindings Raymond Mao
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Define the WorldGuard-related CSR numbers and claim smwg / sswg hart
extensions so platform code can probe support before programming
MLWID, MWIDDELEG and SLWID.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
include/sbi/riscv_encoding.h | 3 +++
include/sbi/sbi_hart.h | 4 ++++
lib/sbi/sbi_hart.c | 2 ++
3 files changed, 9 insertions(+)
diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h
index 46bbeed0..48304132 100644
--- a/include/sbi/riscv_encoding.h
+++ b/include/sbi/riscv_encoding.h
@@ -331,6 +331,7 @@
/* Supervisor Protection and Translation */
#define CSR_SATP 0x180
+#define CSR_SLWID 0x190
/* Supervisor Indirect Register Alias */
#define CSR_SISELECT 0x150
@@ -454,6 +455,7 @@
/* Machine Configuration */
#define CSR_MENVCFG 0x30a
#define CSR_MENVCFGH 0x31a
+#define CSR_MLWID 0x390
/* Machine Trap Handling */
#define CSR_MSCRATCH 0x340
@@ -680,6 +682,7 @@
/* Machine Security Configuration CSR (mseccfg) */
#define CSR_MSECCFG 0x747
#define CSR_MSECCFGH 0x757
+#define CSR_MWIDDELEG 0x748
#define MSECCFG_MML_SHIFT (0)
#define MSECCFG_MML (_UL(1) << MSECCFG_MML_SHIFT)
diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
index cc78eec6..2c725ae2 100644
--- a/include/sbi/sbi_hart.h
+++ b/include/sbi/sbi_hart.h
@@ -63,6 +63,10 @@ enum sbi_hart_extensions {
SBI_HART_EXT_SSCSRIND,
/** Hart has Ssccfg extension */
SBI_HART_EXT_SSCCFG,
+ /** Hart has Smwg extension */
+ SBI_HART_EXT_SMWG,
+ /** Hart has Sswg extension */
+ SBI_HART_EXT_SSWG,
/** Maximum index of Hart extension */
SBI_HART_EXT_MAX,
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index 3d136944..6fc03a59 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -666,6 +666,8 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = {
__SBI_HART_EXT_DATA(sdtrig, SBI_HART_EXT_SDTRIG),
__SBI_HART_EXT_DATA(smcsrind, SBI_HART_EXT_SMCSRIND),
__SBI_HART_EXT_DATA(smcdeleg, SBI_HART_EXT_SMCDELEG),
+ __SBI_HART_EXT_DATA(smwg, SBI_HART_EXT_SMWG),
+ __SBI_HART_EXT_DATA(sswg, SBI_HART_EXT_SSWG),
};
/**
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/7] docs: document hwiso WorldGuard DT bindings
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
2026-05-19 20:33 ` [PATCH 1/7] hart: add WorldGuard CSR IDs and hart extension flags Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 3/7] [NOT-FOR-UPSTREAM] platform: virt: add QEMU WorldGuard hwiso overlay Raymond Mao
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Document the hw-isolation and worldguard_cfg device-tree metadata used
by the HWISO framework for WorldGuard-enabled platforms.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
docs/domain_support.md | 159 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+)
diff --git a/docs/domain_support.md b/docs/domain_support.md
index b34e43aa..d81f1bc6 100644
--- a/docs/domain_support.md
+++ b/docs/domain_support.md
@@ -201,6 +201,165 @@ The DT properties of a domain instance DT node are as follows:
whether the domain instance is allowed to do system reset.
* **system-suspend-allowed** (Optional) - A boolean flag representing
whether the domain instance is allowed to do system suspend.
+* **hw-isolation** (Optional) - A child node acting as a container for
+ system-level hardware isolation mechanisms. Each child node represents a
+ single mechanism configured via its compatible string and properties.
+
+Hardware Isolation Hooks
+------------------------
+
+OpenSBI provides a system-level hardware isolation framework that dispatches
+all registered mechanisms in the following phases:
+
+* **init** - Runs at boot to configure system-level isolation features.
+* **domain_init** - Parses per-domain isolation configuration.
+* **domain_exit** - Runs before switching out of a domain.
+* **domain_enter** - Runs after switching into a domain.
+
+Hardware Isolation Device Tree Binding
+--------------------------------------
+
+The hardware isolation configuration is specified as an optional child node
+named **hw-isolation** under a domain instance node. The **hw-isolation**
+node is a container for one or more mechanism nodes.
+
+The DT properties of a hardware isolation container node are as follows:
+
+* **#address-cells** / **#size-cells** (Optional) - Standard container node
+ properties. They are not interpreted by OpenSBI.
+
+Each hardware isolation mechanism has its own properties and compatible
+string. A mechanism can either use per-domain properties below the domain
+instance node, or parse system-level DT nodes describing isolation hardware.
+
+For the WorldGuard support on QEMU virt, OpenSBI parses the
+following WG-style system nodes:
+
+* **sifive,wgchecker2** - WorldGuard checker node.
+* **reg** - Checker MMIO base/size.
+* **sifive,slot-count** - Number of hardware checker slots.
+* **sifive,subordinates** - List of protected resource phandles owned by the
+ checker.
+* **worldguard_cfg** - Child node of a protected memory or device node
+ describing WorldGuard policy for that resource.
+* **perms** - 64-bit permission bitmap values encoded as **<hi lo>** cell
+ pairs, with either one value for the whole resource or one value per
+ protected range.
+* **reg** - Optional protected address ranges inside a **worldguard_cfg**
+ child. If omitted, the resource node's own **reg** is used. A single
+ subordinate with one **perms** entry and no explicit **worldguard_cfg/reg**
+ is treated as a full-checker rule.
+* **worldguard** - Optional CPU child node compatible with **riscv,wgcpu**
+ providing default WG execution state.
+* **mwid** - Default machine world ID for a hart.
+* **mwidlist** - Valid/delegable world IDs for that hart.
+
+Domain nodes can optionally provide WG execution metadata under the
+**hw-isolation** container:
+
+* **worldguard,wid** - Machine world ID selected when entering the domain.
+* **worldguard,widlist** - World IDs delegated to the domain.
+
+At runtime the WorldGuard implementation uses the hooks as follows:
+
+* **init** - Parses all WG checker nodes, validates the protected ranges, and
+ programs checker MMIO slots at boot when platform checker nodes are
+ present. Runtime WID/WID list support is enabled only when per-CPU WG
+ runtime nodes are present; checker-only DTs do not force runtime
+ switching on.
+* **domain_init** - Parses per-domain **worldguard,wid** and
+ **worldguard,widlist** metadata.
+* **domain_exit** - Quiesces the current hart back to its per-hart default
+ machine WID and clears **MWIDDELEG** before the handoff.
+* **domain_enter** - Reprograms **MLWID**, **MWIDDELEG**, and, when
+ delegation is active, **SLWID** for the destination domain when the hart
+ supports **smwg** / **sswg**.
+
+The CPU **worldguard** defaults are parsed per hart from **/cpus/<cpu>**, so
+platforms may provide different default **mwid** / **mwidlist** values on
+different harts.
+
+Hardware Isolation Examples
+---------------------------
+
+Domain instance with WG execution metadata:
+
+```text
+ chosen {
+ opensbi-domains {
+ compatible = "opensbi,domain,config";
+
+ example_domain: domain@1 {
+ compatible = "opensbi,domain,instance";
+ possible-harts = <&cpu2>;
+ regions = <&mem0 0x3f>;
+ boot-hart = <&cpu2>;
+ next-addr = <0x00000000 0x80200000>;
+ next-mode = <0x1>;
+
+ hw-isolation {
+ worldguard {
+ compatible = "sifive,wgchecker2";
+ worldguard,wid = <1>;
+ worldguard,widlist = <1 3>;
+ };
+ };
+ };
+ };
+ };
+```
+
+WG checker, CPU default state, and protected resource example. These nodes
+remain in the normal system DT topology because they describe isolation
+hardware and protected resources, not OpenSBI domain instances:
+
+```text
+ cpu0: cpu@0 {
+ worldguard {
+ compatible = "riscv,wgcpu";
+ mwid = <0>;
+ mwidlist = <0 1 3>;
+ };
+ };
+
+ flash0: flash@20000000 {
+ reg = <0x0 0x20000000 0x0 0x2000000>;
+ worldguard_cfg {
+ perms = <0x0 0xc3>;
+ };
+ };
+
+ uart0: serial@10000000 {
+ reg = <0x0 0x10000000 0x0 0x100>;
+ worldguard_cfg {
+ perms = <0x0 0xc0>;
+ };
+ };
+
+ memory0: memory@80000000 {
+ reg = <0x0 0x80000000 0x0 0x80000000>;
+ worldguard_cfg {
+ reg = <0x0 0x80000000 0x0 0x40000000
+ 0x0 0xc0000000 0x0 0x01000000
+ 0x0 0xc1000000 0x0 0x3f000000>;
+ perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>;
+ };
+ };
+
+ wgchecker0: wgchecker@10100000 {
+ compatible = "sifive,wgchecker2";
+ reg = <0x0 0x10100000 0x0 0x1000>;
+ sifive,slot-count = <8>;
+ sifive,subordinates = <&memory0 &flash0 &uart0>;
+ };
+```
+
+The test overlay used in this tree is at:
+
+* **platform/generic/virt/qemu-virt-hwiso-overlay.dts**
+
+That overlay only adds per-domain and per-resource metadata. The base DTB
+must still provide the WG checker nodes and per-CPU **worldguard** nodes.
### Assigning HART To Domain Instance
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 3/7] [NOT-FOR-UPSTREAM] platform: virt: add QEMU WorldGuard hwiso overlay
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
2026-05-19 20:33 ` [PATCH 1/7] hart: add WorldGuard CSR IDs and hart extension flags Raymond Mao
2026-05-19 20:33 ` [PATCH 2/7] docs: document hwiso WorldGuard DT bindings Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 4/7] platform: generic: add WorldGuard hwiso support with wgchecker2 Raymond Mao
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Add a QEMU virt device-tree overlay that describes OpenSBI domain
WorldGuard metadata and checker permission policy for the current
HWISO test and demo flow.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
.../generic/virt/qemu-virt-hwiso-overlay.dts | 121 ++++++++++++++++++
1 file changed, 121 insertions(+)
create mode 100644 platform/generic/virt/qemu-virt-hwiso-overlay.dts
diff --git a/platform/generic/virt/qemu-virt-hwiso-overlay.dts b/platform/generic/virt/qemu-virt-hwiso-overlay.dts
new file mode 100644
index 00000000..e44d85b7
--- /dev/null
+++ b/platform/generic/virt/qemu-virt-hwiso-overlay.dts
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/dts-v1/;
+/plugin/;
+
+/*
+ * Test-only overlay for exercising HWISO with WorldGuard metadata.
+ *
+ * This overlay only adds OpenSBI domain metadata and worldguard_cfg resource
+ * policy. The base DTB is expected to already provide the WG checker nodes
+ * and per-CPU worldguard child nodes.
+ *
+ * Usage:
+ * Domain hart phandles are filled in after merge because fdtoverlay does not
+ * reliably resolve CPU-node references against QEMU dumpdtb output here.
+ * See below steps for filling the domain hart phandles (assume the dumped dtb
+ * and merged dtb are represented by 'qemu.dtb' and 'qemu-merged.dtb'
+ * respectively):
+ * cpu0_phandle=$(fdtget -t x qemu.dtb /cpus/cpu@0 phandle)
+ * cpu1_phandle=$(fdtget -t x qemu.dtb /cpus/cpu@1 phandle)
+ * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain@0 \
+ * possible-harts "$cpu0_phandle" "$cpu1_phandle"
+ * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain@0 \
+ * boot-hart "$cpu0_phandle"
+ * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain@1 \
+ * possible-harts "$cpu1_phandle"
+ * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain@1 \
+ * boot-hart "$cpu1_phandle"
+ */
+/ {
+ fragment@0 {
+ target-path = "/chosen";
+ __overlay__ {
+ opensbi-domains {
+ compatible = "opensbi,domain,config";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ memregion0: memregion@0 {
+ compatible = "opensbi,domain,memregion";
+ base = <0x00000000 0x80000000>;
+ order = <0x1f>;
+ };
+
+ guest0: domain@0 {
+ compatible = "opensbi,domain,instance";
+ regions = <&memregion0 0x3f>;
+ next-addr = <0x00000000 0x80200000>;
+ next-arg1 = <0x00000000 0x82200000>;
+ next-mode = <0x1>;
+
+ hw-isolation {
+ worldguard {
+ compatible = "sifive,wgchecker2";
+ worldguard,wid = <0>;
+ worldguard,widlist = <0 1 3>;
+ };
+ };
+ };
+
+ guest1: domain@1 {
+ compatible = "opensbi,domain,instance";
+ regions = <&memregion0 0x3f>;
+ next-addr = <0x00000000 0x80200000>;
+ next-mode = <0x1>;
+
+ hw-isolation {
+ worldguard {
+ compatible = "sifive,wgchecker2";
+ worldguard,wid = <1>;
+ worldguard,widlist = <1 3>;
+ };
+ };
+ };
+ };
+ };
+ };
+
+ fragment@1 {
+ target-path = "/cpus/cpu@0";
+ __overlay__ {
+ opensbi-domain = <&guest0>;
+ };
+ };
+
+ fragment@2 {
+ target-path = "/cpus/cpu@1";
+ __overlay__ {
+ opensbi-domain = <&guest0>;
+ };
+ };
+
+ fragment@3 {
+ target-path = "/memory@80000000";
+ __overlay__ {
+ worldguard_cfg {
+ reg = <0x00000000 0x80000000 0x00000000 0x40000000
+ 0x00000000 0xc0000000 0x00000000 0x01000000
+ 0x00000000 0xc1000000 0x00000000 0x3f000000>;
+ perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>;
+ };
+ };
+ };
+
+ fragment@4 {
+ target-path = "/flash@20000000";
+ __overlay__ {
+ worldguard_cfg {
+ perms = <0x0 0xc3>;
+ };
+ };
+ };
+
+ fragment@5 {
+ target-path = "/soc/serial@10000000";
+ __overlay__ {
+ worldguard_cfg {
+ perms = <0x0 0xc0>;
+ };
+ };
+ };
+};
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 4/7] platform: generic: add WorldGuard hwiso support with wgchecker2
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
` (2 preceding siblings ...)
2026-05-19 20:33 ` [PATCH 3/7] [NOT-FOR-UPSTREAM] platform: virt: add QEMU WorldGuard hwiso overlay Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 5/7] test: add generic hwiso SBI unit coverage Raymond Mao
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Add WorldGuard support for the hardware isolation framework on the
generic platform.
Implement boot-time parsing of sifive,wgchecker2 checker instances and
their subordinate resource permissions from the FDT, then program the
checker MMIO state according to the worldguard_cfg rules described for
each protected resource.
Add WorldGuard runtime support to parse per-hart CPU defaults and
per-domain WorldGuard metadata, and use that state to reprogram MLWID,
MWIDDELEG, and SLWID during domain transitions.
Keep the QEMU virt platform code as the integration layer that
registers the WorldGuard hardware isolation mechanism for the virt
platform.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
platform/generic/include/wgchecker2.h | 55 ++
platform/generic/include/worldguard.h | 45 ++
platform/generic/objects.mk | 3 +
platform/generic/platform.c | 11 +
platform/generic/virt/qemu_virt_worldguard.c | 42 ++
platform/generic/wgchecker2.c | 585 +++++++++++++++++++
platform/generic/worldguard.c | 522 +++++++++++++++++
7 files changed, 1263 insertions(+)
create mode 100644 platform/generic/include/wgchecker2.h
create mode 100644 platform/generic/include/worldguard.h
create mode 100644 platform/generic/virt/qemu_virt_worldguard.c
create mode 100644 platform/generic/wgchecker2.c
create mode 100644 platform/generic/worldguard.c
diff --git a/platform/generic/include/wgchecker2.h b/platform/generic/include/wgchecker2.h
new file mode 100644
index 00000000..37b4bfe0
--- /dev/null
+++ b/platform/generic/include/wgchecker2.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#ifndef __PLATFORM_GENERIC_WGCHECKER2_H__
+#define __PLATFORM_GENERIC_WGCHECKER2_H__
+
+#include <sbi/sbi_types.h>
+
+#define WGCHECKER2_COMPAT "sifive,wgchecker2"
+#define WGCHECKER2_CFG_NODE "worldguard_cfg"
+
+#define WGCHECKER2_PROP_SLOT_COUNT "sifive,slot-count"
+#define WGCHECKER2_PROP_SUBORDINATES "sifive,subordinates"
+#define WGCHECKER2_PROP_PERMS "perms"
+
+/*
+ * The current wgchecker2 model uses a 64-bit permission register with
+ * 2 bits per world, so the current checker model tracks at most 32 WIDs.
+ */
+#define WGCHECKER2_MAX_WIDS 32
+
+/* The current wgchecker2 model requires 4 KiB slot alignment. */
+#define WGCHECKER2_MIN_ALIGN 0x1000ULL
+
+/* Current wgchecker2 MMIO register layout. */
+#define WGCHECKER2_MMIO_NSLOTS 0x008
+#define WGCHECKER2_MMIO_ERRCAUSE 0x010
+#define WGCHECKER2_MMIO_ERRADDR 0x018
+#define WGCHECKER2_MMIO_SLOT_BASE 0x020
+#define WGCHECKER2_MMIO_SLOT_STRIDE 0x020
+#define WGCHECKER2_MMIO_SLOT_ADDR 0x000
+#define WGCHECKER2_MMIO_SLOT_PERM 0x008
+#define WGCHECKER2_MMIO_SLOT_CFG 0x010
+
+/* Current wgchecker2 slot cfg.A[1:0] encoding. */
+#define WGCHECKER2_SLOT_CFG_A_MASK 0x3
+#define WGCHECKER2_SLOT_CFG_A_OFF 0x0
+#define WGCHECKER2_SLOT_CFG_A_TOR 0x1
+
+struct wgchecker2_range {
+ u64 base;
+ u64 size;
+ u64 perm;
+};
+
+u32 wgchecker2_count_platform_checkers(void *fdt);
+int wgchecker2_init(void *fdt);
+void wgchecker2_cleanup(void);
+u32 wgchecker2_checker_count(void);
+
+#endif
diff --git a/platform/generic/include/worldguard.h b/platform/generic/include/worldguard.h
new file mode 100644
index 00000000..ed1e70b8
--- /dev/null
+++ b/platform/generic/include/worldguard.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#ifndef __PLATFORM_GENERIC_WORLDGUARD_H__
+#define __PLATFORM_GENERIC_WORLDGUARD_H__
+
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_hwiso.h>
+#include <sbi/sbi_types.h>
+
+#define WORLDGUARD_CPU_COMPAT "riscv,wgcpu"
+#define WORLDGUARD_CPU_NODE "worldguard"
+
+#define WORLDGUARD_PROP_WID "worldguard,wid"
+#define WORLDGUARD_PROP_WIDLIST "worldguard,widlist"
+#define WORLDGUARD_PROP_MWID "mwid"
+#define WORLDGUARD_PROP_MWIDLIST "mwidlist"
+
+struct wg_cpu_defaults {
+ u32 trusted_wid;
+ u32 nworlds;
+ u32 valid_wid_mask;
+};
+
+struct worldguard_domain_ctx {
+ bool has_wid;
+ u32 wid;
+ u32 widlist_mask;
+};
+
+int worldguard_register(void);
+const struct sbi_hwiso_ops *worldguard_ops_get(void);
+
+#ifdef CONFIG_SBIUNIT
+int worldguard_test_check_runtime_state(bool runtime_enabled);
+int worldguard_test_check_domain_state(const struct sbi_domain *dom,
+ bool expect_ctx, u32 wid,
+ u32 widlist_mask);
+#endif
+
+#endif
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 85aa723a..80bd65ea 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -20,6 +20,9 @@ platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
# Objects to build
platform-objs-y += platform.o
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
# Blobs to build
FW_TEXT_START=0x80000000
diff --git a/platform/generic/platform.c b/platform/generic/platform.c
index b76c2a2f..7326b709 100644
--- a/platform/generic/platform.c
+++ b/platform/generic/platform.c
@@ -11,6 +11,7 @@
#include <platform_override.h>
#include <sbi/riscv_asm.h>
#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_error.h>
#include <sbi/sbi_hartmask.h>
#include <sbi/sbi_heap.h>
#include <sbi/sbi_platform.h>
@@ -30,6 +31,8 @@
#include <sbi_utils/rpxy/fdt_rpxy.h>
#include <sbi_utils/serial/semihosting.h>
+extern int qemu_virt_worldguard_register(void *fdt);
+
/* List of platform override modules generated at compile time */
extern const struct platform_override *platform_override_modules[];
extern unsigned long platform_override_modules_size;
@@ -222,9 +225,17 @@ static int generic_nascent_init(void)
static int generic_early_init(bool cold_boot)
{
+ int rc;
+
if (cold_boot)
fdt_reset_init();
+ if (cold_boot) {
+ rc = qemu_virt_worldguard_register(fdt_get_address());
+ if (rc && rc != SBI_EALREADY)
+ return rc;
+ }
+
if (!generic_plat || !generic_plat->early_init)
return 0;
diff --git a/platform/generic/virt/qemu_virt_worldguard.c b/platform/generic/virt/qemu_virt_worldguard.c
new file mode 100644
index 00000000..b5cdab81
--- /dev/null
+++ b/platform/generic/virt/qemu_virt_worldguard.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * QEMU virt WorldGuard registration shim
+ *
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hwiso_test.h>
+#include <worldguard.h>
+
+#ifdef CONFIG_SBIUNIT
+extern const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops;
+#endif
+
+int qemu_virt_worldguard_register(void *fdt)
+{
+ int rc;
+
+ if (!fdt)
+ return 0;
+
+ if (fdt_node_check_compatible(fdt, 0, "riscv-virtio") &&
+ fdt_node_check_compatible(fdt, 0, "qemu,virt"))
+ return 0;
+
+ rc = worldguard_register();
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_SBIUNIT
+ rc = sbi_hwiso_test_register(worldguard_ops_get(),
+ &qemu_virt_worldguard_test_ops);
+ if (rc && rc != SBI_EALREADY)
+ return rc;
+#endif
+
+ return 0;
+}
diff --git a/platform/generic/wgchecker2.c b/platform/generic/wgchecker2.c
new file mode 100644
index 00000000..bd3fab15
--- /dev/null
+++ b/platform/generic/wgchecker2.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * wgchecker2 model support
+ *
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_string.h>
+#include <wgchecker2.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+struct wgchecker2_checker {
+ char name[32];
+ u64 mmio_base;
+ u64 mmio_size;
+ u32 slot_count;
+ u32 subordinate_count;
+ bool full_checker_rule;
+ u64 full_checker_perm;
+ u32 range_count;
+ struct wgchecker2_range *ranges;
+};
+
+struct wgchecker2_platform_ctx {
+ u32 checker_count;
+ struct wgchecker2_checker *checkers;
+};
+
+static struct wgchecker2_platform_ctx *wgchecker2_platform;
+
+static void wgchecker2_free_platform_ctx(struct wgchecker2_platform_ctx *platform)
+{
+ u32 i;
+
+ if (!platform)
+ return;
+
+ for (i = 0; i < platform->checker_count; i++)
+ sbi_free(platform->checkers[i].ranges);
+
+ sbi_free(platform->checkers);
+ sbi_free(platform);
+}
+
+static u64 wgchecker2_read_cells(const fdt32_t *cells, int count)
+{
+ u64 val = 0;
+ int i;
+
+ for (i = 0; i < count; i++)
+ val = (val << 32) | fdt32_to_cpu(cells[i]);
+
+ return val;
+}
+
+static void wgchecker2_write64(u64 addr, u64 val)
+{
+#if __riscv_xlen != 32
+ writeq(val, (void *)(unsigned long)addr);
+#else
+ writel((u32)val, (void *)(unsigned long)addr);
+ writel((u32)(val >> 32), (void *)(unsigned long)(addr + 4));
+#endif
+}
+
+static void wgchecker2_write32(u64 addr, u32 val)
+{
+ writel(val, (void *)(unsigned long)addr);
+}
+
+static u64 wgchecker2_slot_addr_encode(u64 addr)
+{
+ return addr >> 2;
+}
+
+static bool wgchecker2_range_is_aligned(u64 base, u64 size)
+{
+ if (!size)
+ return false;
+
+ if (base & (WGCHECKER2_MIN_ALIGN - 1))
+ return false;
+ if (size & (WGCHECKER2_MIN_ALIGN - 1))
+ return false;
+
+ return true;
+}
+
+static void wgchecker2_sort_ranges(struct wgchecker2_checker *checker)
+{
+ struct wgchecker2_range tmp;
+ u32 i, j;
+
+ for (i = 1; i < checker->range_count; i++) {
+ tmp = checker->ranges[i];
+ j = i;
+ while (j > 0 && checker->ranges[j - 1].base > tmp.base) {
+ checker->ranges[j] = checker->ranges[j - 1];
+ j--;
+ }
+ checker->ranges[j] = tmp;
+ }
+}
+
+static int wgchecker2_compact_ranges(struct wgchecker2_checker *checker)
+{
+ struct wgchecker2_range *prev, *cur;
+ u64 prev_end, cur_end;
+ u32 i, out = 0;
+
+ if (!checker->range_count)
+ return 0;
+
+ wgchecker2_sort_ranges(checker);
+
+ for (i = 0; i < checker->range_count; i++) {
+ cur = &checker->ranges[i];
+ cur_end = cur->base + cur->size;
+ if (cur_end <= cur->base)
+ return SBI_EINVAL;
+
+ if (!out) {
+ checker->ranges[out++] = *cur;
+ continue;
+ }
+
+ prev = &checker->ranges[out - 1];
+ prev_end = prev->base + prev->size;
+ if (cur->base < prev_end)
+ return SBI_EINVAL;
+
+ if (cur->base == prev_end && cur->perm == prev->perm) {
+ prev->size += cur->size;
+ continue;
+ }
+
+ checker->ranges[out++] = *cur;
+ }
+
+ checker->range_count = out;
+ return 0;
+}
+
+static int wgchecker2_get_reg_cells(void *fdt, int resource_node,
+ int *addr_cells, int *size_cells)
+{
+ int parent;
+
+ parent = fdt_parent_offset(fdt, resource_node);
+ if (parent < 0)
+ return SBI_EINVAL;
+
+ *addr_cells = fdt_address_cells(fdt, parent);
+ *size_cells = fdt_size_cells(fdt, parent);
+ if (*addr_cells <= 0 || *addr_cells > 2 || *size_cells <= 0 ||
+ *size_cells > 2)
+ return SBI_EINVAL;
+
+ return 0;
+}
+
+static int wgchecker2_count_reg_entries(void *fdt, int resource_node,
+ int reg_node)
+{
+ const fdt32_t *reg;
+ int addr_cells, size_cells, entry_cells, len, rc;
+
+ rc = wgchecker2_get_reg_cells(fdt, resource_node, &addr_cells,
+ &size_cells);
+ if (rc)
+ return rc;
+
+ reg = fdt_getprop(fdt, reg_node, "reg", &len);
+ if (!reg || len <= 0)
+ return 0;
+
+ entry_cells = addr_cells + size_cells;
+ if (len % (entry_cells * (int)sizeof(fdt32_t)))
+ return SBI_EINVAL;
+
+ return len / (entry_cells * (int)sizeof(fdt32_t));
+}
+
+static int wgchecker2_parse_perms(void *fdt, int cfg_node, u64 **out_perms,
+ u32 *out_count)
+{
+ const fdt32_t *perms;
+ u64 *vals;
+ int len, i, count;
+
+ *out_perms = NULL;
+ *out_count = 0;
+
+ perms = fdt_getprop(fdt, cfg_node, WGCHECKER2_PROP_PERMS, &len);
+ if (!perms || len <= 0)
+ return 0;
+
+ if (len % (2 * (int)sizeof(fdt32_t)))
+ return SBI_EINVAL;
+
+ count = len / (2 * (int)sizeof(fdt32_t));
+ vals = sbi_calloc(sizeof(*vals), count);
+ if (!vals)
+ return SBI_ENOMEM;
+
+ for (i = 0; i < count; i++, perms += 2)
+ vals[i] = wgchecker2_read_cells(perms, 2);
+
+ *out_perms = vals;
+ *out_count = count;
+ return 0;
+}
+
+static int wgchecker2_fill_ranges(void *fdt, int resource_node, int reg_node,
+ const u64 *perms, u32 perm_count,
+ struct wgchecker2_range *ranges,
+ u32 range_count)
+{
+ const fdt32_t *reg;
+ u64 base, size;
+ int addr_cells, size_cells, entry_cells, len, i, rc;
+
+ rc = wgchecker2_get_reg_cells(fdt, resource_node, &addr_cells,
+ &size_cells);
+ if (rc)
+ return rc;
+
+ reg = fdt_getprop(fdt, reg_node, "reg", &len);
+ if (!reg || len <= 0)
+ return SBI_EINVAL;
+
+ entry_cells = addr_cells + size_cells;
+ for (i = 0; i < (int)range_count; i++, reg += entry_cells) {
+ base = wgchecker2_read_cells(reg, addr_cells);
+ size = wgchecker2_read_cells(reg + addr_cells, size_cells);
+ if (!wgchecker2_range_is_aligned(base, size))
+ return SBI_EINVAL;
+
+ ranges[i].base = base;
+ ranges[i].size = size;
+ ranges[i].perm = perms[(perm_count == 1) ? 0 : i];
+ }
+
+ return 0;
+}
+
+static int wgchecker2_parse_checker_rules(void *fdt, int checker_node,
+ struct wgchecker2_checker *checker)
+{
+ const fdt32_t *subs;
+ u64 *perms = NULL;
+ int cfg_node, len, i, rc = 0, reg_count;
+ u32 perm_count = 0;
+ int child;
+
+ subs = fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES, &len);
+ if (!subs || len <= 0)
+ return 0;
+ if (len % (int)sizeof(fdt32_t))
+ goto err;
+
+ checker->subordinate_count = len / sizeof(fdt32_t);
+ if (!checker->slot_count)
+ goto err;
+
+ checker->ranges = sbi_calloc(sizeof(*checker->ranges),
+ checker->slot_count);
+ if (!checker->ranges)
+ return SBI_ENOMEM;
+
+ for (i = 0; i < checker->subordinate_count; i++) {
+ child = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(subs[i]));
+ if (child < 0) {
+ rc = child;
+ goto err;
+ }
+
+ cfg_node = fdt_subnode_offset(fdt, child, WGCHECKER2_CFG_NODE);
+ if (cfg_node < 0)
+ continue;
+
+ rc = wgchecker2_parse_perms(fdt, cfg_node, &perms, &perm_count);
+ if (rc)
+ goto err;
+ if (!perm_count)
+ continue;
+
+ reg_count = wgchecker2_count_reg_entries(fdt, child, cfg_node);
+ if (reg_count < 0)
+ goto err;
+
+ if (!reg_count && checker->subordinate_count == 1 &&
+ perm_count == 1) {
+ if (checker->range_count)
+ goto err;
+ checker->full_checker_rule = true;
+ checker->full_checker_perm = perms[0];
+ sbi_free(perms);
+ perms = NULL;
+ continue;
+ }
+
+ if (!reg_count)
+ reg_count = wgchecker2_count_reg_entries(fdt, child, child);
+ if (reg_count <= 0)
+ goto err;
+
+ if (perm_count != 1 && perm_count != (u32)reg_count)
+ goto err;
+ if (checker->full_checker_rule)
+ goto err;
+ if (checker->range_count + reg_count > checker->slot_count)
+ goto err;
+
+ rc = wgchecker2_fill_ranges(
+ fdt, child,
+ (fdt_getprop(fdt, cfg_node, "reg", NULL) ? cfg_node : child),
+ perms, perm_count,
+ &checker->ranges[checker->range_count], reg_count);
+ sbi_free(perms);
+ perms = NULL;
+ if (rc)
+ goto err;
+
+ checker->range_count += reg_count;
+ }
+
+ if (checker->full_checker_rule)
+ return 0;
+
+ return wgchecker2_compact_ranges(checker);
+
+err:
+ sbi_free(perms);
+ return rc ? rc : SBI_EINVAL;
+}
+
+static int wgchecker2_parse_checker(void *fdt, int checker_node,
+ struct wgchecker2_checker *checker)
+{
+ const fdt32_t *val;
+ u64 base = 0, size = 0;
+ int len, rc;
+
+ rc = fdt_get_node_addr_size(fdt, checker_node, 0, &base, &size);
+ if (rc)
+ return rc;
+
+ val = fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SLOT_COUNT, &len);
+ if (!val || len < (int)sizeof(fdt32_t))
+ return SBI_EINVAL;
+
+ checker->mmio_base = base;
+ checker->mmio_size = size;
+ checker->slot_count = fdt32_to_cpu(val[0]);
+ sbi_snprintf(checker->name, sizeof(checker->name), "%s",
+ fdt_get_name(fdt, checker_node, NULL));
+
+ return wgchecker2_parse_checker_rules(fdt, checker_node, checker);
+}
+
+static void wgchecker2_program_clear_slots(const struct wgchecker2_checker *checker)
+{
+ u32 slot;
+
+ for (slot = 1; slot < checker->slot_count; slot++) {
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_ADDR, 0);
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM, 0);
+ wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG, 0);
+ }
+}
+
+static void wgchecker2_program_clear_last_slot(const struct wgchecker2_checker *checker)
+{
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM, 0);
+ wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG, 0);
+}
+
+static void wgchecker2_program_clear_slots_from(const struct wgchecker2_checker *checker,
+ u32 first_slot)
+{
+ u32 slot;
+
+ if (first_slot >= checker->slot_count)
+ return;
+
+ for (slot = first_slot; slot < checker->slot_count; slot++) {
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_ADDR, 0);
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM, 0);
+ wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG, 0);
+ }
+}
+
+static int wgchecker2_program_checker(const struct wgchecker2_checker *checker)
+{
+ u64 prev_end = 0;
+ u32 required_slots = 0, slot = 1, i;
+
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_ERRCAUSE, 0);
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_ERRADDR, 0);
+
+ if (checker->full_checker_rule) {
+ wgchecker2_program_clear_slots(checker);
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM,
+ checker->full_checker_perm);
+ wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG,
+ WGCHECKER2_SLOT_CFG_A_TOR);
+ return 0;
+ }
+
+ for (i = 0; i < checker->range_count; i++) {
+ if (!i || checker->ranges[i].base != prev_end)
+ required_slots++;
+ required_slots++;
+ prev_end = checker->ranges[i].base + checker->ranges[i].size;
+ }
+
+ if (required_slots > checker->slot_count - 1)
+ return SBI_EINVAL;
+
+ prev_end = 0;
+ for (i = 0; i < checker->range_count; i++) {
+ const struct wgchecker2_range *range = &checker->ranges[i];
+ u64 end = range->base + range->size;
+
+ if (!i || range->base != prev_end) {
+ wgchecker2_write64(checker->mmio_base +
+ WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_ADDR,
+ wgchecker2_slot_addr_encode(range->base));
+ wgchecker2_write64(checker->mmio_base +
+ WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM, 0);
+ wgchecker2_write32(checker->mmio_base +
+ WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG,
+ WGCHECKER2_SLOT_CFG_A_OFF);
+ slot++;
+ }
+
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_ADDR,
+ wgchecker2_slot_addr_encode(end));
+ wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_PERM, range->perm);
+ wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE +
+ slot * WGCHECKER2_MMIO_SLOT_STRIDE +
+ WGCHECKER2_MMIO_SLOT_CFG,
+ WGCHECKER2_SLOT_CFG_A_TOR);
+ prev_end = end;
+ slot++;
+ }
+
+ /*
+ * Keep the reset-time trusted-WID bypass slot alive until the new
+ * rule set is fully programmed, otherwise the DRAM checker can deny
+ * OpenSBI's own RAM accesses mid-update.
+ */
+ wgchecker2_program_clear_slots_from(checker, slot);
+ wgchecker2_program_clear_last_slot(checker);
+
+ return 0;
+}
+
+u32 wgchecker2_count_platform_checkers(void *fdt)
+{
+ int checker_node;
+ u32 count = 0;
+
+ if (!fdt)
+ return 0;
+
+ checker_node = -1;
+ while (true) {
+ checker_node = fdt_node_offset_by_compatible(fdt, checker_node,
+ WGCHECKER2_COMPAT);
+ if (checker_node < 0)
+ break;
+ if (fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES,
+ NULL))
+ count++;
+ }
+
+ return count;
+}
+
+int wgchecker2_init(void *fdt)
+{
+ struct wgchecker2_platform_ctx *platform;
+ int checker_node, rc;
+ u32 count, idx = 0;
+
+ wgchecker2_cleanup();
+
+ if (!fdt)
+ return 0;
+
+ count = wgchecker2_count_platform_checkers(fdt);
+ if (!count)
+ return 0;
+
+ platform = sbi_zalloc(sizeof(*platform));
+ if (!platform)
+ return SBI_ENOMEM;
+
+ platform->checker_count = count;
+ platform->checkers = sbi_calloc(sizeof(*platform->checkers), count);
+ if (!platform->checkers) {
+ sbi_free(platform);
+ return SBI_ENOMEM;
+ }
+
+ checker_node = -1;
+ while (true) {
+ checker_node = fdt_node_offset_by_compatible(fdt, checker_node,
+ WGCHECKER2_COMPAT);
+ if (checker_node < 0)
+ break;
+ if (!fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES,
+ NULL))
+ continue;
+
+ rc = wgchecker2_parse_checker(fdt, checker_node,
+ &platform->checkers[idx]);
+ if (rc) {
+ wgchecker2_free_platform_ctx(platform);
+ return rc;
+ }
+
+ rc = wgchecker2_program_checker(&platform->checkers[idx]);
+ if (rc) {
+ wgchecker2_free_platform_ctx(platform);
+ return rc;
+ }
+ idx++;
+ }
+
+ wgchecker2_platform = platform;
+ return 0;
+}
+
+void wgchecker2_cleanup(void)
+{
+ if (!wgchecker2_platform)
+ return;
+
+ wgchecker2_free_platform_ctx(wgchecker2_platform);
+ wgchecker2_platform = NULL;
+}
+
+u32 wgchecker2_checker_count(void)
+{
+ return wgchecker2_platform ? wgchecker2_platform->checker_count : 0;
+}
diff --git a/platform/generic/worldguard.c b/platform/generic/worldguard.c
new file mode 100644
index 00000000..a951459b
--- /dev/null
+++ b/platform/generic/worldguard.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Generic WorldGuard runtime support
+ *
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hartmask.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_hwiso.h>
+#include <sbi/sbi_scratch.h>
+#include <worldguard.h>
+#include <wgchecker2.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+struct worldguard_platform_ctx {
+ u32 hart_count;
+ bool runtime_enabled;
+ struct wg_cpu_defaults *hart_defaults;
+};
+
+static struct worldguard_platform_ctx *worldguard_platform;
+
+static u32 worldguard_wid_mask(u32 wid)
+{
+ return (wid < WGCHECKER2_MAX_WIDS) ? (1U << wid) : 0;
+}
+
+static void worldguard_free_platform(void)
+{
+ if (!worldguard_platform)
+ return;
+
+ sbi_free(worldguard_platform->hart_defaults);
+ sbi_free(worldguard_platform);
+ worldguard_platform = NULL;
+}
+
+static bool worldguard_runtime_enabled(void)
+{
+ return worldguard_platform && worldguard_platform->runtime_enabled;
+}
+
+static void worldguard_init_cpu_defaults(struct worldguard_platform_ctx *platform)
+{
+ u32 i;
+
+ if (!platform || !platform->hart_defaults)
+ return;
+
+ for (i = 0; i < platform->hart_count; i++) {
+ platform->hart_defaults[i].trusted_wid = 0;
+ platform->hart_defaults[i].nworlds = 1;
+ platform->hart_defaults[i].valid_wid_mask = 0x1;
+ }
+}
+
+static int worldguard_parse_wid_prop(void *fdt, int node, const char *prop_name,
+ u32 *out_wid)
+{
+ const fdt32_t *prop;
+ int len;
+
+ if (!out_wid)
+ return SBI_EINVAL;
+
+ prop = fdt_getprop(fdt, node, prop_name, &len);
+ if (!prop)
+ return SBI_ENOENT;
+ if (len != (int)sizeof(fdt32_t))
+ return SBI_EINVAL;
+
+ *out_wid = fdt32_to_cpu(prop[0]);
+ if (*out_wid >= WGCHECKER2_MAX_WIDS)
+ return SBI_EINVAL;
+
+ return 0;
+}
+
+static int worldguard_parse_widlist(void *fdt, int node, const char *prop_name,
+ u32 *out_mask)
+{
+ const fdt32_t *prop;
+ u32 mask = 0, count, wid;
+ int len, i;
+
+ if (!out_mask)
+ return SBI_EINVAL;
+
+ *out_mask = 0;
+
+ prop = fdt_getprop(fdt, node, prop_name, &len);
+ if (!prop)
+ return 0;
+ if (len < 0 || (len % (int)sizeof(fdt32_t)))
+ return SBI_EINVAL;
+
+ count = len / sizeof(fdt32_t);
+ if (count > WGCHECKER2_MAX_WIDS)
+ return SBI_EINVAL;
+
+ for (i = 0; i < (int)count; i++) {
+ wid = fdt32_to_cpu(prop[i]);
+ if (wid >= WGCHECKER2_MAX_WIDS)
+ return SBI_EINVAL;
+ if (mask & worldguard_wid_mask(wid))
+ return SBI_EINVAL;
+
+ mask |= worldguard_wid_mask(wid);
+ }
+
+ *out_mask = mask;
+ return 0;
+}
+
+static int worldguard_parse_cpu_defaults(void *fdt,
+ struct worldguard_platform_ctx *platform)
+{
+ struct wg_cpu_defaults *cpu_defaults;
+ u32 hartid, hartindex, max_wid;
+ int cpus_offset, cpu_offset, wgcpu, rc;
+
+ if (!fdt || !platform || !platform->hart_defaults)
+ return 0;
+
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return 0;
+
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ if (fdt_parse_hart_id(fdt, cpu_offset, &hartid))
+ continue;
+
+ hartindex = sbi_hartid_to_hartindex(hartid);
+ if (!sbi_hartindex_valid(hartindex) ||
+ hartindex >= platform->hart_count)
+ continue;
+
+ wgcpu = fdt_subnode_offset(fdt, cpu_offset, WORLDGUARD_CPU_NODE);
+ if (wgcpu < 0 || fdt_node_check_compatible(
+ fdt, wgcpu, WORLDGUARD_CPU_COMPAT))
+ continue;
+
+ cpu_defaults = &platform->hart_defaults[hartindex];
+ rc = worldguard_parse_wid_prop(fdt, wgcpu, WORLDGUARD_PROP_MWID,
+ &cpu_defaults->trusted_wid);
+ if (rc)
+ return rc;
+
+ max_wid = cpu_defaults->trusted_wid;
+ rc = worldguard_parse_widlist(fdt, wgcpu,
+ WORLDGUARD_PROP_MWIDLIST,
+ &cpu_defaults->valid_wid_mask);
+ if (rc)
+ return rc;
+
+ cpu_defaults->valid_wid_mask |=
+ worldguard_wid_mask(cpu_defaults->trusted_wid);
+ if (cpu_defaults->valid_wid_mask) {
+ u32 wid;
+
+ for (wid = 0; wid < WGCHECKER2_MAX_WIDS; wid++) {
+ if (cpu_defaults->valid_wid_mask & (1U << wid))
+ max_wid = wid;
+ }
+ }
+
+ cpu_defaults->nworlds = max_wid + 1;
+ }
+
+ return 0;
+}
+
+static bool worldguard_has_cpu_runtime(void *fdt)
+{
+ u32 hartid;
+ int cpus_offset, cpu_offset, wgcpu;
+
+ if (!fdt)
+ return false;
+
+ cpus_offset = fdt_path_offset(fdt, "/cpus");
+ if (cpus_offset < 0)
+ return false;
+
+ fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+ if (fdt_parse_hart_id(fdt, cpu_offset, &hartid))
+ continue;
+
+ wgcpu = fdt_subnode_offset(fdt, cpu_offset, WORLDGUARD_CPU_NODE);
+ if (wgcpu < 0)
+ continue;
+ if (fdt_node_check_compatible(fdt, wgcpu, WORLDGUARD_CPU_COMPAT))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int worldguard_validate_domain_ctx(const struct sbi_domain *dom,
+ const struct worldguard_domain_ctx *ctx)
+{
+ const struct wg_cpu_defaults *cpu_defaults;
+ u32 hartindex;
+
+ if (!worldguard_platform || !dom || !ctx || dom == &root ||
+ !dom->possible_harts)
+ return 0;
+
+ for (hartindex = 0; hartindex < worldguard_platform->hart_count;
+ hartindex++) {
+ if (!sbi_hartmask_test_hartindex(hartindex, dom->possible_harts))
+ continue;
+
+ cpu_defaults = &worldguard_platform->hart_defaults[hartindex];
+ if (!(cpu_defaults->valid_wid_mask & worldguard_wid_mask(ctx->wid)))
+ return SBI_EINVAL;
+ if (ctx->widlist_mask & ~cpu_defaults->valid_wid_mask)
+ return SBI_EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct wg_cpu_defaults *worldguard_current_cpu_defaults(void)
+{
+ u32 hartindex;
+
+ if (!worldguard_platform || !worldguard_platform->hart_defaults)
+ return NULL;
+
+ hartindex = sbi_hartid_to_hartindex(current_hartid());
+ if (!sbi_hartindex_valid(hartindex) ||
+ hartindex >= worldguard_platform->hart_count)
+ return NULL;
+
+ return &worldguard_platform->hart_defaults[hartindex];
+}
+
+static u32 worldguard_fallback_wid(void)
+{
+ const struct wg_cpu_defaults *cpu_defaults =
+ worldguard_current_cpu_defaults();
+
+ return cpu_defaults ? cpu_defaults->trusted_wid : 0;
+}
+
+static u32 worldguard_valid_wid_mask(void)
+{
+ const struct wg_cpu_defaults *cpu_defaults =
+ worldguard_current_cpu_defaults();
+
+ return cpu_defaults ? cpu_defaults->valid_wid_mask :
+ worldguard_wid_mask(worldguard_fallback_wid());
+}
+
+static u32 worldguard_select_slwid(u32 widlist_mask, bool has_wid, u32 wid,
+ u32 fallback)
+{
+ u32 i;
+
+ if (!widlist_mask)
+ return fallback;
+
+ if (has_wid && (worldguard_wid_mask(wid) & widlist_mask))
+ return wid;
+
+ for (i = 0; i < WGCHECKER2_MAX_WIDS; i++) {
+ if (widlist_mask & (1U << i))
+ return i;
+ }
+
+ return fallback;
+}
+
+static void worldguard_program_wid_state(u32 mlwid, u32 mwiddeleg, u32 slwid)
+{
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+
+ if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG))
+ return;
+
+ if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) {
+ csr_write(CSR_MLWID, mlwid);
+ return;
+ }
+
+ csr_write(CSR_MWIDDELEG, 0);
+ csr_write(CSR_MLWID, mlwid);
+ if (mwiddeleg) {
+ csr_write(CSR_MWIDDELEG, mwiddeleg);
+ csr_write(CSR_SLWID, slwid);
+ }
+}
+
+static int worldguard_init(void *fdt)
+{
+ struct worldguard_platform_ctx *platform;
+ u32 checker_count;
+ bool has_runtime;
+ int rc;
+
+ wgchecker2_cleanup();
+ worldguard_free_platform();
+
+ if (!fdt)
+ return 0;
+
+ checker_count = wgchecker2_count_platform_checkers(fdt);
+ has_runtime = worldguard_has_cpu_runtime(fdt);
+ if (!checker_count && !has_runtime)
+ return 0;
+
+ platform = sbi_zalloc(sizeof(*platform));
+ if (!platform)
+ return SBI_ENOMEM;
+
+ platform->hart_count = sbi_scratch_last_hartindex() + 1;
+ platform->runtime_enabled = has_runtime;
+ platform->hart_defaults = sbi_calloc(sizeof(*platform->hart_defaults),
+ platform->hart_count);
+ if (!platform->hart_defaults) {
+ sbi_free(platform);
+ return SBI_ENOMEM;
+ }
+
+ worldguard_init_cpu_defaults(platform);
+ rc = worldguard_parse_cpu_defaults(fdt, platform);
+ if (rc) {
+ sbi_free(platform->hart_defaults);
+ sbi_free(platform);
+ return rc;
+ }
+
+ worldguard_platform = platform;
+
+ if (checker_count) {
+ rc = wgchecker2_init(fdt);
+ if (rc) {
+ worldguard_free_platform();
+ wgchecker2_cleanup();
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int worldguard_domain_init(void *fdt, int domain_offset,
+ struct sbi_domain *dom, void **out_ctx)
+{
+ struct worldguard_domain_ctx *ctx;
+ int hoff, child, rc;
+ bool found = false;
+
+ if (!out_ctx)
+ return SBI_EINVAL;
+
+ *out_ctx = NULL;
+ if (!worldguard_runtime_enabled())
+ return 0;
+ if (!fdt || domain_offset < 0)
+ return 0;
+
+ hoff = fdt_subnode_offset(fdt, domain_offset, "hw-isolation");
+ if (hoff < 0)
+ return (dom == &root) ? 0 : SBI_EINVAL;
+
+ fdt_for_each_subnode(child, fdt, hoff) {
+ if (fdt_node_check_compatible(fdt, child, WGCHECKER2_COMPAT))
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return (dom == &root) ? 0 : SBI_EINVAL;
+
+ ctx = sbi_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return SBI_ENOMEM;
+
+ rc = worldguard_parse_wid_prop(fdt, child, WORLDGUARD_PROP_WID,
+ &ctx->wid);
+ if (rc)
+ goto err_free_ctx;
+ ctx->has_wid = true;
+
+ rc = worldguard_parse_widlist(fdt, child, WORLDGUARD_PROP_WIDLIST,
+ &ctx->widlist_mask);
+ if (rc)
+ goto err_free_ctx;
+
+ rc = worldguard_validate_domain_ctx(dom, ctx);
+ if (rc)
+ goto err_free_ctx;
+
+ *out_ctx = ctx;
+ return 0;
+
+err_free_ctx:
+ sbi_free(ctx);
+ return rc;
+}
+
+static void worldguard_domain_exit(const struct sbi_domain *src,
+ const struct sbi_domain *dst, void *ctx)
+{
+ u32 mlwid = worldguard_fallback_wid();
+
+ (void)ctx;
+ if (!worldguard_runtime_enabled())
+ return;
+
+ worldguard_program_wid_state(mlwid, 0, mlwid);
+
+}
+
+static void worldguard_domain_enter(const struct sbi_domain *dst,
+ const struct sbi_domain *src, void *ctx)
+{
+ struct worldguard_domain_ctx *dctx = ctx;
+ u32 valid_mask = worldguard_valid_wid_mask();
+ u32 mlwid = worldguard_fallback_wid();
+ u32 mwiddeleg = 0;
+ u32 slwid = mlwid;
+
+ (void)src;
+ if (!worldguard_runtime_enabled())
+ return;
+
+ if (dctx && dctx->has_wid && (worldguard_wid_mask(dctx->wid) & valid_mask))
+ mlwid = dctx->wid;
+
+ if (dctx)
+ mwiddeleg = dctx->widlist_mask & valid_mask;
+ slwid = worldguard_select_slwid(mwiddeleg, dctx && dctx->has_wid,
+ dctx ? dctx->wid : 0, mlwid);
+
+ worldguard_program_wid_state(mlwid, mwiddeleg, slwid);
+
+}
+
+static void worldguard_domain_cleanup(struct sbi_domain *dom, void *ctx)
+{
+ (void)dom;
+ sbi_free(ctx);
+}
+
+static const struct sbi_hwiso_ops worldguard_ops = {
+ .name = WGCHECKER2_COMPAT,
+ .init = worldguard_init,
+ .domain_init = worldguard_domain_init,
+ .domain_exit = worldguard_domain_exit,
+ .domain_enter = worldguard_domain_enter,
+ .domain_cleanup = worldguard_domain_cleanup,
+};
+
+int worldguard_register(void)
+{
+ return sbi_hwiso_register(&worldguard_ops);
+}
+
+const struct sbi_hwiso_ops *worldguard_ops_get(void)
+{
+ return &worldguard_ops;
+}
+
+#ifdef CONFIG_SBIUNIT
+static struct worldguard_domain_ctx *
+worldguard_test_find_domain_ctx(const struct sbi_domain *dom)
+{
+ u32 i;
+
+ if (!dom || !dom->hwiso_ctxs)
+ return NULL;
+
+ for (i = 0; i < dom->hwiso_ctx_count; i++) {
+ if (dom->hwiso_ctxs[i].ops != &worldguard_ops)
+ continue;
+
+ return dom->hwiso_ctxs[i].ctx;
+ }
+
+ return NULL;
+}
+
+int worldguard_test_check_runtime_state(bool runtime_enabled)
+{
+ if (!worldguard_platform)
+ return runtime_enabled ? SBI_ENOENT : 0;
+ if (worldguard_platform->runtime_enabled != runtime_enabled)
+ return SBI_EINVAL;
+
+ return 0;
+}
+
+int worldguard_test_check_domain_state(const struct sbi_domain *dom,
+ bool expect_ctx, u32 wid,
+ u32 widlist_mask)
+{
+ struct worldguard_domain_ctx *ctx = worldguard_test_find_domain_ctx(dom);
+
+ if (!expect_ctx)
+ return ctx ? SBI_EINVAL : 0;
+ if (!ctx || !ctx->has_wid)
+ return SBI_ENOENT;
+ if (ctx->wid != wid || ctx->widlist_mask != widlist_mask)
+ return SBI_EINVAL;
+
+ return 0;
+}
+#endif
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 5/7] test: add generic hwiso SBI unit coverage
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
` (3 preceding siblings ...)
2026-05-19 20:33 ` [PATCH 4/7] platform: generic: add WorldGuard hwiso support with wgchecker2 Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 6/7] platform: virt: add QEMU virt WorldGuard hwiso tests Raymond Mao
2026-05-19 20:33 ` [PATCH 7/7] platform: virt: add WorldGuard HWISO failure-mode SBIUNIT test Raymond Mao
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Add a generic HWISO SBI unit suite that exercises boot-time
initialization and domain-switch hook sequencing without embedding one
platform mechanism's policy directly into the core test driver.
Provide a small test registration layer so each HWISO mechanism can
supply its own state and quiesce assertions.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
include/sbi/sbi_hwiso_test.h | 33 +++++++
lib/sbi/objects.mk | 3 +
lib/sbi/sbi_hwiso_test.c | 167 +++++++++++++++++++++++++++++++++++
lib/sbi/sbi_hwiso_testlib.c | 108 ++++++++++++++++++++++
4 files changed, 311 insertions(+)
create mode 100644 include/sbi/sbi_hwiso_test.h
create mode 100644 lib/sbi/sbi_hwiso_test.c
create mode 100644 lib/sbi/sbi_hwiso_testlib.c
diff --git a/include/sbi/sbi_hwiso_test.h b/include/sbi/sbi_hwiso_test.h
new file mode 100644
index 00000000..3970df5a
--- /dev/null
+++ b/include/sbi/sbi_hwiso_test.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#ifndef __SBI_HWISO_TEST_H__
+#define __SBI_HWISO_TEST_H__
+
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_hwiso.h>
+#include <sbi/sbi_unit_test.h>
+
+#ifdef CONFIG_SBIUNIT
+struct sbi_hwiso_test_ops {
+ void (*boot_test)(struct sbiunit_test_case *test);
+ void (*domain_state_test)(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom, void *ctx);
+ void (*domain_quiesce_test)(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom, void *ctx);
+};
+
+int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops,
+ const struct sbi_hwiso_test_ops *test_ops);
+void sbi_hwiso_test_boot(struct sbiunit_test_case *test);
+void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom);
+void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom);
+#endif
+
+#endif
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index 6091499a..8839d13c 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -13,9 +13,12 @@ libsbi-objs-y += riscv_hardfp.o
libsbi-objs-y += riscv_locks.o
libsbi-objs-$(CONFIG_SBIUNIT) += sbi_unit_test.o
libsbi-objs-$(CONFIG_SBIUNIT) += sbi_unit_tests.o
+libsbi-objs-$(CONFIG_SBIUNIT) += sbi_hwiso_testlib.o
libsbi-objs-$(CONFIG_SBIUNIT) += sbi_bitmap_test.o
+libsbi-objs-$(CONFIG_SBIUNIT) += sbi_hwiso_test.o
carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += bitmap_test_suite
+carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += hwiso_test_suite
carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += console_test_suite
libsbi-objs-y += sbi_ecall.o
diff --git a/lib/sbi/sbi_hwiso_test.c b/lib/sbi/sbi_hwiso_test.c
new file mode 100644
index 00000000..716596e2
--- /dev/null
+++ b/lib/sbi/sbi_hwiso_test.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_domain_context.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_hwiso.h>
+#include <sbi/sbi_hwiso_test.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_trap.h>
+#include <sbi/sbi_unit_test.h>
+
+static u32 hwiso_collect_switch_domains(struct sbi_domain *current,
+ struct sbi_domain **targets,
+ u32 max_targets)
+{
+ u32 i;
+ u32 count = 0;
+ struct sbi_domain *dom;
+
+ if (!targets || !max_targets)
+ return 0;
+
+ sbi_domain_for_each(i, dom) {
+ if (dom == current || dom == &root)
+ continue;
+
+ targets[count++] = dom;
+ if (count == max_targets)
+ return count;
+ }
+
+ if (current != &root && count < max_targets)
+ targets[count++] = &root;
+
+ return count;
+}
+
+static void hwiso_snapshot_context(struct sbi_context *ctx)
+{
+ struct sbi_trap_regs *trap_regs;
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+
+ trap_regs = (struct sbi_trap_regs *)(csr_read(CSR_MSCRATCH) -
+ SBI_TRAP_REGS_SIZE);
+ ctx->regs = *trap_regs;
+ ctx->sstatus = csr_read(CSR_SSTATUS);
+ ctx->sie = csr_read(CSR_SIE);
+ ctx->stvec = csr_read(CSR_STVEC);
+ ctx->sscratch = csr_read(CSR_SSCRATCH);
+ ctx->sepc = csr_read(CSR_SEPC);
+ ctx->scause = csr_read(CSR_SCAUSE);
+ ctx->stval = csr_read(CSR_STVAL);
+ ctx->sip = csr_read(CSR_SIP);
+ ctx->satp = csr_read(CSR_SATP);
+ ctx->scounteren = 0;
+ ctx->senvcfg = 0;
+ if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
+ ctx->scounteren = csr_read(CSR_SCOUNTEREN);
+ if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12)
+ ctx->senvcfg = csr_read(CSR_SENVCFG);
+ ctx->initialized = true;
+}
+
+static struct sbi_context *hwiso_ensure_context(struct sbiunit_test_case *test,
+ struct sbi_domain *dom,
+ struct sbi_context *tmpl,
+ u32 hartindex)
+{
+ struct sbi_context *dom_ctx;
+
+ dom_ctx = sbi_hartindex_to_domain_context(hartindex, dom);
+ if (!dom_ctx) {
+ dom_ctx = sbi_zalloc(sizeof(*dom_ctx));
+ SBIUNIT_ASSERT_NE(test, dom_ctx, NULL);
+ dom_ctx->dom = dom;
+ dom->hartindex_to_context_table[hartindex] = dom_ctx;
+ }
+
+ if (tmpl) {
+ *dom_ctx = *tmpl;
+ dom_ctx->dom = dom;
+ dom->hartindex_to_context_table[hartindex] = dom_ctx;
+ }
+
+ return dom_ctx;
+}
+
+static void hwiso_boot_test(struct sbiunit_test_case *test)
+{
+ sbi_hwiso_test_boot(test);
+}
+
+static void hwiso_domain_switch_test(struct sbiunit_test_case *test)
+{
+ struct sbi_domain *cur_dom = sbi_domain_thishart_ptr();
+ struct sbi_context *ctx = sbi_domain_context_thishart_ptr();
+ struct sbi_domain *switch_doms[2];
+ struct sbi_context *cur_ctx;
+ struct sbi_context tmpl_ctx;
+ u32 switch_count;
+ u32 i;
+ u32 hartindex = sbi_hartid_to_hartindex(current_hartid());
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ bool debug_on = false;
+
+ SBIUNIT_ASSERT_NE(test, cur_dom, NULL);
+
+ if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS)
+ debug_on = true;
+ else
+ scratch->options |= SBI_SCRATCH_DEBUG_PRINTS;
+
+ if (!ctx) {
+ cur_ctx = sbi_zalloc(sizeof(*cur_ctx));
+ SBIUNIT_ASSERT_NE(test, cur_ctx, NULL);
+ cur_ctx->dom = cur_dom;
+ cur_dom->hartindex_to_context_table[hartindex] = cur_ctx;
+ ctx = cur_ctx;
+ }
+
+ hwiso_snapshot_context(ctx);
+ tmpl_ctx = *ctx;
+ switch_count = hwiso_collect_switch_domains(cur_dom, switch_doms, 2);
+
+ (void)hwiso_ensure_context(test, cur_dom, NULL, hartindex);
+ for (i = 0; i < switch_count; i++)
+ (void)hwiso_ensure_context(test, switch_doms[i], &tmpl_ctx,
+ hartindex);
+
+ if (!switch_count)
+ goto out;
+
+ sbi_hwiso_domain_exit(cur_dom, switch_doms[0]);
+ sbi_hwiso_test_domain_quiesced(test, cur_dom);
+ sbi_hwiso_domain_enter(switch_doms[0], cur_dom);
+ sbi_hwiso_test_domain_state(test, switch_doms[0]);
+ sbi_hwiso_domain_exit(switch_doms[0], cur_dom);
+ sbi_hwiso_test_domain_quiesced(test, switch_doms[0]);
+ sbi_hwiso_domain_enter(cur_dom, switch_doms[0]);
+ sbi_hwiso_test_domain_state(test, cur_dom);
+
+ for (i = 0; i < switch_count; i++) {
+ SBIUNIT_ASSERT_EQ(test, sbi_domain_context_enter(switch_doms[i]), 0);
+ sbi_hwiso_test_domain_state(test, switch_doms[i]);
+ }
+
+ SBIUNIT_ASSERT_EQ(test, sbi_domain_context_enter(cur_dom), 0);
+ sbi_hwiso_test_domain_state(test, cur_dom);
+
+out:
+ if (!debug_on)
+ scratch->options &= ~SBI_SCRATCH_DEBUG_PRINTS;
+}
+
+static struct sbiunit_test_case hwiso_test_cases[] = {
+ SBIUNIT_TEST_CASE(hwiso_boot_test),
+ SBIUNIT_TEST_CASE(hwiso_domain_switch_test),
+ SBIUNIT_END_CASE,
+};
+
+SBIUNIT_TEST_SUITE(hwiso_test_suite, hwiso_test_cases);
diff --git a/lib/sbi/sbi_hwiso_testlib.c b/lib/sbi/sbi_hwiso_testlib.c
new file mode 100644
index 00000000..2a988088
--- /dev/null
+++ b/lib/sbi/sbi_hwiso_testlib.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2026 RISCstar Solutions Corporation.
+ *
+ * Author: Raymond Mao <raymond.mao@riscstar.com>
+ */
+
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_hwiso_test.h>
+#include <sbi/sbi_list.h>
+
+struct sbi_hwiso_test_node {
+ const struct sbi_hwiso_ops *ops;
+ const struct sbi_hwiso_test_ops *test_ops;
+ struct sbi_dlist node;
+};
+
+static SBI_LIST_HEAD(hwiso_test_ops_list);
+
+static const struct sbi_hwiso_test_ops *
+sbi_hwiso_find_test_ops(const struct sbi_hwiso_ops *ops)
+{
+ struct sbi_hwiso_test_node *entry;
+
+ sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) {
+ if (entry->ops == ops)
+ return entry->test_ops;
+ }
+
+ return NULL;
+}
+
+int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops,
+ const struct sbi_hwiso_test_ops *test_ops)
+{
+ struct sbi_hwiso_test_node *node;
+
+ if (!ops || !test_ops)
+ return SBI_EINVAL;
+ if (sbi_hwiso_find_test_ops(ops))
+ return SBI_EALREADY;
+
+ node = sbi_zalloc(sizeof(*node));
+ if (!node)
+ return SBI_ENOMEM;
+
+ node->ops = ops;
+ node->test_ops = test_ops;
+ SBI_INIT_LIST_HEAD(&node->node);
+ sbi_list_add_tail(&node->node, &hwiso_test_ops_list);
+
+ return 0;
+}
+
+void sbi_hwiso_test_boot(struct sbiunit_test_case *test)
+{
+ struct sbi_hwiso_test_node *entry;
+
+ sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) {
+ if (!entry->test_ops->boot_test)
+ continue;
+ entry->test_ops->boot_test(test);
+ }
+}
+
+void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom)
+{
+ const struct sbi_hwiso_test_ops *test_ops;
+ u32 i;
+
+ if (!dom || !dom->hwiso_ctxs)
+ return;
+
+ for (i = 0; i < dom->hwiso_ctx_count; i++) {
+ if (!dom->hwiso_ctxs[i].ops)
+ continue;
+
+ test_ops = sbi_hwiso_find_test_ops(dom->hwiso_ctxs[i].ops);
+ if (!test_ops || !test_ops->domain_state_test)
+ continue;
+
+ test_ops->domain_state_test(test, dom, dom->hwiso_ctxs[i].ctx);
+ }
+}
+
+void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test,
+ const struct sbi_domain *dom)
+{
+ const struct sbi_hwiso_test_ops *test_ops;
+ u32 i;
+
+ if (!dom || !dom->hwiso_ctxs)
+ return;
+
+ for (i = 0; i < dom->hwiso_ctx_count; i++) {
+ if (!dom->hwiso_ctxs[i].ops)
+ continue;
+
+ test_ops = sbi_hwiso_find_test_ops(dom->hwiso_ctxs[i].ops);
+ if (!test_ops || !test_ops->domain_quiesce_test)
+ continue;
+
+ test_ops->domain_quiesce_test(test, dom,
+ dom->hwiso_ctxs[i].ctx);
+ }
+}
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 6/7] platform: virt: add QEMU virt WorldGuard hwiso tests
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
` (4 preceding siblings ...)
2026-05-19 20:33 ` [PATCH 5/7] test: add generic hwiso SBI unit coverage Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
2026-05-19 20:33 ` [PATCH 7/7] platform: virt: add WorldGuard HWISO failure-mode SBIUNIT test Raymond Mao
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Add QEMU virt WorldGuard mechanism-specific tests for boot-time
checker programming and runtime CSR state, and verify the expected
root / domain@0 / domain@1 behavior through the HWISO test callback
API.
Signed-off-by: Raymond Mao <raymond.mao@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@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@6000000",
+ .slot_count = 16,
+ .slots = qemu_virt_wg_dram_slots,
+ .last_slot = { .perm = 0, .cfg = 0 },
+ },
+ {
+ .path = "/soc/wgchecker@6001000",
+ .slot_count = 16,
+ .slots = qemu_virt_wg_zero_slots,
+ .last_slot = { .perm = 0xc3, .cfg = WGCHECKER2_SLOT_CFG_A_TOR },
+ },
+ {
+ .path = "/soc/wgchecker@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@0"))
+ return &qemu_virt_wg_domain0_expect;
+ if (!sbi_strcmp(dom->name, "domain@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@0");
+ dom1 = qemu_virt_wg_find_domain("domain@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
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 7/7] platform: virt: add WorldGuard HWISO failure-mode SBIUNIT test
2026-05-19 20:33 [PATCH 0/7] Add WorldGuard hwiso support Raymond Mao
` (5 preceding siblings ...)
2026-05-19 20:33 ` [PATCH 6/7] platform: virt: add QEMU virt WorldGuard hwiso tests Raymond Mao
@ 2026-05-19 20:33 ` Raymond Mao
6 siblings, 0 replies; 8+ messages in thread
From: Raymond Mao @ 2026-05-19 20:33 UTC (permalink / raw)
To: opensbi
Cc: scott, dave.patel, raymond.mao, robin.randhawa, samuel.holland,
anup.patel, anuppate, anup, dhaval, peter.lin
From: Raymond Mao <raymond.mao@riscstar.com>
Extend the generic HWISO SBIUNIT test hook interface with a
failure-test callback and invoke it from a new hwiso_failure_mode_test
case.
Implement the QEMU virt WorldGuard failure-mode test by switching into
domain@0's WorldGuard state, issuing an intentional denied store to a
protected DRAM address, and verifying that the access raises the
expected store access fault with the faulting address recorded in
tval.
Also print the captured trap cause and trap value so the negative-path
result is visible in the runtime log.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
include/sbi/sbi_hwiso_test.h | 2 ++
lib/sbi/sbi_hwiso_test.c | 6 ++++
lib/sbi/sbi_hwiso_testlib.c | 11 ++++++
.../generic/virt/qemu_virt_wgchecker_test.c | 35 +++++++++++++++++++
4 files changed, 54 insertions(+)
diff --git a/include/sbi/sbi_hwiso_test.h b/include/sbi/sbi_hwiso_test.h
index 3970df5a..354e8d17 100644
--- a/include/sbi/sbi_hwiso_test.h
+++ b/include/sbi/sbi_hwiso_test.h
@@ -15,6 +15,7 @@
#ifdef CONFIG_SBIUNIT
struct sbi_hwiso_test_ops {
void (*boot_test)(struct sbiunit_test_case *test);
+ void (*failure_test)(struct sbiunit_test_case *test);
void (*domain_state_test)(struct sbiunit_test_case *test,
const struct sbi_domain *dom, void *ctx);
void (*domain_quiesce_test)(struct sbiunit_test_case *test,
@@ -24,6 +25,7 @@ struct sbi_hwiso_test_ops {
int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops,
const struct sbi_hwiso_test_ops *test_ops);
void sbi_hwiso_test_boot(struct sbiunit_test_case *test);
+void sbi_hwiso_test_failure(struct sbiunit_test_case *test);
void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test,
const struct sbi_domain *dom);
void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test,
diff --git a/lib/sbi/sbi_hwiso_test.c b/lib/sbi/sbi_hwiso_test.c
index 716596e2..6b8384d4 100644
--- a/lib/sbi/sbi_hwiso_test.c
+++ b/lib/sbi/sbi_hwiso_test.c
@@ -96,6 +96,11 @@ static void hwiso_boot_test(struct sbiunit_test_case *test)
sbi_hwiso_test_boot(test);
}
+static void hwiso_failure_mode_test(struct sbiunit_test_case *test)
+{
+ sbi_hwiso_test_failure(test);
+}
+
static void hwiso_domain_switch_test(struct sbiunit_test_case *test)
{
struct sbi_domain *cur_dom = sbi_domain_thishart_ptr();
@@ -160,6 +165,7 @@ out:
static struct sbiunit_test_case hwiso_test_cases[] = {
SBIUNIT_TEST_CASE(hwiso_boot_test),
+ SBIUNIT_TEST_CASE(hwiso_failure_mode_test),
SBIUNIT_TEST_CASE(hwiso_domain_switch_test),
SBIUNIT_END_CASE,
};
diff --git a/lib/sbi/sbi_hwiso_testlib.c b/lib/sbi/sbi_hwiso_testlib.c
index 2a988088..2dfb8646 100644
--- a/lib/sbi/sbi_hwiso_testlib.c
+++ b/lib/sbi/sbi_hwiso_testlib.c
@@ -64,6 +64,17 @@ void sbi_hwiso_test_boot(struct sbiunit_test_case *test)
}
}
+void sbi_hwiso_test_failure(struct sbiunit_test_case *test)
+{
+ struct sbi_hwiso_test_node *entry;
+
+ sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) {
+ if (!entry->test_ops->failure_test)
+ continue;
+ entry->test_ops->failure_test(test);
+ }
+}
+
void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test,
const struct sbi_domain *dom)
{
diff --git a/platform/generic/virt/qemu_virt_wgchecker_test.c b/platform/generic/virt/qemu_virt_wgchecker_test.c
index 0eeaa3a0..eaaa39f7 100644
--- a/platform/generic/virt/qemu_virt_wgchecker_test.c
+++ b/platform/generic/virt/qemu_virt_wgchecker_test.c
@@ -7,14 +7,17 @@
#include <libfdt.h>
#include <sbi/riscv_encoding.h>
#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.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_trap.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_unit_test.h>
+#include <sbi/sbi_unpriv.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <wgchecker2.h>
#include <worldguard.h>
@@ -40,6 +43,8 @@ struct qemu_virt_wg_expect {
u32 slwid;
};
+#define QEMU_VIRT_WG_DENIED_STORE_ADDR 0xc0001000UL
+
static u64 qemu_virt_wg_mmio_read64(unsigned long addr)
{
#if __riscv_xlen != 32
@@ -314,8 +319,38 @@ static void qemu_virt_wg_boot_test(struct sbiunit_test_case *test)
0);
}
+static void qemu_virt_wg_failure_test(struct sbiunit_test_case *test)
+{
+ struct sbi_domain *cur_dom = sbi_domain_thishart_ptr();
+ struct sbi_domain *dom0 = qemu_virt_wg_find_domain("domain@0");
+ struct sbi_trap_info trap = { 0 };
+
+ SBIUNIT_ASSERT_NE(test, cur_dom, NULL);
+ SBIUNIT_ASSERT_NE(test, dom0, NULL);
+
+ if (cur_dom != dom0) {
+ sbi_hwiso_domain_exit(cur_dom, dom0);
+ sbi_hwiso_domain_enter(dom0, cur_dom);
+ }
+
+ sbi_store_u32((u32 *)QEMU_VIRT_WG_DENIED_STORE_ADDR, 0x5a5aa5a5U,
+ &trap);
+
+ if (cur_dom != dom0) {
+ sbi_hwiso_domain_exit(dom0, cur_dom);
+ sbi_hwiso_domain_enter(cur_dom, dom0);
+ }
+
+ sbi_printf("[WG TEST] failure trap cause=0x%lx tval=0x%lx\n",
+ trap.cause, trap.tval);
+
+ SBIUNIT_ASSERT_EQ(test, trap.cause, CAUSE_STORE_ACCESS);
+ SBIUNIT_ASSERT_EQ(test, trap.tval, QEMU_VIRT_WG_DENIED_STORE_ADDR);
+}
+
const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops = {
.boot_test = qemu_virt_wg_boot_test,
+ .failure_test = qemu_virt_wg_failure_test,
.domain_state_test = qemu_virt_wg_assert_state,
.domain_quiesce_test = qemu_virt_wg_assert_quiesced,
};
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 8+ messages in thread