get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2231966/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2231966,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2231966/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/opensbi/patch/20260501183346.1596027-4-raymondmaoca@gmail.com/",
    "project": {
        "id": 67,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/67/?format=api",
        "name": "OpenSBI development",
        "link_name": "opensbi",
        "list_id": "opensbi.lists.infradead.org",
        "list_email": "opensbi@lists.infradead.org",
        "web_url": "https://github.com/riscv/opensbi",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260501183346.1596027-4-raymondmaoca@gmail.com>",
    "date": "2026-05-01T18:33:46",
    "name": "[RFC,3/3] platform: virt: add QEMU virt WorldGuard hwiso mechanism",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "4dd35aa6b7299484589ad52b23bc793676669481",
    "submitter": {
        "id": 91989,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/91989/?format=api",
        "name": "Raymond Mao",
        "email": "raymondmaoca@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/opensbi/patch/20260501183346.1596027-4-raymondmaoca@gmail.com/mbox/",
    "series": [
        {
            "id": 502489,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/502489/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/opensbi/list/?series=502489",
            "date": "2026-05-01T18:33:43",
            "name": "Add QEMU virt WorldGuard support on top of HWISO",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/502489/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2231966/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2231966/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <opensbi-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n secure) header.d=lists.infradead.org header.i=@lists.infradead.org\n header.a=rsa-sha256 header.s=bombadil.20210309 header.b=2VpVKree;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=OlUgUi1C;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=none (no SPF record) smtp.mailfrom=lists.infradead.org\n (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org;\n envelope-from=opensbi-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from bombadil.infradead.org (bombadil.infradead.org\n [IPv6:2607:7c80:54:3::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g6fp9082dz1yHZ\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 02 May 2026 04:34:25 +1000 (AEST)",
            "from localhost ([::1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1wIsgs-00000007Znv-2LxL;\n\tFri, 01 May 2026 18:34:19 +0000",
            "from mail-qv1-xf34.google.com ([2607:f8b0:4864:20::f34])\n\tby bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1wIsgo-00000007Zly-3JVQ\n\tfor opensbi@lists.infradead.org;\n\tFri, 01 May 2026 18:34:16 +0000",
            "by mail-qv1-xf34.google.com with SMTP id\n 6a1803df08f44-8a016799d2cso23324436d6.1\n        for <opensbi@lists.infradead.org>;\n Fri, 01 May 2026 11:34:14 -0700 (PDT)",
            "from ubuntu.localdomain (172-97-209-197.cpe.distributel.net.\n [172.97.209.197])\n        by smtp.gmail.com with ESMTPSA id\n 6a1803df08f44-8b53c1dceddsm29696886d6.30.2026.05.01.11.34.12\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Fri, 01 May 2026 11:34:12 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20210309; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:\n\tList-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:\n\tMessage-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:\n\tResent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:\n\tList-Owner; bh=26M4SY02aKxfbLF2DccjKoEqVoeIsh1ihC8kUr/kefo=; b=2VpVKreeN1EDfF\n\tYu1WkSqdCcUp5YCbdt4U4KDOW+R0bCihyUJD6usANgX5yU/HrAOWlw/iRunFXQ8qOYANm56p5ieGI\n\tXNKwZdtWzDnCTQcyZCpHlc3kdhUk+IjHwhzd1m4SoZJUT5NsNDuntCYga+sd22zhmfqRBWKFt3BKY\n\tKvYOqoMEBTkb6lsSTDjaqRt6vnOBI298zlw/8pB8CYRAIoLua2OU0E8vR6LLwN3b79XNR1Q4vgGY+\n\tZo/H6SKG6VCUWQA4rqptlDhBFZICDt9noiL7GaiyRJFiVBf2AzbyJj8+koJ/GR4p8bp5DtVuirPI6\n\t5vZbrSFVa1OBUn6Ek2BA==;",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1777660453; x=1778265253;\n darn=lists.infradead.org;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=ErqLecHspcj0Lqx76m2eqo7eizGvHu8LOdTmvNeIqdk=;\n        b=OlUgUi1CyvqQjIWpGJx04E73TVTH8y4DQwLvdBiK/aN30afZbV8+ejCkV7z3NRY3Xz\n         EhaAvcYxHRZ6rios/UlrIIoJqzUnohuxCCIjO1Q/koxb4YfPXF1y5qfzBT9Iy8abXBEP\n         7Opf5kSNzDrklDVbF2OxQKwQSom9DOsurFQInzE4Xj6L9TRi1Ed6oSqv70r5+VcsKNAv\n         D0Ltb9nRvPgv9VNOzk6i1IS8daIKgfEw7t5xS2j9Em5aCiGMhDio1OCYnlaAxjIIN2Fa\n         eGJmFUe+o/7g4lB4Xsh1xK2CQ+yWdx4FYaUdx34wIw+C9wrc1k5bvhiTR0GDKXDVV/Fp\n         XuJA=="
        ],
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1777660453; x=1778265253;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n         :to:cc:subject:date:message-id:reply-to;\n        bh=ErqLecHspcj0Lqx76m2eqo7eizGvHu8LOdTmvNeIqdk=;\n        b=WYEglp/33x0eJvsDMNyiy54gTckzA/PEVe4axd2FaGWZmI4yyvRroy6PPFDN2etS18\n         TNAHV9QsH7LeEA7m9bJ6d3iBaPZoI3KgIB0X7fOd/uV4GmTefDodu7DTO92vJTs7dcbZ\n         JJrRG6C20S0Ip2YYmn0zYQuTLP2SQHHj5G5ItAnU9lEOpVjiBMjfU7hYptZY7Zme4EE6\n         oicM/+q11SaTX41Dl9AxC+rMcjuVi84W6lzj9Y4KuJQ+sT3hmNhY5Ln5GqZjWYZlj29S\n         wmmM9MCd0ru1XUMgYSSWF8Dvg33GUkzdG6/3rQTsIlLLBgHU+BkkRtiWfQLzgte8WOxN\n         OBHw==",
        "X-Gm-Message-State": "AOJu0YzCg7F3WTGWNNxNQpG+5Q1Uvpv4vq1hxbbOyMD4gjM7UtVbYFDP\n\t7Ae5bzyq4FCBW6dwMkAQH9aws/sIdjEqcxRd0gfdYkjLFRjmKZhKzomVJ+MA1mUQ",
        "X-Gm-Gg": "AeBDiet+aTgSMS2X+Tygx4DZHDBI2m9WMlGUQTcR1qW4Me8aPHbvsq7sr2HP/I8IKon\n\tt5CxyA/p8rxt0yznR4XBABTfj3g11M7QK1+DsjcIDwohhq+Wkli/OvCONDlWn2Ub2SkcqECOrE1\n\tHAb+BB4pWfEDZ/FhRyiaYddc5NEtFnh4+OUzItfKbQWAmc4VyQconElU5KIdf9yQ5Md71YLPc2h\n\tzhlHqt7YTP2avCdaqix49F4vPIPzQOcDW5MUwf/eHVIJ6ASekH/SdHF4D7VrpPQNGwfCzLdisJf\n\tp9mTWFLE7vswGA9+0JmFkcKwhqDCHzhyAp3HwXOz0/3+baRJXZNn3QnpbmHMiH2dxTRFwt+LHqq\n\tYT+YheobgKpiHUVkw068odyojKRPBmK53ttmhnREITBugZlAE9YShjSHQOpLhYCwxMdrSNk7c1b\n\tLGChqeKogKQq5sDO1im1BmeKqCRQwNJrWjP8Fa6PsCeR+kKcDQHwMtKfp/7hDwoHdMfDEDd5dIF\n\t8G3htG8VRpsphJ3b6fQMQ==",
        "X-Received": "by 2002:a05:6214:5342:b0:8ac:bb62:fe5a with SMTP id\n 6a1803df08f44-8b66892ab60mr11143966d6.33.1777660453142;\n        Fri, 01 May 2026 11:34:13 -0700 (PDT)",
        "From": "Raymond Mao <raymondmaoca@gmail.com>",
        "To": "opensbi@lists.infradead.org",
        "Cc": "scott@riscstar.com,\n\tdave.patel@riscstar.com,\n\traymond.mao@riscstar.com,\n\trobin.randhawa@sifive.com,\n\tsamuel.holland@sifive.com,\n\tanup.patel@qti.qualcomm.com,\n\tanuppate@qti.qualcomm.com,\n\tanup@brainfault.org,\n\tdhaval@rivosinc.com,\n\tpeter.lin@sifive.com",
        "Subject": "[RFC PATCH 3/3] platform: virt: add QEMU virt WorldGuard hwiso\n mechanism",
        "Date": "Fri,  1 May 2026 14:33:46 -0400",
        "Message-Id": "<20260501183346.1596027-4-raymondmaoca@gmail.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20260501183346.1596027-1-raymondmaoca@gmail.com>",
        "References": "<20260501183346.1596027-1-raymondmaoca@gmail.com>",
        "MIME-Version": "1.0",
        "X-CRM114-Version": "20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ",
        "X-CRM114-CacheID": "sfid-20260501_113415_344743_8CA77249 ",
        "X-CRM114-Status": "GOOD (  24.11  )",
        "X-Spam-Score": "-2.1 (--)",
        "X-Spam-Report": "Spam detection software,\n running on the system \"bombadil.infradead.org\",\n has NOT identified this incoming email as spam.  The original\n message has been attached to this so you can view it or label\n similar future email.  If you have any questions, see\n the administrator of that system for details.\n Content preview:  From: Raymond Mao Implement the QEMU virt WorldGuard HWISO\n    mechanism. Parse checker subordinates and resource permissions from the\n FDT,\n    program wgChecker MMIO state at boot,\n parse per-hart CPU defaults plus per-domain\n    [...]\n Content analysis details:   (-2.1 points, 5.0 required)\n  pts rule name              description\n ---- ----------------------\n --------------------------------------------------\n -0.0 RCVD_IN_DNSWL_NONE     RBL: Sender listed at https://www.dnswl.org/, no\n                             trust\n                             [2607:f8b0:4864:20:0:0:0:f34 listed in]\n                             [list.dnswl.org]\n -0.0 SPF_PASS               SPF: sender matches SPF record\n  0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record\n -0.1 DKIM_VALID_EF          Message has a valid DKIM or DK signature from\n                             envelope-from domain\n -0.1 DKIM_VALID_AU          Message has a valid DKIM or DK signature from\n author's\n                             domain\n  0.1 DKIM_SIGNED            Message has a DKIM or DK signature,\n not necessarily valid\n -0.1 DKIM_VALID             Message has at least one valid DKIM or DK\n signature\n -1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%\n                             [score: 0.0000]\n  0.0 FREEMAIL_FROM          Sender email is commonly abused enduser mail\n provider\n                             [raymondmaoca(at)gmail.com]",
        "X-BeenThere": "opensbi@lists.infradead.org",
        "X-Mailman-Version": "2.1.34",
        "Precedence": "list",
        "List-Id": "<opensbi.lists.infradead.org>",
        "List-Unsubscribe": "<http://lists.infradead.org/mailman/options/opensbi>,\n <mailto:opensbi-request@lists.infradead.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.infradead.org/pipermail/opensbi/>",
        "List-Post": "<mailto:opensbi@lists.infradead.org>",
        "List-Help": "<mailto:opensbi-request@lists.infradead.org?subject=help>",
        "List-Subscribe": "<http://lists.infradead.org/mailman/listinfo/opensbi>,\n <mailto:opensbi-request@lists.infradead.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "\"opensbi\" <opensbi-bounces@lists.infradead.org>",
        "Errors-To": "opensbi-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org"
    },
    "content": "From: Raymond Mao <raymond.mao@riscstar.com>\n\nImplement the QEMU virt WorldGuard HWISO mechanism. Parse checker\nsubordinates and resource permissions from the FDT, program wgChecker\nMMIO state at boot, parse per-hart CPU defaults plus per-domain\nWorldGuard metadata, and switch MLWID, MWIDDELEG and SLWID on domain\ntransitions.\n\nSigned-off-by: Raymond Mao <raymond.mao@riscstar.com>\n---\n platform/generic/include/qemu_virt_wg.h     |   60 ++\n platform/generic/objects.mk                 |    1 +\n platform/generic/platform.c                 |   11 +\n platform/generic/virt/qemu_virt_wgchecker.c | 1050 +++++++++++++++++++\n 4 files changed, 1122 insertions(+)\n create mode 100644 platform/generic/include/qemu_virt_wg.h\n create mode 100644 platform/generic/virt/qemu_virt_wgchecker.c",
    "diff": "diff --git a/platform/generic/include/qemu_virt_wg.h b/platform/generic/include/qemu_virt_wg.h\nnew file mode 100644\nindex 00000000..c1685c0c\n--- /dev/null\n+++ b/platform/generic/include/qemu_virt_wg.h\n@@ -0,0 +1,60 @@\n+/*\n+ * SPDX-License-Identifier: BSD-2-Clause\n+ *\n+ * Copyright (c) 2026 RISCstar Solutions Corporation.\n+ *\n+ * Author: Raymond Mao <raymond.mao@riscstar.com>\n+ */\n+\n+#ifndef __QEMU_VIRT_WG_H__\n+#define __QEMU_VIRT_WG_H__\n+\n+#include <sbi/sbi_types.h>\n+\n+/*\n+ * QEMU virt WorldGuard model definitions\n+ */\n+#define QEMU_VIRT_WG_COMPAT\t\t\t\"sifive,wgchecker2\"\n+#define QEMU_VIRT_WG_CPU_COMPAT\t\t\t\"riscv,wgcpu\"\n+#define QEMU_VIRT_WG_CPU_NODE\t\t\t\"worldguard\"\n+#define QEMU_VIRT_WG_CFG_NODE\t\t\t\"worldguard_cfg\"\n+\n+#define QEMU_VIRT_WG_PROP_SLOT_COUNT\t\t\"sifive,slot-count\"\n+#define QEMU_VIRT_WG_PROP_SUBORDINATES\t\t\"sifive,subordinates\"\n+#define QEMU_VIRT_WG_PROP_WID\t\t\t\"worldguard,wid\"\n+#define QEMU_VIRT_WG_PROP_WIDLIST\t\t\"worldguard,widlist\"\n+#define QEMU_VIRT_WG_PROP_MWID\t\t\t\"mwid\"\n+#define QEMU_VIRT_WG_PROP_MWIDLIST\t\t\"mwidlist\"\n+#define QEMU_VIRT_WG_PROP_PERMS\t\t\t\"perms\"\n+\n+/*\n+ * The current QEMU wgChecker model uses a 64-bit permission register with\n+ * 2 bits per world, so the current software model tracks at most 32 WIDs.\n+ */\n+#define QEMU_VIRT_WG_MAX_WIDS\t\t\t32\n+\n+/* The current QEMU wgChecker model requires 4 KiB slot alignment. */\n+#define QEMU_VIRT_WG_MIN_ALIGN\t\t\t0x1000ULL\n+\n+/* Current QEMU wgChecker MMIO register layout. */\n+#define QEMU_VIRT_WG_MMIO_NSLOTS\t\t0x008\n+#define QEMU_VIRT_WG_MMIO_ERRCAUSE\t\t0x010\n+#define QEMU_VIRT_WG_MMIO_ERRADDR\t\t0x018\n+#define QEMU_VIRT_WG_MMIO_SLOT_BASE\t\t0x020\n+#define QEMU_VIRT_WG_MMIO_SLOT_STRIDE\t\t0x020\n+#define QEMU_VIRT_WG_MMIO_SLOT_ADDR\t\t0x000\n+#define QEMU_VIRT_WG_MMIO_SLOT_PERM\t\t0x008\n+#define QEMU_VIRT_WG_MMIO_SLOT_CFG\t\t0x010\n+\n+/* Current QEMU wgChecker slot cfg.A[1:0] encoding. */\n+#define QEMU_VIRT_WG_SLOT_CFG_A_MASK\t\t0x3\n+#define QEMU_VIRT_WG_SLOT_CFG_A_OFF\t\t0x0\n+#define QEMU_VIRT_WG_SLOT_CFG_A_TOR\t\t0x1\n+\n+struct qemu_virt_wg_range {\n+\tu64 base;\n+\tu64 size;\n+\tu64 perm;\n+};\n+\n+#endif\ndiff --git a/platform/generic/objects.mk b/platform/generic/objects.mk\nindex 85aa723a..ebca6940 100644\n--- a/platform/generic/objects.mk\n+++ b/platform/generic/objects.mk\n@@ -20,6 +20,7 @@ platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \\\n # Objects to build\n platform-objs-y += platform.o\n platform-objs-y += platform_override_modules.o\n+platform-objs-y += virt/qemu_virt_wgchecker.o\n \n # Blobs to build\n FW_TEXT_START=0x80000000\ndiff --git a/platform/generic/platform.c b/platform/generic/platform.c\nindex b76c2a2f..5f2bc1e4 100644\n--- a/platform/generic/platform.c\n+++ b/platform/generic/platform.c\n@@ -11,6 +11,7 @@\n #include <platform_override.h>\n #include <sbi/riscv_asm.h>\n #include <sbi/sbi_bitops.h>\n+#include <sbi/sbi_error.h>\n #include <sbi/sbi_hartmask.h>\n #include <sbi/sbi_heap.h>\n #include <sbi/sbi_platform.h>\n@@ -30,6 +31,8 @@\n #include <sbi_utils/rpxy/fdt_rpxy.h>\n #include <sbi_utils/serial/semihosting.h>\n \n+extern int qemu_virt_hwiso_register(void *fdt);\n+\n /* List of platform override modules generated at compile time */\n extern const struct platform_override *platform_override_modules[];\n extern unsigned long platform_override_modules_size;\n@@ -222,9 +225,17 @@ static int generic_nascent_init(void)\n \n static int generic_early_init(bool cold_boot)\n {\n+\tint rc;\n+\n \tif (cold_boot)\n \t\tfdt_reset_init();\n \n+\tif (cold_boot) {\n+\t\trc = qemu_virt_hwiso_register(fdt_get_address());\n+\t\tif (rc && rc != SBI_EALREADY)\n+\t\t\treturn rc;\n+\t}\n+\n \tif (!generic_plat || !generic_plat->early_init)\n \t\treturn 0;\n \ndiff --git a/platform/generic/virt/qemu_virt_wgchecker.c b/platform/generic/virt/qemu_virt_wgchecker.c\nnew file mode 100644\nindex 00000000..063fcecb\n--- /dev/null\n+++ b/platform/generic/virt/qemu_virt_wgchecker.c\n@@ -0,0 +1,1050 @@\n+/*\n+ * SPDX-License-Identifier: BSD-2-Clause\n+ *\n+ * QEMU virt WorldGuard hardware isolation support\n+ *\n+ * Copyright (c) 2026 RISCstar Solutions Corporation.\n+ *\n+ * Author: Raymond Mao <raymond.mao@riscstar.com>\n+ */\n+\n+#include <libfdt.h>\n+#include <sbi/riscv_asm.h>\n+#include <sbi/riscv_encoding.h>\n+#include <sbi/riscv_io.h>\n+#include <sbi/sbi_console.h>\n+#include <sbi/sbi_domain.h>\n+#include <sbi/sbi_error.h>\n+#include <sbi/sbi_hart.h>\n+#include <sbi/sbi_hartmask.h>\n+#include <sbi/sbi_heap.h>\n+#include <sbi/sbi_hwiso.h>\n+#include <sbi/sbi_scratch.h>\n+#include <sbi/sbi_string.h>\n+#include <qemu_virt_wg.h>\n+#include <sbi_utils/fdt/fdt_helper.h>\n+\n+struct wg_checker {\n+\tchar name[32];\n+\tu64 mmio_base;\n+\tu64 mmio_size;\n+\tu32 slot_count;\n+\tu32 subordinate_count;\n+\tbool full_checker_rule;\n+\tu64 full_checker_perm;\n+\tu32 range_count;\n+\tstruct qemu_virt_wg_range *ranges;\n+};\n+\n+struct wg_cpu_defaults {\n+\tu32 trusted_wid;\n+\tu32 nworlds;\n+\tu32 valid_wid_mask;\n+};\n+\n+struct wg_platform_ctx {\n+\tu32 checker_count;\n+\tu32 hart_count;\n+\tbool checker_enabled;\n+\tbool runtime_enabled;\n+\tstruct wg_checker *checkers;\n+\tstruct wg_cpu_defaults *hart_defaults;\n+};\n+\n+struct wg_domain_ctx {\n+\tbool has_wid;\n+\tu32 wid;\n+\tu32 widlist_count;\n+\tu32 widlist_mask;\n+\tu32 widlist[QEMU_VIRT_WG_MAX_WIDS];\n+};\n+\n+static struct wg_platform_ctx *wg_platform;\n+\n+static void wg_free_platform_ctx(struct wg_platform_ctx *platform)\n+{\n+\tu32 i;\n+\n+\tif (!platform)\n+\t\treturn;\n+\n+\tfor (i = 0; i < platform->checker_count; i++)\n+\t\tsbi_free(platform->checkers[i].ranges);\n+\n+\tsbi_free(platform->checkers);\n+\tsbi_free(platform->hart_defaults);\n+\tsbi_free(platform);\n+}\n+\n+static bool wg_runtime_enabled(void)\n+{\n+\treturn wg_platform && wg_platform->runtime_enabled;\n+}\n+\n+static u64 wg_read_cells(const fdt32_t *cells, int count)\n+{\n+\tu64 val = 0;\n+\tint i;\n+\n+\tfor (i = 0; i < count; i++)\n+\t\tval = (val << 32) | fdt32_to_cpu(cells[i]);\n+\n+\treturn val;\n+}\n+\n+static void wg_write64(u64 addr, u64 val)\n+{\n+#if __riscv_xlen != 32\n+\twriteq(val, (void *)(unsigned long)addr);\n+#else\n+\twritel((u32)val, (void *)(unsigned long)addr);\n+\twritel((u32)(val >> 32), (void *)(unsigned long)(addr + 4));\n+#endif\n+}\n+\n+static void wg_write32(u64 addr, u32 val)\n+{\n+\twritel(val, (void *)(unsigned long)addr);\n+}\n+\n+static u64 wg_slot_addr_encode(u64 addr)\n+{\n+\treturn addr >> 2;\n+}\n+\n+static u64 wg_wid_mask(u32 wid)\n+{\n+\treturn (wid < 32) ? (1ULL << wid) : 0;\n+}\n+\n+static bool wg_range_is_aligned(u64 base, u64 size)\n+{\n+\tif (!size)\n+\t\treturn false;\n+\n+\tif (base & (QEMU_VIRT_WG_MIN_ALIGN - 1))\n+\t\treturn false;\n+\tif (size & (QEMU_VIRT_WG_MIN_ALIGN - 1))\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static void wg_sort_ranges(struct wg_checker *checker)\n+{\n+\tstruct qemu_virt_wg_range tmp;\n+\tu32 i, j;\n+\n+\tfor (i = 1; i < checker->range_count; i++) {\n+\t\ttmp = checker->ranges[i];\n+\t\tj = i;\n+\t\twhile (j > 0 && checker->ranges[j - 1].base > tmp.base) {\n+\t\t\tchecker->ranges[j] = checker->ranges[j - 1];\n+\t\t\tj--;\n+\t\t}\n+\t\tchecker->ranges[j] = tmp;\n+\t}\n+}\n+\n+static int wg_compact_ranges(struct wg_checker *checker)\n+{\n+\tstruct qemu_virt_wg_range *prev, *cur;\n+\tu64 prev_end, cur_end;\n+\tu32 i, out = 0;\n+\n+\tif (!checker->range_count)\n+\t\treturn 0;\n+\n+\twg_sort_ranges(checker);\n+\n+\tfor (i = 0; i < checker->range_count; i++) {\n+\t\tcur = &checker->ranges[i];\n+\t\tcur_end = cur->base + cur->size;\n+\t\tif (cur_end <= cur->base)\n+\t\t\treturn SBI_EINVAL;\n+\n+\t\tif (!out) {\n+\t\t\tchecker->ranges[out++] = *cur;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tprev = &checker->ranges[out - 1];\n+\t\tprev_end = prev->base + prev->size;\n+\t\tif (cur->base < prev_end)\n+\t\t\treturn SBI_EINVAL;\n+\n+\t\tif (cur->base == prev_end && cur->perm == prev->perm) {\n+\t\t\tprev->size += cur->size;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tchecker->ranges[out++] = *cur;\n+\t}\n+\n+\tchecker->range_count = out;\n+\treturn 0;\n+}\n+\n+static int wg_get_reg_cells(void *fdt, int resource_node,\n+\t\t\t      int *addr_cells, int *size_cells)\n+{\n+\tint parent;\n+\n+\tparent = fdt_parent_offset(fdt, resource_node);\n+\tif (parent < 0)\n+\t\treturn SBI_EINVAL;\n+\n+\t*addr_cells = fdt_address_cells(fdt, parent);\n+\t*size_cells = fdt_size_cells(fdt, parent);\n+\tif (*addr_cells <= 0 || *addr_cells > 2 || *size_cells <= 0 ||\n+\t    *size_cells > 2)\n+\t\treturn SBI_EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int wg_count_reg_entries(void *fdt, int resource_node, int reg_node)\n+{\n+\tconst fdt32_t *reg;\n+\tint addr_cells, size_cells, entry_cells, len, rc;\n+\n+\trc = wg_get_reg_cells(fdt, resource_node, &addr_cells, &size_cells);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treg = fdt_getprop(fdt, reg_node, \"reg\", &len);\n+\tif (!reg || len <= 0)\n+\t\treturn 0;\n+\n+\tentry_cells = addr_cells + size_cells;\n+\tif (len % (entry_cells * (int)sizeof(fdt32_t)))\n+\t\treturn SBI_EINVAL;\n+\n+\treturn len / (entry_cells * (int)sizeof(fdt32_t));\n+}\n+\n+static int wg_parse_perms(void *fdt, int cfg_node, u64 **out_perms,\n+\t\t\t   u32 *out_count)\n+{\n+\tconst fdt32_t *perms;\n+\tu64 *vals;\n+\tint len, i, count;\n+\n+\t*out_perms = NULL;\n+\t*out_count = 0;\n+\n+\tperms = fdt_getprop(fdt, cfg_node, QEMU_VIRT_WG_PROP_PERMS, &len);\n+\tif (!perms || len <= 0)\n+\t\treturn 0;\n+\n+\t/* QEMU virt WG permissions are always encoded as 64-bit <hi lo> cells. */\n+\tif (len % (2 * (int)sizeof(fdt32_t)))\n+\t\treturn SBI_EINVAL;\n+\n+\tcount = len / (2 * (int)sizeof(fdt32_t));\n+\tvals = sbi_calloc(sizeof(*vals), count);\n+\tif (!vals)\n+\t\treturn SBI_ENOMEM;\n+\n+\tfor (i = 0; i < count; i++, perms += 2)\n+\t\tvals[i] = wg_read_cells(perms, 2);\n+\n+\t*out_perms = vals;\n+\t*out_count = count;\n+\treturn 0;\n+}\n+\n+static int wg_fill_ranges(void *fdt, int resource_node, int reg_node,\n+\t\t\t  const u64 *perms, u32 perm_count,\n+\t\t\t  struct qemu_virt_wg_range *ranges, u32 range_count)\n+{\n+\tconst fdt32_t *reg;\n+\tu64 base, size;\n+\tint addr_cells, size_cells, entry_cells, len, i, rc;\n+\n+\trc = wg_get_reg_cells(fdt, resource_node, &addr_cells, &size_cells);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treg = fdt_getprop(fdt, reg_node, \"reg\", &len);\n+\tif (!reg || len <= 0)\n+\t\treturn SBI_EINVAL;\n+\n+\tentry_cells = addr_cells + size_cells;\n+\tfor (i = 0; i < (int)range_count; i++, reg += entry_cells) {\n+\t\tbase = wg_read_cells(reg, addr_cells);\n+\t\tsize = wg_read_cells(reg + addr_cells, size_cells);\n+\t\tif (!wg_range_is_aligned(base, size))\n+\t\t\treturn SBI_EINVAL;\n+\n+\t\tranges[i].base = base;\n+\t\tranges[i].size = size;\n+\t\tranges[i].perm = perms[(perm_count == 1) ? 0 : i];\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int wg_parse_checker_rules(void *fdt, int checker_node,\n+\t\t\t\t  struct wg_checker *checker)\n+{\n+\tconst fdt32_t *subs;\n+\tu64 *perms = NULL;\n+\tint cfg_node, len, i, rc = 0, reg_count;\n+\tu32 perm_count = 0;\n+\tint child;\n+\n+\tsubs = fdt_getprop(fdt, checker_node,\n+\t\t\t   QEMU_VIRT_WG_PROP_SUBORDINATES, &len);\n+\tif (!subs || len <= 0)\n+\t\treturn 0;\n+\tif (len % (int)sizeof(fdt32_t))\n+\t\tgoto err;\n+\n+\tchecker->subordinate_count = len / sizeof(fdt32_t);\n+\tif (!checker->slot_count)\n+\t\tgoto err;\n+\n+\tchecker->ranges = sbi_calloc(sizeof(*checker->ranges),\n+\t\t\t\t     checker->slot_count);\n+\tif (!checker->ranges)\n+\t\treturn SBI_ENOMEM;\n+\n+\tfor (i = 0; i < checker->subordinate_count; i++) {\n+\t\tchild = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(subs[i]));\n+\t\tif (child < 0) {\n+\t\t\tsbi_printf(\"[WG] checker %s has invalid subordinate\"\n+\t\t\t\t   \" phandle[%d]=0x%x err=%d\\n\",\n+\t\t\t\t   checker->name, i, fdt32_to_cpu(subs[i]),\n+\t\t\t\t   child);\n+\t\t\trc = child;\n+\t\t\tgoto err;\n+\t\t}\n+\n+\t\tcfg_node = fdt_subnode_offset(fdt, child,\n+\t\t\t\t\t      QEMU_VIRT_WG_CFG_NODE);\n+\t\tif (cfg_node < 0)\n+\t\t\tcontinue;\n+\n+\t\trc = wg_parse_perms(fdt, cfg_node, &perms, &perm_count);\n+\t\tif (rc)\n+\t\t\tgoto err;\n+\t\tif (!perm_count)\n+\t\t\tcontinue;\n+\n+\t\treg_count = wg_count_reg_entries(fdt, child, cfg_node);\n+\t\tif (reg_count < 0)\n+\t\t\tgoto err;\n+\n+\t\tif (!reg_count && checker->subordinate_count == 1 &&\n+\t\t    perm_count == 1) {\n+\t\t\tif (checker->range_count)\n+\t\t\t\tgoto err;\n+\t\t\tchecker->full_checker_rule = true;\n+\t\t\tchecker->full_checker_perm = perms[0];\n+\t\t\tsbi_free(perms);\n+\t\t\tperms = NULL;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!reg_count)\n+\t\t\treg_count = wg_count_reg_entries(fdt, child, child);\n+\t\tif (reg_count <= 0)\n+\t\t\tgoto err;\n+\n+\t\tif (perm_count != 1 && perm_count != (u32)reg_count)\n+\t\t\tgoto err;\n+\t\tif (checker->full_checker_rule)\n+\t\t\tgoto err;\n+\t\tif (checker->range_count + reg_count > checker->slot_count)\n+\t\t\tgoto err;\n+\n+\t\trc = wg_fill_ranges(fdt, child,\n+\t\t\t\t     (fdt_getprop(fdt, cfg_node, \"reg\", NULL) ?\n+\t\t\t\t      cfg_node : child),\n+\t\t\t\t     perms, perm_count,\n+\t\t\t\t     &checker->ranges[checker->range_count],\n+\t\t\t\t     reg_count);\n+\t\tsbi_free(perms);\n+\t\tperms = NULL;\n+\t\tif (rc)\n+\t\t\tgoto err;\n+\n+\t\tchecker->range_count += reg_count;\n+\t}\n+\n+\tif (checker->full_checker_rule)\n+\t\treturn 0;\n+\n+\treturn wg_compact_ranges(checker);\n+\n+err:\n+\tsbi_free(perms);\n+\treturn rc ? rc : SBI_EINVAL;\n+}\n+\n+static int wg_parse_checker(void *fdt, int checker_node,\n+\t\t\t    struct wg_checker *checker)\n+{\n+\tconst fdt32_t *val;\n+\tu64 base = 0, size = 0;\n+\tint len, rc;\n+\n+\trc = fdt_get_node_addr_size(fdt, checker_node, 0, &base, &size);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tval = fdt_getprop(fdt, checker_node,\n+\t\t\t  QEMU_VIRT_WG_PROP_SLOT_COUNT, &len);\n+\tif (!val || len < (int)sizeof(fdt32_t))\n+\t\treturn SBI_EINVAL;\n+\n+\tchecker->mmio_base = base;\n+\tchecker->mmio_size = size;\n+\tchecker->slot_count = fdt32_to_cpu(val[0]);\n+\tsbi_snprintf(checker->name, sizeof(checker->name), \"%s\",\n+\t\t     fdt_get_name(fdt, checker_node, NULL));\n+\n+\treturn wg_parse_checker_rules(fdt, checker_node, checker);\n+}\n+\n+static void wg_program_clear_slots(const struct wg_checker *checker)\n+{\n+\tu32 slot;\n+\n+\tfor (slot = 1; slot < checker->slot_count; slot++) {\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_ADDR, 0);\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM, 0);\n+\t\twg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG, 0);\n+\t}\n+\n+}\n+\n+static void wg_program_clear_last_slot(const struct wg_checker *checker)\n+{\n+\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t   checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM, 0);\n+\twg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t   checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG, 0);\n+}\n+\n+static void wg_program_clear_slots_from(const struct wg_checker *checker,\n+\t\t\t\t\tu32 first_slot)\n+{\n+\tu32 slot;\n+\n+\tif (first_slot >= checker->slot_count)\n+\t\treturn;\n+\n+\tfor (slot = first_slot; slot < checker->slot_count; slot++) {\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_ADDR, 0);\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM, 0);\n+\t\twg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG, 0);\n+\t}\n+}\n+\n+static int wg_program_checker(const struct wg_checker *checker)\n+{\n+\tu64 prev_end = 0;\n+\tu32 required_slots = 0, slot = 1, i;\n+\n+\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_ERRCAUSE, 0);\n+\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_ERRADDR, 0);\n+\n+\tif (checker->full_checker_rule) {\n+\t\twg_program_clear_slots(checker);\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM,\n+\t\t\t   checker->full_checker_perm);\n+\t\twg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG,\n+\t\t\t   QEMU_VIRT_WG_SLOT_CFG_A_TOR);\n+\t\treturn 0;\n+\t}\n+\n+\tfor (i = 0; i < checker->range_count; i++) {\n+\t\tif (!i || checker->ranges[i].base != prev_end)\n+\t\t\trequired_slots++;\n+\t\trequired_slots++;\n+\t\tprev_end = checker->ranges[i].base + checker->ranges[i].size;\n+\t}\n+\n+\tif (required_slots > checker->slot_count - 1)\n+\t\treturn SBI_EINVAL;\n+\n+\tprev_end = 0;\n+\tfor (i = 0; i < checker->range_count; i++) {\n+\t\tconst struct qemu_virt_wg_range *range = &checker->ranges[i];\n+\t\tu64 end = range->base + range->size;\n+\n+\t\tif (!i || range->base != prev_end) {\n+\t\t\twg_write64(checker->mmio_base +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_ADDR,\n+\t\t\t\t   wg_slot_addr_encode(range->base));\n+\t\t\twg_write64(checker->mmio_base +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM, 0);\n+\t\t\twg_write32(checker->mmio_base +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG,\n+\t\t\t\t   QEMU_VIRT_WG_SLOT_CFG_A_OFF);\n+\t\t\tslot++;\n+\t\t}\n+\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_ADDR,\n+\t\t\t   wg_slot_addr_encode(end));\n+\t\twg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_PERM,\n+\t\t\t   range->perm);\n+\t\twg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE +\n+\t\t\t   slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE +\n+\t\t\t   QEMU_VIRT_WG_MMIO_SLOT_CFG,\n+\t\t\t   QEMU_VIRT_WG_SLOT_CFG_A_TOR);\n+\t\tprev_end = end;\n+\t\tslot++;\n+\t}\n+\n+\t/*\n+\t * Keep the reset-time trusted-WID bypass slot alive until the new\n+\t * rule set is fully programmed, otherwise the DRAM checker can deny\n+\t * OpenSBI's own RAM accesses mid-update.\n+\t */\n+\twg_program_clear_slots_from(checker, slot);\n+\twg_program_clear_last_slot(checker);\n+\n+\treturn 0;\n+}\n+\n+static void wg_free_platform(void)\n+{\n+\tif (!wg_platform)\n+\t\treturn;\n+\n+\twg_free_platform_ctx(wg_platform);\n+\twg_platform = NULL;\n+}\n+\n+static void wg_init_cpu_defaults(struct wg_platform_ctx *platform)\n+{\n+\tu32 i;\n+\n+\tif (!platform || !platform->hart_defaults)\n+\t\treturn;\n+\n+\tfor (i = 0; i < platform->hart_count; i++) {\n+\t\tplatform->hart_defaults[i].trusted_wid = 0;\n+\t\tplatform->hart_defaults[i].nworlds = 1;\n+\t\tplatform->hart_defaults[i].valid_wid_mask = 0x1;\n+\t}\n+}\n+\n+static int wg_parse_wid_prop(void *fdt, int node, const char *prop_name,\n+\t\t\t     u32 *out_wid)\n+{\n+\tconst fdt32_t *prop;\n+\tint len;\n+\n+\tif (!out_wid)\n+\t\treturn SBI_EINVAL;\n+\n+\tprop = fdt_getprop(fdt, node, prop_name, &len);\n+\tif (!prop)\n+\t\treturn SBI_ENOENT;\n+\tif (len != (int)sizeof(fdt32_t))\n+\t\treturn SBI_EINVAL;\n+\n+\t*out_wid = fdt32_to_cpu(prop[0]);\n+\tif (*out_wid >= QEMU_VIRT_WG_MAX_WIDS)\n+\t\treturn SBI_EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int wg_parse_widlist(void *fdt, int node, const char *prop_name,\n+\t\t\t    u32 *out_mask, u32 *out_wids, u32 *out_count)\n+{\n+\tconst fdt32_t *prop;\n+\tu32 mask = 0, count = 0, wid;\n+\tint len, i;\n+\n+\tif (!out_mask || !out_count)\n+\t\treturn SBI_EINVAL;\n+\n+\t*out_mask = 0;\n+\t*out_count = 0;\n+\n+\tprop = fdt_getprop(fdt, node, prop_name, &len);\n+\tif (!prop)\n+\t\treturn 0;\n+\tif (len < 0 || (len % (int)sizeof(fdt32_t)))\n+\t\treturn SBI_EINVAL;\n+\n+\tcount = len / sizeof(fdt32_t);\n+\tif (count > QEMU_VIRT_WG_MAX_WIDS)\n+\t\treturn SBI_EINVAL;\n+\n+\tfor (i = 0; i < (int)count; i++) {\n+\t\twid = fdt32_to_cpu(prop[i]);\n+\t\tif (wid >= QEMU_VIRT_WG_MAX_WIDS)\n+\t\t\treturn SBI_EINVAL;\n+\t\tif (mask & wg_wid_mask(wid))\n+\t\t\treturn SBI_EINVAL;\n+\n+\t\tmask |= wg_wid_mask(wid);\n+\t\tif (out_wids)\n+\t\t\tout_wids[i] = wid;\n+\t}\n+\n+\t*out_mask = mask;\n+\t*out_count = count;\n+\treturn 0;\n+}\n+\n+static int wg_parse_cpu_defaults(void *fdt, struct wg_platform_ctx *platform)\n+{\n+\tstruct wg_cpu_defaults *cpu_defaults;\n+\tu32 hartid, hartindex, max_wid, widlist_count;\n+\tint cpus_offset, cpu_offset, wgcpu, rc;\n+\n+\tif (!fdt || !platform || !platform->hart_defaults)\n+\t\treturn 0;\n+\n+\tcpus_offset = fdt_path_offset(fdt, \"/cpus\");\n+\tif (cpus_offset < 0)\n+\t\treturn 0;\n+\n+\tfdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {\n+\t\tif (fdt_parse_hart_id(fdt, cpu_offset, &hartid))\n+\t\t\tcontinue;\n+\n+\t\thartindex = sbi_hartid_to_hartindex(hartid);\n+\t\tif (!sbi_hartindex_valid(hartindex) ||\n+\t\t    hartindex >= platform->hart_count)\n+\t\t\tcontinue;\n+\n+\t\twgcpu = fdt_subnode_offset(fdt, cpu_offset,\n+\t\t\t\t\t   QEMU_VIRT_WG_CPU_NODE);\n+\t\tif (wgcpu < 0 || fdt_node_check_compatible(\n+\t\t\t\t\t fdt, wgcpu, QEMU_VIRT_WG_CPU_COMPAT))\n+\t\t\tcontinue;\n+\n+\t\tcpu_defaults = &platform->hart_defaults[hartindex];\n+\t\trc = wg_parse_wid_prop(fdt, wgcpu, QEMU_VIRT_WG_PROP_MWID,\n+\t\t\t\t       &cpu_defaults->trusted_wid);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tmax_wid = cpu_defaults->trusted_wid;\n+\t\trc = wg_parse_widlist(fdt, wgcpu, QEMU_VIRT_WG_PROP_MWIDLIST,\n+\t\t\t\t      &cpu_defaults->valid_wid_mask, NULL,\n+\t\t\t\t      &widlist_count);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tcpu_defaults->valid_wid_mask |=\n+\t\t\twg_wid_mask(cpu_defaults->trusted_wid);\n+\t\tif (cpu_defaults->valid_wid_mask) {\n+\t\t\tu32 wid;\n+\n+\t\t\tfor (wid = 0; wid < QEMU_VIRT_WG_MAX_WIDS; wid++) {\n+\t\t\t\tif (cpu_defaults->valid_wid_mask & (1U << wid))\n+\t\t\t\t\tmax_wid = wid;\n+\t\t\t}\n+\t\t}\n+\n+\t\tcpu_defaults->nworlds = max_wid + 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static bool wg_has_cpu_runtime(void *fdt)\n+{\n+\tu32 hartid;\n+\tint cpus_offset, cpu_offset, wgcpu;\n+\n+\tif (!fdt)\n+\t\treturn false;\n+\n+\tcpus_offset = fdt_path_offset(fdt, \"/cpus\");\n+\tif (cpus_offset < 0)\n+\t\treturn false;\n+\n+\tfdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {\n+\t\tif (fdt_parse_hart_id(fdt, cpu_offset, &hartid))\n+\t\t\tcontinue;\n+\n+\t\twgcpu = fdt_subnode_offset(fdt, cpu_offset,\n+\t\t\t\t\t   QEMU_VIRT_WG_CPU_NODE);\n+\t\tif (wgcpu < 0)\n+\t\t\tcontinue;\n+\t\tif (fdt_node_check_compatible(fdt, wgcpu,\n+\t\t\t\t\t      QEMU_VIRT_WG_CPU_COMPAT))\n+\t\t\tcontinue;\n+\n+\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+static u32 wg_count_platform_checkers(void *fdt)\n+{\n+\tint checker_node;\n+\tu32 count = 0;\n+\n+\tif (!fdt)\n+\t\treturn 0;\n+\n+\tchecker_node = -1;\n+\twhile (true) {\n+\t\tchecker_node = fdt_node_offset_by_compatible(\n+\t\t\tfdt, checker_node, QEMU_VIRT_WG_COMPAT);\n+\t\tif (checker_node < 0)\n+\t\t\tbreak;\n+\t\tif (fdt_getprop(fdt, checker_node,\n+\t\t\t\tQEMU_VIRT_WG_PROP_SUBORDINATES, NULL))\n+\t\t\tcount++;\n+\t}\n+\n+\treturn count;\n+}\n+\n+static int wg_validate_domain_ctx(const struct sbi_domain *dom,\n+\t\t\t\t  const struct wg_domain_ctx *ctx)\n+{\n+\tconst struct wg_cpu_defaults *cpu_defaults;\n+\tu32 hartindex;\n+\n+\tif (!wg_platform || !dom || !ctx || dom == &root || !dom->possible_harts)\n+\t\treturn 0;\n+\n+\tfor (hartindex = 0; hartindex < wg_platform->hart_count; hartindex++) {\n+\t\tif (!sbi_hartmask_test_hartindex(hartindex, dom->possible_harts))\n+\t\t\tcontinue;\n+\n+\t\tcpu_defaults = &wg_platform->hart_defaults[hartindex];\n+\t\tif (!(cpu_defaults->valid_wid_mask & wg_wid_mask(ctx->wid)))\n+\t\t\treturn SBI_EINVAL;\n+\t\tif (ctx->widlist_mask & ~cpu_defaults->valid_wid_mask)\n+\t\t\treturn SBI_EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct wg_cpu_defaults *wg_current_cpu_defaults(void)\n+{\n+\tu32 hartindex;\n+\n+\tif (!wg_platform || !wg_platform->hart_defaults)\n+\t\treturn NULL;\n+\n+\thartindex = sbi_hartid_to_hartindex(current_hartid());\n+\tif (!sbi_hartindex_valid(hartindex) ||\n+\t    hartindex >= wg_platform->hart_count)\n+\t\treturn NULL;\n+\n+\treturn &wg_platform->hart_defaults[hartindex];\n+}\n+\n+static void wg_program_wid_state(u32 mlwid, u32 mwiddeleg, u32 slwid)\n+{\n+\tstruct sbi_scratch *scratch = sbi_scratch_thishart_ptr();\n+\n+\tif (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG))\n+\t\treturn;\n+\n+\tif (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) {\n+\t\tcsr_write(CSR_MLWID, mlwid);\n+\t\treturn;\n+\t}\n+\n+\tcsr_write(CSR_MWIDDELEG, 0);\n+\tcsr_write(CSR_MLWID, mlwid);\n+\tif (mwiddeleg) {\n+\t\tcsr_write(CSR_MWIDDELEG, mwiddeleg);\n+\t\tcsr_write(CSR_SLWID, slwid);\n+\t}\n+}\n+\n+static int wg_init(void *fdt)\n+{\n+\tstruct wg_platform_ctx *platform;\n+\tint checker_node, rc;\n+\tu32 count, idx = 0;\n+\tbool has_runtime;\n+\n+\twg_free_platform();\n+\n+\tif (!fdt)\n+\t\treturn 0;\n+\n+\tcount = wg_count_platform_checkers(fdt);\n+\thas_runtime = wg_has_cpu_runtime(fdt);\n+\tif (!count && !has_runtime)\n+\t\treturn 0;\n+\n+\tplatform = sbi_zalloc(sizeof(*platform));\n+\tif (!platform)\n+\t\treturn SBI_ENOMEM;\n+\n+\tplatform->hart_count = sbi_scratch_last_hartindex() + 1;\n+\tplatform->checker_count = count;\n+\tplatform->checker_enabled = !!count;\n+\tplatform->runtime_enabled = has_runtime;\n+\tplatform->checkers = sbi_calloc(sizeof(*platform->checkers), count);\n+\tif (count && !platform->checkers) {\n+\t\tsbi_free(platform);\n+\t\treturn SBI_ENOMEM;\n+\t}\n+\n+\tplatform->hart_defaults = sbi_calloc(sizeof(*platform->hart_defaults),\n+\t\t\t\t\t      platform->hart_count);\n+\tif (!platform->hart_defaults) {\n+\t\twg_free_platform_ctx(platform);\n+\t\treturn SBI_ENOMEM;\n+\t}\n+\n+\twg_init_cpu_defaults(platform);\n+\trc = wg_parse_cpu_defaults(fdt, platform);\n+\tif (rc) {\n+\t\twg_free_platform_ctx(platform);\n+\t\treturn rc;\n+\t}\n+\n+\tif (platform->checker_enabled) {\n+\t\tchecker_node = -1;\n+\t\twhile (true) {\n+\t\t\tchecker_node = fdt_node_offset_by_compatible(\n+\t\t\t\tfdt, checker_node, QEMU_VIRT_WG_COMPAT);\n+\t\t\tif (checker_node < 0)\n+\t\t\t\tbreak;\n+\t\t\tif (!fdt_getprop(fdt, checker_node,\n+\t\t\t\t\t QEMU_VIRT_WG_PROP_SUBORDINATES, NULL))\n+\t\t\t\tcontinue;\n+\n+\t\t\trc = wg_parse_checker(fdt, checker_node,\n+\t\t\t\t\t      &platform->checkers[idx]);\n+\t\t\tif (rc) {\n+\t\t\t\tsbi_printf(\"[WG] failed to parse checker %s err=%d\\n\",\n+\t\t\t\t\t   fdt_get_name(fdt, checker_node, NULL),\n+\t\t\t\t\t   rc);\n+\t\t\t\twg_free_platform_ctx(platform);\n+\t\t\t\treturn rc;\n+\t\t\t}\n+\n+\t\t\trc = wg_program_checker(&platform->checkers[idx]);\n+\t\t\tif (rc) {\n+\t\t\t\tsbi_printf(\"[WG] failed to program checker %s err=%d\\n\",\n+\t\t\t\t\t   platform->checkers[idx].name, rc);\n+\t\t\t\twg_free_platform_ctx(platform);\n+\t\t\t\treturn rc;\n+\t\t\t}\n+\n+\t\t\tsbi_printf(\"[WG] checker %s base=0x%llx slots=%u rules=%u%s\\n\",\n+\t\t\t\t   platform->checkers[idx].name,\n+\t\t\t\t   (unsigned long long)platform->checkers[idx].mmio_base,\n+\t\t\t\t   platform->checkers[idx].slot_count,\n+\t\t\t\t   platform->checkers[idx].range_count,\n+\t\t\t\t   platform->checkers[idx].full_checker_rule ?\n+\t\t\t\t\t\" full-checker\" : \"\");\n+\t\t\tidx++;\n+\t\t}\n+\t}\n+\n+\twg_platform = platform;\n+\treturn 0;\n+}\n+\n+static int wg_domain_init(void *fdt, int domain_offset,\n+\t\t\t  struct sbi_domain *dom, void **out_ctx)\n+{\n+\tstruct wg_domain_ctx *ctx;\n+\tint hoff, child, rc;\n+\tbool found = false;\n+\n+\tif (!out_ctx)\n+\t\treturn SBI_EINVAL;\n+\n+\t*out_ctx = NULL;\n+\tif (!wg_runtime_enabled())\n+\t\treturn 0;\n+\tif (!fdt || domain_offset < 0)\n+\t\treturn 0;\n+\n+\thoff = fdt_subnode_offset(fdt, domain_offset, \"hw-isolation\");\n+\tif (hoff < 0)\n+\t\treturn (dom == &root) ? 0 : SBI_EINVAL;\n+\n+\tfdt_for_each_subnode(child, fdt, hoff) {\n+\t\tif (fdt_node_check_compatible(\n+\t\t\t\tfdt, child, QEMU_VIRT_WG_COMPAT))\n+\t\t\tcontinue;\n+\t\tfound = true;\n+\t\tbreak;\n+\t}\n+\n+\tif (!found)\n+\t\treturn (dom == &root) ? 0 : SBI_EINVAL;\n+\n+\tctx = sbi_zalloc(sizeof(*ctx));\n+\tif (!ctx)\n+\t\treturn SBI_ENOMEM;\n+\n+\trc = wg_parse_wid_prop(fdt, child, QEMU_VIRT_WG_PROP_WID, &ctx->wid);\n+\tif (rc)\n+\t\tgoto err_free_ctx;\n+\tctx->has_wid = true;\n+\n+\trc = wg_parse_widlist(fdt, child, QEMU_VIRT_WG_PROP_WIDLIST,\n+\t\t\t      &ctx->widlist_mask, ctx->widlist,\n+\t\t\t      &ctx->widlist_count);\n+\tif (rc)\n+\t\tgoto err_free_ctx;\n+\n+\trc = wg_validate_domain_ctx(dom, ctx);\n+\tif (rc)\n+\t\tgoto err_free_ctx;\n+\n+\t*out_ctx = ctx;\n+\treturn 0;\n+\n+err_free_ctx:\n+\tsbi_free(ctx);\n+\treturn rc;\n+}\n+\n+static u32 wg_fallback_wid(void)\n+{\n+\tconst struct wg_cpu_defaults *cpu_defaults = wg_current_cpu_defaults();\n+\n+\treturn cpu_defaults ? cpu_defaults->trusted_wid : 0;\n+}\n+\n+static u32 wg_valid_wid_mask(void)\n+{\n+\tconst struct wg_cpu_defaults *cpu_defaults = wg_current_cpu_defaults();\n+\n+\treturn cpu_defaults ? cpu_defaults->valid_wid_mask :\n+\t\t\t      (u32)wg_wid_mask(wg_fallback_wid());\n+}\n+\n+static u32 wg_select_slwid(u32 widlist_mask, bool has_wid, u32 wid, u32 fallback)\n+{\n+\tu32 i;\n+\n+\tif (!widlist_mask)\n+\t\treturn fallback;\n+\n+\tif (has_wid && (wg_wid_mask(wid) & widlist_mask))\n+\t\treturn wid;\n+\n+\tfor (i = 0; i < 32; i++) {\n+\t\tif (widlist_mask & (1U << i))\n+\t\t\treturn i;\n+\t}\n+\n+\treturn fallback;\n+}\n+\n+static void wg_domain_exit(const struct sbi_domain *src,\n+\t\t\t   const struct sbi_domain *dst, void *ctx)\n+{\n+\tu32 mlwid = wg_fallback_wid();\n+\n+\t(void)ctx;\n+\tif (!wg_runtime_enabled())\n+\t\treturn;\n+\n+\twg_program_wid_state(mlwid, 0, mlwid);\n+\n+\tsbi_printf(\"[WG] domain_exit src=%s dst=%s mlwid=%u mwiddeleg=0x0\\n\",\n+\t\t   src ? src->name : \"<null>\",\n+\t\t   dst ? dst->name : \"<null>\", mlwid);\n+}\n+\n+static void wg_domain_enter(const struct sbi_domain *dst,\n+\t\t\t    const struct sbi_domain *src, void *ctx)\n+{\n+\tstruct wg_domain_ctx *dctx = ctx;\n+\tu32 valid_mask = wg_valid_wid_mask();\n+\tu32 mlwid = wg_fallback_wid();\n+\tu32 mwiddeleg = 0;\n+\tu32 slwid = mlwid;\n+\n+\t(void)src;\n+\tif (!wg_runtime_enabled())\n+\t\treturn;\n+\n+\tif (dctx && dctx->has_wid && (wg_wid_mask(dctx->wid) & valid_mask))\n+\t\tmlwid = dctx->wid;\n+\n+\tif (dctx)\n+\t\tmwiddeleg = dctx->widlist_mask & valid_mask;\n+\tslwid = wg_select_slwid(mwiddeleg, dctx && dctx->has_wid,\n+\t\t\t\tdctx ? dctx->wid : 0, mlwid);\n+\n+\twg_program_wid_state(mlwid, mwiddeleg, slwid);\n+\n+\tsbi_printf(\"[WG] domain_enter dst=%s mlwid=%u mwiddeleg=0x%x\",\n+\t\t   dst ? dst->name : \"<null>\", mlwid, mwiddeleg);\n+\tsbi_printf(\" slwid=%u\\n\", slwid);\n+}\n+\n+static void wg_domain_cleanup(struct sbi_domain *dom, void *ctx)\n+{\n+\t(void)dom;\n+\tsbi_free(ctx);\n+}\n+\n+static const struct sbi_hwiso_ops wg_ops = {\n+\t.name = QEMU_VIRT_WG_COMPAT,\n+\t.init = wg_init,\n+\t.domain_init = wg_domain_init,\n+\t.domain_exit = wg_domain_exit,\n+\t.domain_enter = wg_domain_enter,\n+\t.domain_cleanup = wg_domain_cleanup,\n+};\n+\n+int qemu_virt_hwiso_register(void *fdt)\n+{\n+\tint rc;\n+\n+\tif (!fdt)\n+\t\treturn 0;\n+\n+\tif (fdt_node_check_compatible(fdt, 0, \"riscv-virtio\") &&\n+\t    fdt_node_check_compatible(fdt, 0, \"qemu,virt\"))\n+\t\treturn 0;\n+\n+\trc = sbi_hwiso_register(&wg_ops);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn 0;\n+}\n+\n",
    "prefixes": [
        "RFC",
        "3/3"
    ]
}