Skip to main content

Milestone 5

Milestone 5 Status

The latest M5 patch series was posted to the OpenSBI mailing list on May 14, 2026 and is under review.


Milestone Description

This milestone provides an implementation using WorldGuard of the generic hardware isolation framework defined in Milestone 4. This includes boot time WorldGuard configuration and OpenSBI domain context switch time WorldGuard configuration.

Overview

Milestone 5 (M5) extends the generic hardware isolation framework introduced in milestone 4 (M4) to provide a concrete WorldGuard implementation in OpenSBI.

The M5 work uses the existing hardware isolation framework’s registration and domain lifecycle model, then adds the platform-specific logic required to configure WorldGuard checker units at boot time and reprogram WorldGuard hart state during domain transitions.

The current implementation uses QEMU and its WorldGuard model as the reference platform for validation. The implementation provides a complete software path for:

  • boot-time parsing of WorldGuard resources and programming of checker MMIO state based on SiFive WorldGuard Checker2 (sifive,wgchecker2) implementation.
  • per-domain parsing of WorldGuard execution metadata from DeviceTree
  • runtime reprogramming of WorldGuard WID-related CSRs during domain enter/exit paths
  • unit test coverage for both the generic hardware isolation switch path and the WorldGuard mechanism state
Deliverables

The M5 deliverables include:

  • software changes to OpenSBI to program platform WorldGuard checker units at boot time
  • software changes to reprogram WorldGuard WID-related CSRs at domain context switch time
  • DeviceTree binding updates and examples for WorldGuard configuration
  • unit-test coverage and local validation using QEMU’s WorldGuard model

Implementation is complete and validated on QEMU (-M virt,aia=aplic,wg=on), provided by the RISE team at riscv-wg-dts.

Background and Motivation

The M4 work introduced a mechanism-agnostic hardware isolation framework for OpenSBI domains. That framework addressed the missing operations during the domain lifecycle by providing:

  • a registration API for multiple isolation mechanisms
  • boot-time initialization for mechanism-global state
  • per-domain initialization for mechanism-specific policies
  • domain exit and domain enter hooks during context switches

The existing OpenSBI domain framework controls CPU memory accesses using RISC-V Physical Memory Protection (PMP). This implements isolation by permitting certain memory regions to be accessible or not based on privilege level. The effect of this isolation is limited to the CPU.

WorldGuard on the other hand, provides a system level isolation mechanism, which limits access to system resources based on distinct domains. It applies to peripherals as well as CPUs, and provides more granular access control, independent of privilege level.

Implementing a hardware isolation mechanism based on WorldGuard involves configuring platform-wide WID-aware checkers and programming the WorldGuard execution state on harts when they change domains.

M5 builds on the foundation of M4 by connecting WorldGuard policy described in DeviceTree to routines that perform:

  • platform-wide checker programming at boot time
  • per-hart WID state programming at runtime
Design Goals
  • Use the M4 hardware isolation abstraction without adding a WorldGuard-specific API into the generic framework.
  • Have OpenSBI discover and configure all WorldGuard checker devices during boot time.
  • Parse per-domain WorldGuard metadata from DeviceTree and attach it to the OpenSBI domain state.
  • Reprogram hart-local WorldGuard execution state during domain transitions.
  • Keep generic hardware isolation tests separate from mechanism-specific WorldGuard tests.
Non-goals
  • Support any WorldGuard checker model other than SiFive WorldGuard Checker2 (sifive,wgchecker2). Dynamic reallocation of checker resources after system boot.

High-Level Architecture

Design Overview

The M5 implementation keeps the generic M4 layering intact. The generic hardware isolation framework remains responsible for:

  • maintaining a list of registered hardware isolation mechanisms
  • invoking mechanism-global initialization during boot (init)
  • allocating per-domain mechanism contexts (domain_init)
  • dispatching domain_exit() and domain_enter() during domain switches
  • invoking cleanup on initialization failure (domain_cleanup)

The WorldGuard mechanism is one registered hardware isolation mechanism. It contributes its own implementation for:

  • init()
  • domain_init()
  • domain_exit()
  • domain_enter()
  • domain_cleanup()

At a high level, the flow is:

  1. OpenSBI boots and registers the WorldGuard hardware isolation mechanism.
  2. wg_init() scans the DeviceTree, builds a software model of each checker, validates the policy, and programs WorldGuard checker MMIO state.
  3. wg_domain_init() parses per-domain worldguard,wid and worldguard,widlist metadata and stores it in a per-domain hardware isolation context.
  4. During a domain switch, the generic hardware isolation framework invokes:
    • wg_domain_exit() for the current domain, and the WorldGuard implementation of this restores the fallback machine WID
    • wg_domain_enter() for the domain being switched to, and the WorldGuard implementation of this programs the destination WID/delegation state.
Hardware Isolation Framework Baseline

M5 depends on the M4 framework and does not modify its core model. The generic operations structure remains:

struct sbi_hwiso_ops {
const char *name;
int (*init)(void *fdt);
int (*domain_init)(void *fdt, int domain_offset,
struct sbi_domain *dom, void **ctx);
void (*domain_exit)(const struct sbi_domain *src,
const struct sbi_domain *dst, void *ctx);
void (*domain_enter)(const struct sbi_domain *dst,
const struct sbi_domain *src, void *ctx);
void (*domain_cleanup)(struct sbi_domain *dom, void *ctx);
};

Each OpenSBI domain carries a list of hardware isolation mechanism contexts:

struct sbi_hwiso_domain_ctx {
const struct sbi_hwiso_ops *ops;
void *ctx;
};

This means M5 can implement WorldGuard entirely as a mechanism-specific module without changing the domain model or adding WorldGuard-specific fields into the generic framework.

WorldGuard CSR Support

M5 defines the WorldGuard-related CSR IDs and hart extension flags required for runtime programming in OpenSBI:

  • CSR_MLWID
  • CSR_MWIDDELEG
  • CSR_SLWID
  • SBI_HART_EXT_SMWG
  • SBI_HART_EXT_SSWG

The extension flags allow the platform mechanism to probe whether the current hart advertises WorldGuard execution support before attempting to write the WorldGuard-related CSRs in run time.

The implementation treats mwid and mwidlist as CPU default configuration values conveyed by DeviceTree, while MLWID, MWIDDELEG, and SLWID are the runtime CSRs programmed during domain transitions.

Boot-Time WorldGuard Checker Programming

The boot-time checker implementation lives in:

  • platform/generic/wgchecker2.c

Its purpose is to parse all platform WorldGuard checker configuration from DeviceTree and convert that policy into WorldGuard checker MMIO programming.

The platform context is modeled by:

struct worldguard_platform_ctx {
u32 hart_count;
bool runtime_enabled;
struct wg_cpu_defaults *hart_defaults;
};

And WorldGuard checker state is modeled by:

struct wgchecker2_range {
u64 base;
u64 size;
u64 perm;
};

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;
};

Each checker entry stores:

  • MMIO base and size
  • hardware slot count
  • subordinate count
  • whether the checker collapses into a full-checker rule
  • an explicit list of parsed ranges and permissions

Boot-time parsing performs the following steps:

  1. Discover compatible = "sifive,wgchecker2" nodes.
  2. Ignore nodes that do not advertise sifive,subordinates, because they do not represent active checker devices for this software path.
  3. Parse each subordinate resource and its worldguard_cfg child.
  4. Parse perms strictly as 64-bit <hi lo> cell pairs.
  5. Support both:
    • full-resource rules
    • explicit worldguard_cfg/reg protected ranges
  6. Validate alignment, range wraparound, and overlap constraints.
  7. Sort and coalesce adjacent ranges when they have identical permissions.
  8. Convert the final policy into the wgchecker2 TOR (Top of Range) slot programming model.
  9. Program the checker MMIO registers at boot.

The parsing is intentionally fail-closed. Malformed perms, invalid subordinate phandles, invalid reg shapes, overlapping ranges, or impossible slot usage abort the initialization process and cleanup, rather than silently preserving partial policy.

Runtime WorldGuard Reprogramming on Domain Switch

The runtime world selection logic is split across:

  • CPU defaults parsed from /cpus/*/worldguard
  • per-domain WID policy parsed from domain/hw-isolation/worldguard
  • runtime CSR programming on domain_exit() and domain_enter()

The CPU-default context is modeled as:

struct wg_cpu_defaults {
u32 trusted_wid;
u32 nworlds;
u32 valid_wid_mask;
};

The per-domain WorldGuard context is modeled as:

struct worldguard_domain_ctx {
bool has_wid;
u32 wid;
u32 widlist_mask;
};
Runtime Enablement

Runtime CSR programming is enabled only when per-CPU WorldGuard runtime nodes exist. Checker-only DeviceTree configurations do not force runtime switching on. This keeps the boot-time checker configuration path independent from the hart-local CSR programming path.

Domain Initialization

wg_domain_init() is called from the generic hardware isolation per-domain initialization path. It:

  • finds the hw-isolation child node of each domain
  • finds the WorldGuard mechanism child by compatible string
  • parses worldguard,wid
  • parses worldguard,widlist
  • validates the domain WID and WIDLIST against each possible hart’s CPU WG defaults

The current implementation treats domain metadata strictly:

  • non-root domains fail if runtime is enabled but no hw-isolation node exists
  • non-root domains fail if the WorldGuard child is missing
  • worldguard,wid is mandatory once the WG node is parsed
  • worldguard,widlist is range-checked and duplicate-checked
Domain Exit

wg_domain_exit() quiesces the old domain’s WorldGuard state before the switch completes.

Current behavior:

  • restore MLWID to the current hart fallback/default machine WID
  • clear MWIDDELEG
  • do not write SLWID on the quiesce path
Domain Enter

wg_domain_enter() applies the destination domain’s WorldGuard execution policy.

The logic is:

  1. Determine the valid WID mask from the current hart CPU defaults.
  2. Select MLWID from the domain worldguard,wid if it is valid; otherwise fall back to the CPU default machine WID.
  3. Compute MWIDDELEG from the intersection of the domain delegated set and the CPU valid WID mask.
  4. Select SLWID using:
    1. the domain wid itself if it is included in the delegated set
    2. otherwise the lowest-numbered delegated WID
    3. otherwise the chosen MLWID
  5. Write the resulting CSRs.

The runtime CSR programming helper is gated by hart extensions:

  • if smwg is not present, runtime programming is skipped
  • if smwg is present but sswg is absent, only MLWID is written
  • if both are present, MWIDDELEG and SLWID are also programmed when delegation is active
DeviceTree-binding Model

The M5 design uses three separated WorldGuard-related configuration layers in DeviceTree.

  • Platform checker and protected resource policy in the normal system topology
  • CPU default execution policy under /cpus
  • Per-domain execution metadata under OpenSBI domain nodes
Platform Checker Nodes

Platform checker devices are described by nodes such as:

wgchecker0: wgchecker@10100000 {
compatible = "sifive,wgchecker2";
reg = <0x0 0x10100000 0x0 0x1000>;
sifive,slot-count = <8>;
sifive,subordinates = <&memory0 &flash0 &uart0>;
};

These nodes define the hardware checker instances OpenSBI programs at boot.

Protected Resource Policy Nodes

Protected resources carry a worldguard_cfg child that describes the permission policy:

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>;
};
};

The reg property inside worldguard_cfg is optional. When 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.

CPU Default WorldGuard Execution State

Each hart can provide a worldguard child under its CPU node:

cpu0: cpu@0 {
worldguard {
compatible = "riscv,wgcpu";
mwid = <3>;
mwidlist = <0 1 2 3>;
};
};

These values define the fallback machine WID and the valid/delegable WID set for that hart.

Per-domain WorldGuard Metadata

Domains describe their desired runtime WID state under the hardware isolation container:

guest0: domain@0 {
compatible = "opensbi,domain,instance";
...
hw-isolation {
worldguard {
compatible = "sifive,wgchecker2";
worldguard,wid = <0>;
worldguard,widlist = <0 1 3>;
};
};
};
WorldGuard Hardware Isolation Operation Hooks

The WorldGuard hardware isolation mechanism is implemented by registering:

static const struct sbi_hwiso_ops worldguard_ops = {
.name = "sifive,wgchecker2",
.init = worldguard_init,
.domain_init = worldguard_domain_init,
.domain_exit = worldguard_domain_exit,
.domain_enter = worldguard_domain_enter,
.domain_cleanup = worldguard_domain_cleanup,
};

The platform registration entry point is worldguard_register(), invoked from platform/generic/platform.c during early initialization. The function:

  • registers the runtime WorldGuard mechanism
  • under CONFIG_SBIUNIT, also registers mechanism-specific test callbacks

The hardware isolation core does not need to know anything about WorldGuard checker MMIO layout or WorldGuard CSR semantics.

Test Suite

The M5 deliverables contain both generic hardware isolation tests and QEMU WorldGuard mechanism-specific tests.

Generic Hardware Isolation Runtime Test

lib/sbi/sbi_hwiso_test.c exercises the generic domain switch path. It:

  • snapshots the current domain context
  • constructs per-domain context state for switch targets
  • invokes a mechanism-specific boot-time test callback
  • invokes a mechanism-specific failure-mode test callback
  • invokes sbi_hwiso_domain_exit() and sbi_hwiso_domain_enter()
  • invokes sbi_domain_context_enter()
  • dispatches mechanism-specific assertions through a test callback layer

The generic test discovers switch targets dynamically and prefers non-root domains, using root as a fallback path when needed.

Mechanism-specific WorldGuard Test
  • platform/generic/virt/qemu_virt_wgchecker_test.c performs QEMU virt WorldGuard-specific validation. It checks:
  • boot-time checker MMIO layout and slot programming
  • platform state such as checker count and runtime enablement
  • parsed per-domain wid / widlist values
  • failure-mode behavior by intentionally issuing a denied store to a protected DRAM address
  • runtime CSR values after domain enter
  • quiesced CSR values after domain exit

Build Steps and Test Instructions

Build via Buildroot Project

Get Buildroot source code:

$ git clone https://gitlab.com/riseproject/riscv-optee/buildroot.git -b rp016_m5

Configure Buildroot:

$ cd buildroot
$ make qemu_riscv64_virt_optee_defconfig

Build:

$ make -j$(nproc)

To avoid building errors due to outdated Buildroot native CMakeLists.txt files, if you have a CMAKE version > 3.30 on your host, build with:

$ make -j$(nproc) CMAKE_POLICY_VERSION_MINIMUM=3.5

This will build all of the required components. All build artifacts can be found under output/build.

Running OpenSBI Hardware Isolation Unit Tests

Start QEMU:

$ ./output/images/start-qemu-dto.sh

By running this script, the test DeviceTree overlay ‘qemu-virt-hwiso-overlay.dts’ will be compiled and applied to the dumped QEMU base DeviceTree before re-running QEMU.

Log messages that appear in the console after QEMU re-launch.

[HWISO] init sifive,wgchecker2
[WG] checker wgchecker@6002000 base=0x6002000 slots=1 rules=0 full-checker
[WG] checker wgchecker@6001000 base=0x6001000 slots=16 rules=0 full-checker
[WG] checker wgchecker@6000000 base=0x6000000 slots=16 rules=3
[HWISO] ops: sifive,wgchecker2, init domain: domain@1
[HWISO] ops: sifive,wgchecker2, init domain: domain@0
[HWISO] ops: sifive,wgchecker2, init domain: root

The boot logs show that the WorldGuard-based hardware isolation mechanism was registered successfully through the OpenSBI hardware isolation framework. During initialization, OpenSBI discovers all WorldGuard checker instances described in DeviceTree, records their base addresses and capabilities, and parses the per-domain WorldGuard metadata for domain@1, domain@0. This confirms that the platform-wide checker topology and the domain isolation context required by the hardware isolation framework implementation are available before runtime domain switching begins.

After boot, the SBI unit test suite starts automatically and runs the hardware isolation unit tests.

# Running SBIUNIT tests #
...
## Running test suite: hwiso_test_suite
[PASSED] hwiso_boot_test
[WG TEST] failure trap cause=0x7 tval=0xc0001000
[PASSED] hwiso_failure_mode_test
[HWISO] ops: sifive,wgchecker2, domain exit src=domain@0 dst=domain@1
[WG] domain_exit src=domain@0 dst=domain@1 mlwid=3 mwiddeleg=0x0
[HWISO] ops: sifive,wgchecker2, domain enter dst=domain@1 src=domain@0
[WG] domain_enter dst=domain@1 mlwid=1 mwiddeleg=0xa slwid=1
[HWISO] ops: sifive,wgchecker2, domain exit src=domain@1 dst=domain@0
[WG] domain_exit src=domain@1 dst=domain@0 mlwid=3 mwiddeleg=0x0
[HWISO] ops: sifive,wgchecker2, domain enter dst=domain@0 src=domain@1
[WG] domain_enter dst=domain@0 mlwid=0 mwiddeleg=0xb slwid=0
[HWISO] ops: sifive,wgchecker2, domain exit src=domain@0 dst=domain@1
[WG] domain_exit src=domain@0 dst=domain@1 mlwid=3 mwiddeleg=0x0
[HWISO] ops: sifive,wgchecker2, domain enter dst=domain@1 src=domain@0
[WG] domain_enter dst=domain@1 mlwid=1 mwiddeleg=0xa slwid=1
[HWISO] ops: sifive,wgchecker2, domain exit src=domain@1 dst=root
[WG] domain_exit src=domain@1 dst=root mlwid=3 mwiddeleg=0x0
[HWISO] ops: sifive,wgchecker2, domain enter dst=root src=domain@1
[WG] domain_enter dst=root mlwid=3 mwiddeleg=0x0 slwid=3
[HWISO] ops: sifive,wgchecker2, domain exit src=root dst=domain@0
[WG] domain_exit src=root dst=domain@0 mlwid=3 mwiddeleg=0x0
[HWISO] ops: sifive,wgchecker2, domain enter dst=domain@0 src=root
[WG] domain_enter dst=domain@0 mlwid=0 mwiddeleg=0xb slwid=0
[PASSED] hwiso_domain_switch_test
3 PASSED / 0 FAILED / 3 TOTAL

The hwiso_boot_test confirms that the boot-time WorldGuard setup completed successfully. The hwiso_failure_mode_test then exercises a negative path by intentionally issuing a denied store to a protected DRAM address and confirming that the access raises the expected store access fault, with the faulting address reported in tval. The hwiso_domain_switch_test then exercises domain switching by executing multiple domain transitions, including switches between domain@0, domain@1, and root. For each transition, the logs show the corresponding WorldGuard CSR state being updated, including MLWID, MWIDDELEG, and SLWID. The exit path clears or restores the previous execution context, and the enter path applies the destination domain's WorldGuard context. Together, these logs demonstrate that the hardware isolation runtime hooks both enforce WorldGuard access restrictions and correctly reprogram WorldGuard state during OpenSBI domain context switches.