diff mbox series

[RFC,04/16] hw/misc: riscv_worldguard: Add RISC-V WorldGuard global config

Message ID 20240612081416.29704-5-jim.shu@sifive.com
State New
Headers show
Series Implements RISC-V WorldGuard extension v0.4 | expand

Commit Message

Jim Shu June 12, 2024, 8:14 a.m. UTC
Add a device for RISCV WG global config, which contains the number of
worlds, reset value, and trusted WID ... etc.

This global config is used by both CPU WG extension and wgChecker devices.

Signed-off-by: Jim Shu <jim.shu@sifive.com>
---
 hw/misc/Kconfig                    |   3 +
 hw/misc/meson.build                |   1 +
 hw/misc/riscv_worldguard.c         | 183 +++++++++++++++++++++++++++++
 include/hw/misc/riscv_worldguard.h |  55 +++++++++
 4 files changed, 242 insertions(+)
 create mode 100644 hw/misc/riscv_worldguard.c
 create mode 100644 include/hw/misc/riscv_worldguard.h
diff mbox series

Patch

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1e08785b83..08fc0f2b8c 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -213,4 +213,7 @@  config IOSB
 config XLNX_VERSAL_TRNG
     bool
 
+config RISCV_WORLDGUARD
+    bool
+
 source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 86596a3888..a75668ff86 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -34,6 +34,7 @@  system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
+specific_ss.add(when: 'CONFIG_RISCV_WORLDGUARD', if_true: files('riscv_worldguard.c'))
 
 subdir('macio')
 
diff --git a/hw/misc/riscv_worldguard.c b/hw/misc/riscv_worldguard.c
new file mode 100644
index 0000000000..c839cc4e87
--- /dev/null
+++ b/hw/misc/riscv_worldguard.c
@@ -0,0 +1,183 @@ 
+/*
+ * RISC-V WorldGuard Device
+ *
+ * Copyright (c) 2022 SiFive, Inc.
+ *
+ * This provides WorldGuard global config.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "exec/hwaddr.h"
+#include "hw/registerfields.h"
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/riscv_worldguard.h"
+#include "hw/core/cpu.h"
+#include "target/riscv/cpu.h"
+#include "trace.h"
+
+/*
+ * WorldGuard global config:
+ * List the global setting of WG, like num-of-worlds. It is unique in the machine.
+ * All CPUs with WG extension and wgChecker devices will use it.
+ */
+struct RISCVWorldGuardState *worldguard_config = NULL;
+
+static Property riscv_worldguard_properties[] = {
+    DEFINE_PROP_UINT32("nworlds", RISCVWorldGuardState, nworlds, 0),
+
+    /* Only Trusted WID could access wgCheckers if it is enabled. */
+    DEFINE_PROP_UINT32("trustedwid", RISCVWorldGuardState, trustedwid, NO_TRUSTEDWID),
+
+    /*
+     * WG reset value is bypass mode in HW. All WG permission checkings are
+     * pass by default, so SW could correctly run on the machine w/o any WG
+     * programming.
+     */
+    DEFINE_PROP_BOOL("hw-bypass", RISCVWorldGuardState, hw_bypass, false),
+
+    /*
+     * TrustZone compatible mode:
+     * This mode is only supported in 2 worlds system. It converts WorldGuard
+     * WID to TZ NS signal on the bus so WG could be cooperated with
+     * TZ components. In QEMU, it converts WID to 'MemTxAttrs.secure' bit used
+     * by TZ.
+     */
+    DEFINE_PROP_BOOL("tz-compat", RISCVWorldGuardState, tz_compat, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+/* WID to MemTxAttrs converter */
+static void wid_to_mem_attrs(MemTxAttrs *attrs, uint32_t wid)
+{
+    g_assert(wid < worldguard_config->nworlds);
+
+    attrs->unspecified = 0;
+    if (worldguard_config->tz_compat) {
+        attrs->secure = wid;
+    } else {
+        attrs->world_id = wid;
+    }
+}
+
+/* MemTxAttrs to WID converter */
+uint32_t mem_attrs_to_wid(MemTxAttrs attrs)
+{
+    if (attrs.unspecified) {
+        if (worldguard_config->trustedwid != NO_TRUSTEDWID) {
+            return worldguard_config->trustedwid;
+        } else {
+            return worldguard_config->nworlds - 1;
+        }
+    }
+
+    if (worldguard_config->tz_compat) {
+        return attrs.secure;
+    } else {
+        return attrs.world_id;
+    }
+}
+
+bool could_access_wgblocks(MemTxAttrs attrs, const char *wgblock)
+{
+    uint32_t wid = mem_attrs_to_wid(attrs);
+    uint32_t trustedwid = worldguard_config->trustedwid;
+
+    if ((trustedwid == NO_TRUSTEDWID) || (wid == trustedwid)) {
+        return true;
+    } else {
+        /*
+         * Only Trusted WID could access WG blocks if having it.
+         * Access them from other WIDs will get failed.
+         */
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Invalid access to %s from non-trusted WID %d\n",
+                      __func__, wgblock, wid);
+
+        return false;
+    }
+}
+
+static void riscv_worldguard_realize(DeviceState *dev, Error **errp)
+{
+    RISCVWorldGuardState *s = RISCV_WORLDGUARD(dev);
+
+    if (worldguard_config != NULL) {
+        error_setg(errp, "Couldn't realize multiple global WorldGuard configs.");
+        return;
+    }
+
+    if ((s->nworlds) & (s->nworlds - 1)) {
+        error_setg(errp, "Current implementation only support power-of-2 NWorld.");
+        return;
+    }
+
+    if ((s->trustedwid != NO_TRUSTEDWID) && (s->trustedwid >= s->nworlds)) {
+        error_setg(errp, "Trusted WID must be less than the number of world.");
+        return;
+    }
+
+    if ((s->nworlds != 2) && (s->tz_compat)) {
+        error_setg(errp, "Only 2 worlds system could use TrustZone compatible mode.");
+        return;
+    }
+
+    /* Register WG global config */
+    worldguard_config = s;
+
+    /* Initialize global data for wgChecker */
+    wgc_slot_perm_mask = MAKE_64BIT_MASK(0, 2 * worldguard_config->nworlds);
+}
+
+static void riscv_worldguard_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, riscv_worldguard_properties);
+    dc->user_creatable = true;
+    dc->realize = riscv_worldguard_realize;
+}
+
+static const TypeInfo riscv_worldguard_info = {
+    .name          = TYPE_RISCV_WORLDGUARD,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(RISCVWorldGuardState),
+    .class_init    = riscv_worldguard_class_init,
+};
+
+/*
+ * Create WorldGuard global config
+ */
+DeviceState *riscv_worldguard_create(uint32_t nworlds, uint32_t trustedwid,
+                                     bool hw_bypass, bool tz_compat)
+{
+    DeviceState *dev = qdev_new(TYPE_RISCV_WORLDGUARD);
+    qdev_prop_set_uint32(dev, "nworlds", nworlds);
+    qdev_prop_set_uint32(dev, "trustedwid", trustedwid);
+    qdev_prop_set_bit(dev, "hw-bypass", hw_bypass);
+    qdev_prop_set_bit(dev, "tz-compat", tz_compat);
+    qdev_realize(DEVICE(dev), NULL, &error_fatal);
+    return dev;
+}
+
+static void riscv_worldguard_register_types(void)
+{
+    type_register_static(&riscv_worldguard_info);
+}
+
+type_init(riscv_worldguard_register_types)
diff --git a/include/hw/misc/riscv_worldguard.h b/include/hw/misc/riscv_worldguard.h
new file mode 100644
index 0000000000..8a533a0517
--- /dev/null
+++ b/include/hw/misc/riscv_worldguard.h
@@ -0,0 +1,55 @@ 
+/*
+ * RISC-V WorldGuard Devices
+ *
+ * Copyright (c) 2022 RISCV, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_RISCV_WORLDGUARD_H
+#define HW_RISCV_WORLDGUARD_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+#include "exec/hwaddr.h"
+
+#define TYPE_RISCV_WORLDGUARD "riscv.worldguard"
+
+#define NO_TRUSTEDWID           UINT32_MAX
+
+typedef struct RISCVWorldGuardState RISCVWorldGuardState;
+DECLARE_INSTANCE_CHECKER(RISCVWorldGuardState, RISCV_WORLDGUARD,
+                         TYPE_RISCV_WORLDGUARD)
+
+struct RISCVWorldGuardState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+
+    /* Property */
+    uint32_t nworlds;
+    uint32_t trustedwid;
+    bool hw_bypass;
+    bool tz_compat;
+};
+
+extern struct RISCVWorldGuardState *worldguard_config;
+
+DeviceState *riscv_worldguard_create(uint32_t nworlds, uint32_t trustedwid,
+                                     bool hw_bypass, bool tz_compat);
+
+uint32_t mem_attrs_to_wid(MemTxAttrs attrs);
+bool could_access_wgblocks(MemTxAttrs attrs, const char *wgblock);
+
+#endif