Message ID | 20240612081416.29704-5-jim.shu@sifive.com |
---|---|
State | New |
Headers | show |
Series | Implements RISC-V WorldGuard extension v0.4 | expand |
On Wed, Jun 12, 2024 at 6:18 PM Jim Shu <jim.shu@sifive.com> wrote: > > 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 --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 We should ensure this is enabled as well so that it is built by default Alistair > + > 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 > -- > 2.17.1 > >
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
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