From 411ee798b5602141bcc2bc050e9105887481a7f4 Mon Sep 17 00:00:00 2001 From: Lex Bailey Date: Tue, 17 Feb 2026 11:23:24 +0000 Subject: [PATCH 01/12] Introduce ot-earlgrey machine, and one peripheral for it (alert handler) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the start of efforts to contribute code from the lowRISC fork of QEMU to bring the QEMU support for OpenTitan machines in line with Earl Grey 1.0 by adding a new machine called ot-earlgrey. This work was done by Emmanuel Blot and Loïc Lefort from Rivos, and various people at lowRISC. This patch set in particular adds the basic machine definition, and one of the peripherals required for it: the alert handler device. I previously emailed asking for advice on how to test our changes in a way that is acceptable upstream, and how best to submit such a large set of changes, if anyone has more thoughts on that, please do reply to that email. (https://lists.nongnu.org/archive/html/qemu-riscv/2026-02/msg00139.html) There's lots more commits to come, I don't want to dump too much on the mailing list all at once. For now this is a small first step. This patch set should give a flavour of the style of what's to come, and I'm expecting there will be some changes required. We'd just like to understand what will be needed to make this acceptable as soon as we can before we try to upstream lots more of these changes. In particular, most of our new peripheral blocks are currently in the directory called hw/opentitan. In our fork we have about 60 blocks in this directory, we have put them there to keep them organised as opentitan-specific blocks. Is this a suitable place for them? would you prefer a different structure? Thanks! To: qemu-devel@nongnu.org Cc: Palmer Dabbelt Cc: Alistair Francis Cc: Weiwei Li Cc: Daniel Henrique Barboza Cc: Liu Zhiwei Cc: qemu-riscv@nongnu.org Cc: Paolo Bonzini Cc: Marc-André Lureau Cc: Alistair Francis Cc: Pierrick Bouvier Cc: Dr. David Alan Gilbert Cc: Daniel P. Berrangé Cc: Philippe Mathieu-Daudé Cc: lowRISC Cc: nabihestefan@google.com Cc: Amit Kumar-Hermosillo Signed-off-by: Lex Bailey --- b4-submit-tracking --- # This section is used internally by b4 prep for tracking purposes. { "series": { "revision": 1, "change-id": "20260217-opentitan-earlgrey-474a14215feb", "prefixes": [ "RFC" ] } } From 4c2365a6e99e70036d42cac439d179784d29ceaa Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Apr 2023 19:39:11 +0200 Subject: [PATCH 02/12] [ot] target/riscv: rename ibex hart as lowrisc-ibex Follow vendor-device syntax used with other RISCV cores Signed-off-by: Emmanuel Blot --- hw/riscv/opentitan.c | 2 +- target/riscv/cpu-qom.h | 2 +- target/riscv/cpu.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 309125e854bc7..19cb35b3510df 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -119,7 +119,7 @@ static void opentitan_machine_class_init(ObjectClass *oc, const void *data) mc->desc = "RISC-V Board compatible with OpenTitan"; mc->init = opentitan_machine_init; mc->max_cpus = 1; - mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; + mc->default_cpu_type = TYPE_RISCV_CPU_LOWRISC_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 30dcdcfaae516..3a6394fd11817 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -42,7 +42,7 @@ #define TYPE_RISCV_CPU_RVA22S64 RISCV_CPU_TYPE_NAME("rva22s64") #define TYPE_RISCV_CPU_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") #define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") -#define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") +#define TYPE_RISCV_CPU_LOWRISC_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") #define TYPE_RISCV_CPU_SIFIVE_E RISCV_CPU_TYPE_NAME("sifive-e") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e56470a374847..24a4c2fd3ffa7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3043,7 +3043,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV32, ), - DEFINE_RISCV_CPU(TYPE_RISCV_CPU_IBEX, TYPE_RISCV_VENDOR_CPU, + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_LOWRISC_IBEX, TYPE_RISCV_VENDOR_CPU, .misa_mxl_max = MXL_RV32, .misa_ext = RVI | RVM | RVC | RVU, .priv_spec = PRIV_VERSION_1_12_0, From efb4f331f9a7e5d16728f88f5e4ddfad6e8abb70 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 30 Jan 2023 13:14:50 +0100 Subject: [PATCH 03/12] [ot] hw/char, hw/timer, hw/ssi: fix device definitions Use a separate Kconfig symbols for Ibex UART, Timer, and SPI devices: having an Ibex CPU does not imply usage of these specific implementations. Signed-off-by: Emmanuel Blot --- hw/char/Kconfig | 3 +++ hw/char/meson.build | 2 +- hw/riscv/Kconfig | 3 +++ hw/ssi/Kconfig | 4 ++++ hw/ssi/meson.build | 2 +- hw/timer/Kconfig | 3 +++ hw/timer/ibex_timer.c | 2 +- hw/timer/meson.build | 2 +- 8 files changed, 17 insertions(+), 4 deletions(-) diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 020c0a84bb6e5..0cbbaedb2f189 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -95,3 +95,6 @@ config IP_OCTAL_232 bool default y depends on IPACK + +config IBEX_UART + bool diff --git a/hw/char/meson.build b/hw/char/meson.build index a9e1dc26c0fa1..f47fa11bbe023 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -2,7 +2,7 @@ system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) system_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) system_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) -system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) +system_ss.add(when: 'CONFIG_IBEX_UART', if_true: files('ibex_uart.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) system_ss.add(when: 'CONFIG_IP_OCTAL_232', if_true: files('ipoctal232.c')) system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 0222c93f878b3..e6aa32ee8f869 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -38,6 +38,9 @@ config OPENTITAN default y depends on RISCV32 select IBEX + select IBEX_SPI_HOST + select IBEX_TIMER + select IBEX_UART select SIFIVE_PLIC select UNIMP diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 1bd56463c1e0c..c8d573eeb98ab 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -32,3 +32,7 @@ config PNV_SPI config ALLWINNER_A10_SPI bool select SSI + +config IBEX_SPI_HOST + bool + select SSI diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 6afb1ea200121..a3ba319280d6d 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -10,6 +10,6 @@ system_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) system_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) -system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_IBEX_SPI_HOST', if_true: files('ibex_spi_host.c')) system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c')) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index b3d823ce2c394..2fdefd0d1fcfd 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -65,3 +65,6 @@ config STELLARIS_GPTM config AVR_TIMER16 bool + +config IBEX_TIMER + bool diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index ee186521893ec..858e1a67de2b0 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -31,7 +31,7 @@ #include "hw/timer/ibex_timer.h" #include "hw/core/irq.h" #include "hw/core/qdev-properties.h" -#include "target/riscv/cpu.h" +#include "hw/core/registerfields.h" #include "migration/vmstate.h" REG32(ALERT_TEST, 0x00) diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 178321c029c8d..cc02aa0dda346 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -30,7 +30,7 @@ system_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) system_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) system_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) -specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) +system_ss.add(when: 'CONFIG_IBEX_TIMER', if_true: files('ibex_timer.c')) system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) From 1992169dfe9d7aad8f1f5b90e5877f5f38a8303c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 5 Jun 2023 12:24:08 +0200 Subject: [PATCH 04/12] [ot] hw/riscv: add helper for Ibex platforms Signed-off-by: Lex Bailey --- MAINTAINERS | 14 +- hmp-commands-info.hx | 12 ++ hw/riscv/Kconfig | 3 + hw/riscv/ibex_common.c | 316 ++++++++++++++++++++++++++++++++ hw/riscv/meson.build | 1 + include/hw/riscv/ibex_common.h | 322 +++++++++++++++++++++++++++++++++ 6 files changed, 666 insertions(+), 2 deletions(-) create mode 100644 hw/riscv/ibex_common.c create mode 100644 include/hw/riscv/ibex_common.h diff --git a/MAINTAINERS b/MAINTAINERS index e0c481e2125d8..8b8bb7b554e5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1716,9 +1716,13 @@ M: Alistair Francis L: qemu-riscv@nongnu.org S: Supported F: hw/riscv/opentitan.c -F: hw/*/ibex_*.c +F: hw/ssi/ibex_spi_host.c +F: hw/timer/ibex_timer.c +F: hw/char/ibex_uart.c F: include/hw/riscv/opentitan.h -F: include/hw/*/ibex_*.h +F: include/hw/ssi/ibex_spi_host.h +F: include/hw/timer/ibex_timer.h +F: include/hw/char/ibex_uart.h Microchip PolarFire SoC Icicle Kit L: qemu-riscv@nongnu.org @@ -4450,6 +4454,12 @@ F: docs/devel/ebpf_rss.rst F: ebpf/* F: tools/ebpf/* +Ibex +M: lowRISC +S: Supported +F: hw/riscv/ibex_common.c +F: include/hw/riscv/ibex_common.h +F: include/hw/riscv/ibex_irq.h Build and test automation ------------------------- Build and test automation, general continuous integration diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 74c741f80e243..90f05f7599e3c 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -1010,3 +1010,15 @@ SRST ``info firmware-log`` Show the firmware (ovmf) debug log. ERST + + { + .name = "ibex", + .args_type = "", + .params = "", + .help = "Show Ibex vCPU info", + }, + +SRST + ``info ibex`` + Show Ibex vCPU information. +ERST diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index e6aa32ee8f869..7877f0615c416 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -7,6 +7,9 @@ config RISCV_NUMA config IBEX bool +config IBEX_COMMON + bool + # RISC-V machines in alphabetical order config MICROCHIP_PFSOC diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c new file mode 100644 index 0000000000000..c6056767c7fc5 --- /dev/null +++ b/hw/riscv/ibex_common.c @@ -0,0 +1,316 @@ +/* + * QEMU RISC-V Helpers for LowRISC Ibex Demo System & OpenTitan EarlGrey + * + * Copyright (c) 2022-2023 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "cpu.h" +#include "disas/disas.h" +#include "elf.h" +#include "exec/hwaddr.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/misc/unimp.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/ibex_common.h" +#include "hw/sysbus.h" +#include "monitor/monitor.h" + + +static void ibex_mmio_map_device(SysBusDevice *dev, MemoryRegion *mr, + unsigned nr, hwaddr addr) +{ + assert(nr < dev->num_mmio); + assert(dev->mmio[nr].addr == (hwaddr)-1); + dev->mmio[nr].addr = addr; + memory_region_add_subregion(mr, addr, dev->mmio[nr].memory); +} + +DeviceState **ibex_create_devices(const IbexDeviceDef *defs, unsigned count, + DeviceState *parent) +{ + DeviceState **devices = g_new0(DeviceState *, count); + unsigned unimp_count = 0; + for (unsigned idx = 0; idx < count; idx++) { + const IbexDeviceDef *def = &defs[idx]; + assert(def->type); + devices[idx] = qdev_new(def->type); + + char *name; + if (!strcmp(def->type, TYPE_UNIMPLEMENTED_DEVICE)) { + if (def->name) { + name = g_strdup_printf("%s[%u]", def->name, def->instance); + } else { + name = g_strdup_printf(TYPE_UNIMPLEMENTED_DEVICE "[%u]", + unimp_count); + } + unimp_count += 1u; + } else { + name = g_strdup_printf("%s[%u]", def->type, def->instance); + } + object_property_add_child(OBJECT(parent), name, OBJECT(devices[idx])); + g_free(name); + } + return devices; +} + +void ibex_link_devices(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count) +{ + /* Link devices */ + for (unsigned idx = 0; idx < count; idx++) { + DeviceState *dev = devices[idx]; + const IbexDeviceLinkDef *link = defs[idx].link; + if (link) { + while (link->propname) { + DeviceState *target = devices[link->index]; + (void)object_property_set_link(OBJECT(dev), link->propname, + OBJECT(target), &error_fatal); + link++; + } + } + } +} + +void ibex_define_device_props(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count) +{ + for (unsigned idx = 0; idx < count; idx++) { + DeviceState *dev = devices[idx]; + const IbexDevicePropDef *prop = defs[idx].prop; + if (prop) { + while (prop->propname) { + switch (prop->type) { + case IBEX_PROP_TYPE_BOOL: + object_property_set_bool(OBJECT(dev), prop->propname, + prop->b, &error_fatal); + break; + case IBEX_PROP_TYPE_INT: + object_property_set_int(OBJECT(dev), prop->propname, + prop->i, &error_fatal); + break; + case IBEX_PROP_TYPE_UINT: + object_property_set_int(OBJECT(dev), prop->propname, + prop->u, &error_fatal); + break; + case IBEX_PROP_TYPE_STR: + object_property_set_str(OBJECT(dev), prop->propname, + prop->s, &error_fatal); + break; + default: + g_assert_not_reached(); + break; + } + prop++; + } + } + } +} + +void ibex_realize_system_devices(DeviceState **devices, + const IbexDeviceDef *defs, unsigned count) +{ + BusState *bus = sysbus_get_default(); + + ibex_realize_devices(devices, bus, defs, count); + + MemoryRegion *mrs[] = { get_system_memory(), NULL, NULL, NULL }; + + ibex_map_devices(devices, mrs, defs, count); +} + +void ibex_realize_devices(DeviceState **devices, BusState *bus, + const IbexDeviceDef *defs, unsigned count) +{ + /* Realize devices */ + for (unsigned idx = 0; idx < count; idx++) { + DeviceState *dev = devices[idx]; + const IbexDeviceDef *def = &defs[idx]; + + if (def->cfg) { + def->cfg(dev, def, DEVICE(OBJECT(dev)->parent)); + } + + if (def->memmap) { + SysBusDevice *busdev = + (SysBusDevice *)object_dynamic_cast(OBJECT(dev), + TYPE_SYS_BUS_DEVICE); + if (!busdev) { + /* non-sysbus devices are not supported for now */ + g_assert_not_reached(); + } + + qdev_realize_and_unref(DEVICE(busdev), bus, &error_fatal); + } else { + /* device is not connected to a bus */ + qdev_realize_and_unref(dev, NULL, &error_fatal); + } + } +} + +void ibex_map_devices(DeviceState **devices, MemoryRegion **mrs, + const IbexDeviceDef *defs, unsigned count) +{ + for (unsigned idx = 0; idx < count; idx++) { + DeviceState *dev = devices[idx]; + const IbexDeviceDef *def = &defs[idx]; + + if (def->memmap) { + SysBusDevice *busdev = + (SysBusDevice *)object_dynamic_cast(OBJECT(dev), + TYPE_SYS_BUS_DEVICE); + if (busdev) { + const MemMapEntry *memmap = def->memmap; + unsigned mem = 0; + while (memmap->size) { + unsigned region = IBEX_MEMMAP_GET_REGIDX(memmap->base); + MemoryRegion *mr = mrs[region]; + if (mr) { + ibex_mmio_map_device(busdev, mr, mem, + IBEX_MEMMAP_GET_ADDRESS( + memmap->base)); + } + mem++; + memmap++; + } + } + } + } +} + +void ibex_connect_devices(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count) +{ + /* Connect GPIOs (in particular, IRQs) */ + for (unsigned idx = 0; idx < count; idx++) { + DeviceState *dev = devices[idx]; + const IbexDeviceDef *def = &defs[idx]; + + if (def->gpio) { + const IbexGpioConnDef *conn = def->gpio; + while (conn->out.num >= 0 && conn->in.num >= 0) { + qemu_irq in_gpio = + qdev_get_gpio_in_named(devices[conn->in.index], + conn->in.name, conn->in.num); + + qdev_connect_gpio_out_named(dev, conn->out.name, conn->out.num, + in_gpio); + conn++; + } + } + } +} + +void ibex_configure_devices(DeviceState **devices, BusState *bus, + const IbexDeviceDef *defs, unsigned count) +{ + ibex_link_devices(devices, defs, count); + ibex_define_device_props(devices, defs, count); + ibex_realize_devices(devices, bus, defs, count); + ibex_connect_devices(devices, defs, count); +} + +void ibex_unimp_configure(DeviceState *dev, const IbexDeviceDef *def, + DeviceState *parent) +{ + if (def->name) { + qdev_prop_set_string(dev, "name", def->name); + } + assert(def->memmap != NULL); + assert(def->memmap->size != 0); + qdev_prop_set_uint64(dev, "size", def->memmap->size); +} + +void ibex_load_kernel(AddressSpace *as) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + /* load kernel if provided */ + if (ms->kernel_filename) { + uint64_t kernel_entry; + if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, + &kernel_entry, NULL, NULL, NULL, 0, EM_RISCV, 1, 0, + as, true, NULL) <= 0) { + error_report("Cannot load ELF kernel %s", ms->kernel_filename); + exit(EXIT_FAILURE); + } + + CPUState *cpu; + CPU_FOREACH(cpu) { + if (!as || cpu->as == as) { + CPURISCVState *env = &RISCV_CPU(cpu)->env; + env->resetvec = (target_ulong)kernel_entry; + } + } + } +} + +uint64_t ibex_get_current_pc(void) +{ + CPUState *cs = current_cpu; + + return cs && cs->cc->get_pc ? cs->cc->get_pc(cs) : 0u; +} + +/* x0 is replaced with PC */ +static const char ibex_ireg_names[32u][4u] = { + "pc", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", + "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", + "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", +}; + +void ibex_log_vcpu_registers(uint64_t regbm) +{ + CPURISCVState *env = &RISCV_CPU(current_cpu)->env; + qemu_log_mask(CPU_LOG_TB_IN_ASM, "\n....\n"); + if (regbm & 0x1u) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%4s: 0x" TARGET_FMT_lx "\n", + ibex_ireg_names[0], env->pc); + } + for (unsigned gix = 1u; gix < 32u; gix++) { + uint64_t mask = 1u << gix; + if (regbm & mask) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%4s: 0x" TARGET_FMT_lx "\n", + ibex_ireg_names[gix], env->gpr[gix]); + } + } +} + +/* + * Note: this is not specific to Ibex, and might apply to any vCPU. + */ +static void hmp_info_ibex(Monitor *mon, const QDict *qdict) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + vaddr pc; + const char *symbol; + if (cpu->cc->get_pc) { + pc = cpu->cc->get_pc(cpu); + symbol = lookup_symbol(pc); + } else { + pc = -1; + symbol = "?"; + } + monitor_printf(mon, "* CPU #%d: 0x%" PRIx64 " in '%s'\n", + cpu->cpu_index, (uint64_t)pc, symbol); + } +} + +static void ibex_register_types(void) +{ + monitor_register_hmp("ibex", true, &hmp_info_ibex); +} + +type_init(ibex_register_types) diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 533472e22aef3..70d63f56b52ce 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -3,6 +3,7 @@ riscv_ss.add(files('boot.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) +riscv_ss.add(when: 'CONFIG_IBEX_COMMON', if_true: files('ibex_common.c')) riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c')) riscv_ss.add(when: 'CONFIG_SHAKTI_C', if_true: files('shakti_c.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h new file mode 100644 index 0000000000000..6c7dae5cbe275 --- /dev/null +++ b/include/hw/riscv/ibex_common.h @@ -0,0 +1,322 @@ +/* + * QEMU RISC-V Helpers for LowRISC OpenTitan EarlGrey and related systems + * + * Copyright (c) 2022-2023 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_RISCV_IBEX_COMMON_H +#define HW_RISCV_IBEX_COMMON_H + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "hw/qdev-core.h" + + +/* ------------------------------------------------------------------------ */ +/* Devices & GPIOs */ +/* ------------------------------------------------------------------------ */ + +#define IBEX_MAX_MMIO_ENTRIES 4u +#define IBEX_MAX_GPIO_ENTRIES 16u + +typedef struct IbexDeviceDef IbexDeviceDef; + +typedef void (*ibex_dev_cfg_fn)(DeviceState *dev, const IbexDeviceDef *def, + DeviceState *parent); + +/** + * Structure defining a GPIO connection (in particular, IRQs) from the current + * device to a target device + */ +typedef struct { + /** Source GPIO */ + struct { + /** Name of source GPIO array or NULL for unnamed */ + const char *name; + /** Index of source output GPIO */ + int num; + } out; + + /** Target GPIO */ + struct { + /** Target device index */ + int index; + /** Name of target input GPIO array or NULL for unnamed */ + const char *name; + /** Index of target input GPIO */ + int num; + } in; +} IbexGpioConnDef; + +typedef struct { + /** Name of the property to assign the linked device to */ + const char *propname; + /** Linked device index */ + int index; +} IbexDeviceLinkDef; + +/** Type of device property */ +typedef enum { + IBEX_PROP_TYPE_BOOL, + IBEX_PROP_TYPE_INT, + IBEX_PROP_TYPE_UINT, + IBEX_PROP_TYPE_STR, +} IbexPropertyType; + +typedef struct { + /** Name of the property */ + const char *propname; + /** Type of property */ + IbexPropertyType type; + /** Value */ + union { + bool b; + int64_t i; + uint64_t u; + const char *s; + }; +} IbexDevicePropDef; + +struct IbexDeviceDef { + /** Registered type of the device */ + const char *type; + /** Optional name, may be NULL */ + const char *name; + /** Instance number, default to 0 */ + int instance; + /** Optional configuration function */ + ibex_dev_cfg_fn cfg; + /** Array of memory map */ + const MemMapEntry *memmap; + /** Array of GPIO connections */ + const IbexGpioConnDef *gpio; + /** Array of linked devices */ + const IbexDeviceLinkDef *link; + /** Array of properties */ + const IbexDevicePropDef *prop; +}; + +/* + * Special memory address marked to flag a special MemMapEntry. + * Flagged MemMapEntry are used to select a memory region while mem mapping + * devices. There could be up to 4 different regions. + */ +#define IBEX_MEMMAP_REGIDX_COUNT 4u +#define IBEX_MEMMAP_REGIDX_MASK \ + ((IBEX_MEMMAP_REGIDX_COUNT) - 1u) /* address are always word-aligned */ +#define IBEX_MEMMAP_MAKE_REG(_addr_, _flag_) \ + ((_addr_) | ((_flag_) & IBEX_MEMMAP_REGIDX_MASK)) +#define IBEX_MEMMAP_GET_REGIDX(_addr_) ((_addr_) & IBEX_MEMMAP_REGIDX_MASK) +#define IBEX_MEMMAP_GET_ADDRESS(_addr_) ((_addr_) & ~IBEX_MEMMAP_REGIDX_MASK) + +/** + * Create memory map entries, each arg is MemMapEntry definition + */ +#define MEMMAPENTRIES(...) \ + (const MemMapEntry[]) \ + { \ + __VA_ARGS__, \ + { \ + .size = 0u \ + } \ + } + +/** + * Create GPIO connection entries, each arg is IbexGpioConnDef definition + */ +#define IBEXGPIOCONNDEFS(...) \ + (const IbexGpioConnDef[]) \ + { \ + __VA_ARGS__, \ + { \ + .out = {.num = -1 } \ + } \ + } + +/** + * Create device link entries, each arg is IbexDeviceLinkDef definition + */ +#define IBEXDEVICELINKDEFS(...) \ + (const IbexDeviceLinkDef[]) \ + { \ + __VA_ARGS__, \ + { \ + .propname = NULL \ + } \ + } + +/** + * Create device property entries, each arg is IbexDevicePropDef definition + */ +#define IBEXDEVICEPROPDEFS(...) \ + (const IbexDevicePropDef[]) \ + { \ + __VA_ARGS__, \ + { \ + .propname = NULL \ + } \ + } + +/** + * Create a IbexGpioConnDef to connect two unnamed GPIOs + */ +#define IBEX_GPIO(_irq_, _in_idx_, _num_) \ + { \ + .out = { \ + .num = (_irq_), \ + }, \ + .in = { \ + .index = (_in_idx_), \ + .num = (_num_), \ + } \ + } + +/** + * Create a IbexGpioConnDef to connect a SysBus IRQ to an unnamed GPIO + */ +#define IBEX_GPIO_SYSBUS_IRQ(_irq_, _in_idx_, _num_) \ + { \ + .out = { \ + .name = SYSBUS_DEVICE_GPIO_IRQ, \ + .num = (_irq_), \ + }, \ + .in = { \ + .index = (_in_idx_), \ + .num = (_num_), \ + } \ + } + +/** + * Create a IbexLinkDeviceDef to link one device to another + */ +#define IBEX_DEVLINK(_pname_, _idx_) \ + { \ + .propname = (_pname_), .index = (_idx_), \ + } + +/** + * Create a boolean device property + */ +#define IBEX_DEV_BOOL_PROP(_pname_, _b_) \ + { \ + .propname = (_pname_), .type = IBEX_PROP_TYPE_BOOL, .b = (_b_), \ + } + +/** + * Create a signed integer device property + */ +#define IBEX_DEV_INT_PROP(_pname_, _i_) \ + { \ + .propname = (_pname_), .type = IBEX_PROP_TYPE_INT, .i = (_i_), \ + } + +/** + * Create an unsigned integer device property + */ +#define IBEX_DEV_UINT_PROP(_pname_, _u_) \ + { \ + .propname = (_pname_), .type = IBEX_PROP_TYPE_UINT, .u = (_u_), \ + } + +/** + * Create a string device property + */ +#define IBEX_DEV_STRING_PROP(_pname_, _s_) \ + { \ + .propname = (_pname_), .type = IBEX_PROP_TYPE_STR, .s = (_s_), \ + } + +DeviceState **ibex_create_devices(const IbexDeviceDef *defs, unsigned count, + DeviceState *parent); +void ibex_link_devices(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count); +void ibex_define_device_props(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count); +void ibex_realize_system_devices(DeviceState **devices, + const IbexDeviceDef *defs, unsigned count); +void ibex_realize_devices(DeviceState **devices, BusState *bus, + const IbexDeviceDef *defs, unsigned count); +void ibex_connect_devices(DeviceState **devices, const IbexDeviceDef *defs, + unsigned count); +void ibex_map_devices(DeviceState **devices, MemoryRegion **mrs, + const IbexDeviceDef *defs, unsigned count); +void ibex_configure_devices(DeviceState **devices, BusState *bus, + const IbexDeviceDef *defs, unsigned count); + +/** + * Utility function to configure unimplemented device. + * The Ibex device definition should have one defined memory entry, and an + * optional name. + */ +void ibex_unimp_configure(DeviceState *dev, const IbexDeviceDef *def, + DeviceState *parent); + +/* ------------------------------------------------------------------------ */ +/* CPU */ +/* ------------------------------------------------------------------------ */ + +/** + * Load an ELF application into a CPU address space. + * @as the address space to load the application into, maybe NULL to use the + * default address space + */ +void ibex_load_kernel(AddressSpace *as); + +/** + * Helper for device debugging: report the current guest PC, if any. + * + * If a HW access is performed from another device but the CPU, reported PC + * is 0. + */ +uint64_t ibex_get_current_pc(void); + +enum { + RV_GPR_PC = (1u << 0u), + RV_GPR_RA = (1u << 1u), + RV_GPR_SP = (1u << 2u), + RV_GPR_GP = (1u << 3u), + RV_GPR_TP = (1u << 4u), + RV_GPR_T0 = (1u << 5u), + RV_GPR_T1 = (1u << 6u), + RV_GPR_T2 = (1u << 7u), + RV_GPR_S0 = (1u << 8u), + RV_GPR_S1 = (1u << 9u), + RV_GPR_A0 = (1u << 10u), + RV_GPR_A1 = (1u << 11u), + RV_GPR_A2 = (1u << 12u), + RV_GPR_A3 = (1u << 13u), + RV_GPR_A4 = (1u << 14u), + RV_GPR_A5 = (1u << 15u), + RV_GPR_A6 = (1u << 16u), + RV_GPR_A7 = (1u << 17u), + RV_GPR_S2 = (1u << 18u), + RV_GPR_S3 = (1u << 19u), + RV_GPR_S4 = (1u << 20u), + RV_GPR_S5 = (1u << 21u), + RV_GPR_S6 = (1u << 22u), + RV_GPR_S7 = (1u << 23u), + RV_GPR_S8 = (1u << 24u), + RV_GPR_S9 = (1u << 25u), + RV_GPR_S10 = (1u << 26u), + RV_GPR_S11 = (1u << 27u), + RV_GPR_T3 = (1u << 28u), + RV_GPR_T4 = (1u << 29u), + RV_GPR_T5 = (1u << 30u), + RV_GPR_T6 = (1u << 31u), +}; + +/** + * Log current vCPU registers. + * + * @regbm is a bitmap of registers to be dumped [x1..t6], pc replace x0 + */ +void ibex_log_vcpu_registers(uint64_t regbm); + +#endif /* HW_RISCV_IBEX_COMMON_H */ From 5cfa8b2f3f1be43e10800e66cb68944406b2d4db Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Feb 2023 16:59:35 +0100 Subject: [PATCH 05/12] [ot] target/riscv: implement custom LowRisc Ibex CSRs Signed-off-by: Emmanuel Blot Includes existing MIT licenced code (already published elsewhere) --- target/riscv/cpu.c | 2 +- target/riscv/cpu.h | 7 ++++ target/riscv/ibex_csr.c | 85 ++++++++++++++++++++++++++++++++++++++++ target/riscv/meson.build | 1 + 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 target/riscv/ibex_csr.c diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 24a4c2fd3ffa7..dd30b21dd22cd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3056,7 +3056,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zba = true, .cfg.ext_zbb = true, .cfg.ext_zbc = true, - .cfg.ext_zbs = true + .cfg.ext_zbs = true, ), DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E31, TYPE_RISCV_CPU_SIFIVE_E, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 35d1f6362ca84..faf55e8b5ec3f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -481,6 +481,10 @@ struct CPUArchState { uint64_t hstateen[SMSTATEEN_MAX_COUNT]; uint64_t sstateen[SMSTATEEN_MAX_COUNT]; uint64_t henvcfg; + + /* Ibex custom CSRs */ + target_ulong cpuctrlsts; + target_ulong secureseed; #endif /* Fields from here on are preserved across CPU reset. */ @@ -988,5 +992,8 @@ extern const RISCVCSR th_csr_list[]; /* Implemented in mips_csr.c */ extern const RISCVCSR mips_csr_list[]; +/* Implemented in ibex_csr.c */ +extern const RISCVCSR ibex_csr_list[]; + const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/ibex_csr.c b/target/riscv/ibex_csr.c new file mode 100644 index 0000000000000..d62638bbfb39a --- /dev/null +++ b/target/riscv/ibex_csr.c @@ -0,0 +1,85 @@ +/* + * QEMU LowRisc Ibex core features + * + * Copyright (c) 2023 Rivos, Inc. + * + * SPDX-License-Identifier: MIT + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" + +/* Custom CSRs */ +#define CSR_CPUCTRLSTS 0x7c0 +#define CSR_SECURESEED 0x7c1 + +#define CPUCTRLSTS_ICACHE_ENABLE 0x000 +#define CPUCTRLSTS_DATA_IND_TIMING 0x001 +#define CPUCTRLSTS_DUMMY_INSTR_EN 0x002 +#define CPUCTRLSTS_DUMMY_INSTR_MASK 0x038 +#define CPUCTRLSTS_SYNC_EXC_SEEN 0x040 +#define CPUCTRLSTS_DOUBLE_FAULT_SEEN 0x080 +#define CPUCTRLSTS_IC_SCR_KEY_VALID 0x100 + +#if !defined(CONFIG_USER_ONLY) + +static RISCVException read_cpuctrlsts(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = CPUCTRLSTS_IC_SCR_KEY_VALID | env->cpuctrlsts; + return RISCV_EXCP_NONE; +} + +static RISCVException write_cpuctrlsts(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t a) +{ + /* b7 can only be cleared */ + env->cpuctrlsts &= ~0xbf; + /* b6 should be cleared on mret */ + env->cpuctrlsts |= val & 0x3f; + return RISCV_EXCP_NONE; +} + +static RISCVException read_secureseed(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* + * "Seed values are not actually stored in a register and so reads to this + * register will always return zero." + */ + *val = 0; + return RISCV_EXCP_NONE; +} + +static RISCVException write_secureseed(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t a) +{ + (void)val; + return RISCV_EXCP_NONE; +} + +static RISCVException any(CPURISCVState *env, int csrno) +{ + /* + * unfortunately, this predicate is not public, so duplicate the standard + * implementation + */ + return RISCV_EXCP_NONE; +} + +const RISCVCSR ibex_csr_list[] = { + { + .csrno = CSR_CPUCTRLSTS, + .csr_ops = { "cpuctrlsts", any, &read_cpuctrlsts, &write_cpuctrlsts }, + }, + { + .csrno = CSR_SECURESEED, + .csr_ops = { "secureseed", any, &read_secureseed, &write_secureseed }, + }, + {} +}; + +#endif /* !defined(CONFIG_USER_ONLY) */ + diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 3842c7c1a8606..5a174a6feb6b4 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -17,6 +17,7 @@ riscv_ss.add(files( 'cpu.c', 'cpu_helper.c', 'csr.c', + 'ibex_csr.c', 'fpu_helper.c', 'gdbstub.c', 'op_helper.c', From 80d15ea1d8b831c0924c56f6d7b1035e0cbb3b9b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 Mar 2023 19:16:44 +0200 Subject: [PATCH 06/12] [ot] target/riscv: add custom mtvec CSR management Signed-off-by: Emmanuel Blot --- target/riscv/ibex_csr.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/target/riscv/ibex_csr.c b/target/riscv/ibex_csr.c index d62638bbfb39a..a6e815e78215c 100644 --- a/target/riscv/ibex_csr.c +++ b/target/riscv/ibex_csr.c @@ -69,7 +69,37 @@ static RISCVException any(CPURISCVState *env, int csrno) return RISCV_EXCP_NONE; } +static RISCVException read_mtvec(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mtvec; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mtvec(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t a) +{ + /* bits [1:0] encode mode; Ibex only supports 1 = vectored */ + if ((val & 3u) != 1u) { + qemu_log_mask(LOG_UNIMP, + "CSR_MTVEC: reserved mode not supported 0x" TARGET_FMT_lx "\n", + val); + /* WARL */ + return RISCV_EXCP_NONE; + } + + /* bits [7:2] are always 0, address should be aligned in 256 bytes */ + env->mtvec = val & ~0xFCu; + + return RISCV_EXCP_NONE; +} + const RISCVCSR ibex_csr_list[] = { + { + .csrno = CSR_MTVEC, + .csr_ops = { "mtvec", any, &read_mtvec, &write_mtvec }, + }, { .csrno = CSR_CPUCTRLSTS, .csr_ops = { "cpuctrlsts", any, &read_cpuctrlsts, &write_cpuctrlsts }, From 3c53016d93124b3295415315454eaf50d22376af Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Apr 2023 19:45:37 +0200 Subject: [PATCH 07/12] [ot] target/riscv, hw/riscv: add basic OpenTitan EarlGrey machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Define a generic version of lowrisc-ibex core that can be used in several machines: - leave MISA empty so that generic properties can be defined for this core - remove all arbitrary default properties but ISA I,C,U which are mandatory for ibex - define default mtvec which is only support vectored mode - update privilege version (1.12) according to the Ibex documentation - define ibex architecture identifier - remove hart array (mostly useless, its definition is incoherent and prevent from applying properties to CPU cores) 2. Add an EarlGrey machine that uses this new definition Signed-off-by: Loïc Lefort Signed-off-by: Emmanuel Blot Signed-off-by: Lex Bailey --- MAINTAINERS | 7 + hw/Kconfig | 3 + hw/meson.build | 1 + hw/opentitan/Kconfig | 2 + hw/opentitan/meson.build | 2 + hw/opentitan/trace-events | 1 + hw/opentitan/trace.h | 2 + hw/riscv/Kconfig | 7 + hw/riscv/ibex_common.c | 10 +- hw/riscv/meson.build | 1 + hw/riscv/opentitan.c | 30 +- hw/riscv/ot_earlgrey.c | 734 +++++++++++++++++++++++++++++++++ include/hw/riscv/ibex_common.h | 2 +- include/hw/riscv/opentitan.h | 4 +- include/hw/riscv/ot_earlgrey.h | 27 ++ meson.build | 1 + target/riscv/cpu-qom.h | 2 + target/riscv/cpu.c | 14 +- 18 files changed, 829 insertions(+), 21 deletions(-) create mode 100644 hw/opentitan/Kconfig create mode 100644 hw/opentitan/meson.build create mode 100644 hw/opentitan/trace-events create mode 100644 hw/opentitan/trace.h create mode 100644 hw/riscv/ot_earlgrey.c create mode 100644 include/hw/riscv/ot_earlgrey.h diff --git a/MAINTAINERS b/MAINTAINERS index 8b8bb7b554e5b..c7f98e4b61e5e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4460,6 +4460,13 @@ S: Supported F: hw/riscv/ibex_common.c F: include/hw/riscv/ibex_common.h F: include/hw/riscv/ibex_irq.h + +OpenTitan +M: lowRISC +S: Supported +F: hw/opentitan/* +F: include/hw/opentitan/* + Build and test automation ------------------------- Build and test automation, general continuous integration diff --git a/hw/Kconfig b/hw/Kconfig index f8f92b5d03d88..5215240092c82 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -69,6 +69,9 @@ source sparc64/Kconfig source tricore/Kconfig source xtensa/Kconfig +# OpenTitan devices +source opentitan/Kconfig + # Symbols used by multiple targets config TEST_DEVICES bool diff --git a/hw/meson.build b/hw/meson.build index 66e46b8090dba..f00346854e925 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -20,6 +20,7 @@ subdir('sparc') subdir('sparc64') subdir('tricore') subdir('xtensa') +subdir('opentitan') subdir('9pfs') subdir('acpi') diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig new file mode 100644 index 0000000000000..6bd1855a84383 --- /dev/null +++ b/hw/opentitan/Kconfig @@ -0,0 +1,2 @@ +# OpenTitan devices + diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build new file mode 100644 index 0000000000000..6bd1855a84383 --- /dev/null +++ b/hw/opentitan/meson.build @@ -0,0 +1,2 @@ +# OpenTitan devices + diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events new file mode 100644 index 0000000000000..695debd3805e7 --- /dev/null +++ b/hw/opentitan/trace-events @@ -0,0 +1 @@ +# OpenTitan EarlGrey Trace Events diff --git a/hw/opentitan/trace.h b/hw/opentitan/trace.h new file mode 100644 index 0000000000000..17c1fd28ed1c7 --- /dev/null +++ b/hw/opentitan/trace.h @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "trace/trace-hw_opentitan.h" diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 7877f0615c416..76588c43ae3c9 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -12,6 +12,13 @@ config IBEX_COMMON # RISC-V machines in alphabetical order +config OT_EARLGREY + bool + default y + select IBEX + select IBEX_COMMON + select SIFIVE_PLIC + config MICROCHIP_PFSOC bool default y diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c index c6056767c7fc5..6b53662a5b97c 100644 --- a/hw/riscv/ibex_common.c +++ b/hw/riscv/ibex_common.c @@ -17,13 +17,13 @@ #include "disas/disas.h" #include "elf.h" #include "exec/hwaddr.h" -#include "hw/boards.h" -#include "hw/loader.h" +#include "hw/core/boards.h" +#include "hw/core/loader.h" #include "hw/misc/unimp.h" -#include "hw/qdev-core.h" -#include "hw/qdev-properties.h" +#include "hw/core/qdev.h" +#include "hw/core/qdev-properties.h" #include "hw/riscv/ibex_common.h" -#include "hw/sysbus.h" +#include "hw/core/sysbus.h" #include "monitor/monitor.h" diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 70d63f56b52ce..76dc93147ae74 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -4,6 +4,7 @@ riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) riscv_ss.add(when: 'CONFIG_IBEX_COMMON', if_true: files('ibex_common.c')) +riscv_ss.add(when: 'CONFIG_OT_EARLGREY', if_true: files('ot_earlgrey.c')) riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c')) riscv_ss.add(when: 'CONFIG_SHAKTI_C', if_true: files('shakti_c.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 19cb35b3510df..22f8b20f42a26 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -104,7 +104,9 @@ static void opentitan_machine_init(MachineState *machine) riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL); } - riscv_boot_info_init(&boot_info, &s->soc.cpus); + boot_info.kernel_size = 0; + boot_info.is_32bit = 1; + if (machine->kernel_filename) { riscv_load_kernel(machine, &boot_info, memmap[IBEX_DEV_RAM].base, @@ -119,7 +121,7 @@ static void opentitan_machine_class_init(ObjectClass *oc, const void *data) mc->desc = "RISC-V Board compatible with OpenTitan"; mc->init = opentitan_machine_init; mc->max_cpus = 1; - mc->default_cpu_type = TYPE_RISCV_CPU_LOWRISC_IBEX; + mc->default_cpu_type = TYPE_RISCV_CPU_LOWRISC_OPENTITAN; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } @@ -128,7 +130,7 @@ static void lowrisc_ibex_soc_init(Object *obj) { LowRISCIbexSoCState *s = RISCV_IBEX_SOC(obj); - object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); + object_initialize_child(obj, "cpu", &s->cpu, TYPE_RISCV_CPU_LOWRISC_OPENTITAN); object_initialize_child(obj, "plic", &s->plic, TYPE_SIFIVE_PLIC); @@ -152,13 +154,17 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *sys_mem = get_system_memory(); int i; - object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, - &error_abort); - object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, - &error_abort); - object_property_set_int(OBJECT(&s->cpus), "resetvec", s->resetvec, - &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); + Object *cpu = OBJECT(&s->cpu); + object_property_set_int(cpu, "resetvec", s->resetvec, + &error_fatal); + object_property_set_bool(cpu, "m", true, &error_fatal); + object_property_set_bool(cpu, "pmp", true, &error_fatal); + object_property_set_bool(cpu, "zba", true, &error_fatal); + object_property_set_bool(cpu, "zbb", true, &error_fatal); + object_property_set_bool(cpu, "zbc", true, &error_fatal); + object_property_set_bool(cpu, "zbs", true, &error_fatal); + object_property_set_bool(cpu, "smepmp", true, &error_fatal); + qdev_realize(DEVICE(&s->cpu), NULL, &error_fatal); /* Boot ROM */ memory_region_init_rom(&s->rom, OBJECT(dev_soc), "riscv.lowrisc.ibex.rom", @@ -194,10 +200,10 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->plic), 0, memmap[IBEX_DEV_PLIC].base); for (i = 0; i < ms->smp.cpus; i++) { - CPUState *cpu = qemu_get_cpu(i); + CPUState *cpu_i = qemu_get_cpu(i); qdev_connect_gpio_out(DEVICE(&s->plic), ms->smp.cpus + i, - qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); + qdev_get_gpio_in(DEVICE(cpu_i), IRQ_M_EXT)); } /* UART */ diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c new file mode 100644 index 0000000000000..760a8d2c2e604 --- /dev/null +++ b/hw/riscv/ot_earlgrey.c @@ -0,0 +1,734 @@ +/* + * QEMU RISC-V Board Compatible with OpenTitan EarlGrey FPGA platform + * + * Copyright (c) 2022-2023 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * + * This implementation is based on OpenTitan RTL version: + * + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "system/address-spaces.h" +#include "hw/core/boards.h" +#include "hw/intc/sifive_plic.h" +#include "hw/misc/unimp.h" +#include "hw/core/qdev-properties.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ot_earlgrey.h" +#include "system/system.h" + +/* ------------------------------------------------------------------------ */ +/* Constants */ +/* ------------------------------------------------------------------------ */ + +#define OT_EARLGREY_PERIPHERAL_CLK_HZ 2500000u + +enum OtEarlgreySocMemory { + OT_EARLGREY_SOC_MEM_ROM, + OT_EARLGREY_SOC_MEM_RAM, + OT_EARLGREY_SOC_MEM_FLASH, +}; + +static const MemMapEntry ot_earlgrey_soc_memories[] = { + [OT_EARLGREY_SOC_MEM_ROM] = { 0x00008000u, 0x8000u }, + [OT_EARLGREY_SOC_MEM_RAM] = { 0x10000000u, 0x20000u }, + [OT_EARLGREY_SOC_MEM_FLASH] = { 0x20000000u, 0x100000u }, +}; + +enum OtEarlgreySocDevice { + OT_EARLGREY_SOC_DEV_ADC_CTRL, + OT_EARLGREY_SOC_DEV_AES, + OT_EARLGREY_SOC_DEV_ALERT_HANDLER, + OT_EARLGREY_SOC_DEV_AON_TIMER, + OT_EARLGREY_SOC_DEV_AST, + OT_EARLGREY_SOC_DEV_CLKMGR, + OT_EARLGREY_SOC_DEV_CSRNG, + OT_EARLGREY_SOC_DEV_EDN0, + OT_EARLGREY_SOC_DEV_EDN1, + OT_EARLGREY_SOC_DEV_ENTROPY_SRC, + OT_EARLGREY_SOC_DEV_FLASH_CTRL, + OT_EARLGREY_SOC_DEV_FLASH_CTRL_PRIM, + OT_EARLGREY_SOC_DEV_GPIO, + OT_EARLGREY_SOC_DEV_HART, + OT_EARLGREY_SOC_DEV_HMAC, + OT_EARLGREY_SOC_DEV_I2C0, + OT_EARLGREY_SOC_DEV_I2C1, + OT_EARLGREY_SOC_DEV_I2C2, + OT_EARLGREY_SOC_DEV_IBEX_WRAPPER, + OT_EARLGREY_SOC_DEV_KEYMGR, + OT_EARLGREY_SOC_DEV_KMAC, + OT_EARLGREY_SOC_DEV_LC_CTRL, + OT_EARLGREY_SOC_DEV_OTBN, + OT_EARLGREY_SOC_DEV_OTP_CTRL, + OT_EARLGREY_SOC_DEV_OTP_CTRL_PRIM, + OT_EARLGREY_SOC_DEV_PATTGEN, + OT_EARLGREY_SOC_DEV_PINMUX, + OT_EARLGREY_SOC_DEV_PLIC, + OT_EARLGREY_SOC_DEV_PWM, + OT_EARLGREY_SOC_DEV_PWRMGR, + OT_EARLGREY_SOC_DEV_RAM_RET, + OT_EARLGREY_SOC_DEV_ROM_CTRL, + OT_EARLGREY_SOC_DEV_RSTMGR, + OT_EARLGREY_SOC_DEV_RV_DM, + OT_EARLGREY_SOC_DEV_RV_DM_MEM, + OT_EARLGREY_SOC_DEV_SENSOR_CTRL, + OT_EARLGREY_SOC_DEV_SPI_DEVICE, + OT_EARLGREY_SOC_DEV_SPI_HOST0, + OT_EARLGREY_SOC_DEV_SPI_HOST1, + OT_EARLGREY_SOC_DEV_SRAM_CTRL, + OT_EARLGREY_SOC_DEV_SRAM_CTRL_MAIN, + OT_EARLGREY_SOC_DEV_SYSRST_CTRL, + OT_EARLGREY_SOC_DEV_TIMER, + OT_EARLGREY_SOC_DEV_UART0, + OT_EARLGREY_SOC_DEV_UART1, + OT_EARLGREY_SOC_DEV_UART2, + OT_EARLGREY_SOC_DEV_UART3, + OT_EARLGREY_SOC_DEV_USBDEV, +}; + +#define OT_EARLGREY_SOC_GPIO(_irq_, _target_, _num_) \ + IBEX_GPIO(_irq_, OT_EARLGREY_SOC_DEV_##_target_, _num_) + +#define OT_EARLGREY_SOC_GPIO_SYSBUS_IRQ(_irq_, _target_, _num_) \ + IBEX_GPIO_SYSBUS_IRQ(_irq_, OT_EARLGREY_SOC_DEV_##_target_, _num_) + +#define OT_EARLGREY_SOC_DEVLINK(_pname_, _target_) \ + IBEX_DEVLINK(_pname_, OT_EARLGREY_SOC_DEV_##_target_) + +/* + * MMIO/interrupt mapping as per: + * lowRISC/opentitan: hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h + * and + * lowRISC/opentitan: hw/top_earlgrey/sw/autogen/top_earlgrey.h + */ +static const IbexDeviceDef ot_earlgrey_soc_devices[] = { + /* clang-format off */ + [OT_EARLGREY_SOC_DEV_HART] = { + .type = TYPE_RISCV_CPU_LOWRISC_OPENTITAN, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_BOOL_PROP("zba", true), + IBEX_DEV_BOOL_PROP("zbb", true), + IBEX_DEV_BOOL_PROP("zbc", true), + IBEX_DEV_BOOL_PROP("zbs", true), + IBEX_DEV_BOOL_PROP("smepmp", true) + ), + }, + [OT_EARLGREY_SOC_DEV_RV_DM_MEM] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-rv_dm_mem", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x00010000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_UART0] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-uart", + .cfg = &ibex_unimp_configure, + .instance = 0, + .memmap = MEMMAPENTRIES( + { 0x40000000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_UART1] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-uart", + .cfg = &ibex_unimp_configure, + .instance = 1, + .memmap = MEMMAPENTRIES( + { 0x40010000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_UART2] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-uart", + .cfg = &ibex_unimp_configure, + .instance = 2, + .memmap = MEMMAPENTRIES( + { 0x40020000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_UART3] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-uart", + .cfg = &ibex_unimp_configure, + .instance = 3, + .memmap = MEMMAPENTRIES( + { 0x40030000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_GPIO] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-gpio", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40040000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_SPI_DEVICE] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-spi_device", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40050000u, 0x2000u } + ), + }, + [OT_EARLGREY_SOC_DEV_I2C0] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-i2c", + .cfg = &ibex_unimp_configure, + .instance = 0, + .memmap = MEMMAPENTRIES( + { 0x40080000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_I2C1] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-i2c", + .cfg = &ibex_unimp_configure, + .instance = 1, + .memmap = MEMMAPENTRIES( + { 0x40090000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_I2C2] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-i2c", + .cfg = &ibex_unimp_configure, + .instance = 2, + .memmap = MEMMAPENTRIES( + { 0x400a0000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_PATTGEN] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-pattgen", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x400e0000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_TIMER] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-timer", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40100000u, 0x200u } + ), + }, + [OT_EARLGREY_SOC_DEV_OTP_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-otp_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40130000u, 0x2000u } + ), + }, + [OT_EARLGREY_SOC_DEV_OTP_CTRL_PRIM] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-ot_ctrl_prim", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40132000u, 0x20u } + ), + }, + [OT_EARLGREY_SOC_DEV_LC_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-lc_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40140000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_ALERT_HANDLER] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-alert_handler", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40150000u, 0x800u } + ), + }, + [OT_EARLGREY_SOC_DEV_SPI_HOST0] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-spi_host", + .cfg = &ibex_unimp_configure, + .instance = 0, + .memmap = MEMMAPENTRIES( + { 0x40300000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_SPI_HOST1] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-spi_host", + .cfg = &ibex_unimp_configure, + .instance = 1, + .memmap = MEMMAPENTRIES( + { 0x40310000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_USBDEV] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-usbdev", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40320000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_PWRMGR] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-pwrmgr", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40400000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_RSTMGR] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-rstmgr", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40410000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_CLKMGR] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-clkmgr", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40420000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_SYSRST_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-sysrst_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40430000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_ADC_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-adc_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40440000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_PWM] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-pwm", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40450000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_PINMUX] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-pinmux", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40460000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_AON_TIMER] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-aon_timer", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40470000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_AST] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-ast", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40480000u, 0x400u } + ), + }, + [OT_EARLGREY_SOC_DEV_SENSOR_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-sensor_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40490000u, 0x40u } + ), + }, + [OT_EARLGREY_SOC_DEV_SRAM_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-sram_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40500000u, 0x20u } + ), + }, + [OT_EARLGREY_SOC_DEV_RAM_RET] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-ram_ret", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x40600000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_FLASH_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-flash_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41000000u, 0x200u } + ), + }, + [OT_EARLGREY_SOC_DEV_FLASH_CTRL_PRIM] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-flash_ctrl_prim", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41008000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_AES] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-aes", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41100000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_HMAC] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-hmac", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41110000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_KMAC] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-kmac", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41120000u, 0x1000u } + ), + }, + [OT_EARLGREY_SOC_DEV_OTBN] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-otbn", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41130000u, 0x10000u } + ), + }, + [OT_EARLGREY_SOC_DEV_KEYMGR] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-keymgr", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41140000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_CSRNG] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-csrng", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41150000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_ENTROPY_SRC] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-entropy_src", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41160000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_EDN0] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-edn", + .cfg = &ibex_unimp_configure, + .instance = 0, + .memmap = MEMMAPENTRIES( + { 0x41170000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_EDN1] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-edn", + .cfg = &ibex_unimp_configure, + .instance = 1, + .memmap = MEMMAPENTRIES( + { 0x41180000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_SRAM_CTRL_MAIN] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-sram_ctrl_main", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x411c0000u, 0x20u } + ), + }, + [OT_EARLGREY_SOC_DEV_ROM_CTRL] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-rom_ctrl", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x411e0000u, 0x80u } + ), + }, + [OT_EARLGREY_SOC_DEV_IBEX_WRAPPER] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-ibex_wrapper", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x411f0000u, 0x100u } + ), + }, + [OT_EARLGREY_SOC_DEV_RV_DM] = { + .type = TYPE_UNIMPLEMENTED_DEVICE, + .name = "ot-rv_dm", + .cfg = &ibex_unimp_configure, + .memmap = MEMMAPENTRIES( + { 0x41200000u, 0x4u } + ), + }, + [OT_EARLGREY_SOC_DEV_PLIC] = { + .type = TYPE_SIFIVE_PLIC, + .memmap = MEMMAPENTRIES( + { 0x48000000u, 0x8000000u } + ), + .gpio = IBEXGPIOCONNDEFS( + OT_EARLGREY_SOC_GPIO(1, HART, IRQ_M_EXT) + ), + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_STRING_PROP("hart-config", "M"), + IBEX_DEV_UINT_PROP("hartid-base", 0u), + /* note: should always be max_irq + 1 */ + IBEX_DEV_UINT_PROP("num-sources", 185u), + IBEX_DEV_UINT_PROP("num-priorities", 3u), + IBEX_DEV_UINT_PROP("priority-base", 0x0u), + IBEX_DEV_UINT_PROP("pending-base", 0x1000u), + IBEX_DEV_UINT_PROP("enable-base", 0x2000u), + IBEX_DEV_UINT_PROP("enable-stride", 32u), + IBEX_DEV_UINT_PROP("context-base", 0x200000u), + IBEX_DEV_UINT_PROP("context-stride", 8u), + IBEX_DEV_UINT_PROP("aperture-size", 0x8000000u) + ), + }, + /* clang-format on */ +}; + +enum OtEarlgreyBoardDevice { + OT_EARLGREY_BOARD_DEV_SOC, + _OT_EARLGREY_BOARD_DEV_COUNT, +}; + +/* ------------------------------------------------------------------------ */ +/* Type definitions */ +/* ------------------------------------------------------------------------ */ + +struct OtEarlGreySoCState { + SysBusDevice parent_obj; + + DeviceState **devices; + MemoryRegion *memories; +}; + +struct OtEarlGreyBoardState { + DeviceState parent_obj; + + DeviceState **devices; +}; + +struct OtEarlGreyMachineState { + MachineState parent_obj; +}; + +/* ------------------------------------------------------------------------ */ +/* SoC */ +/* ------------------------------------------------------------------------ */ + +static void ot_earlgrey_soc_reset(DeviceState *dev) +{ + OtEarlGreySoCState *s = RISCV_OT_EARLGREY_SOC(dev); + + cpu_reset(CPU(s->devices[OT_EARLGREY_SOC_DEV_HART])); +} + +static void ot_earlgrey_soc_realize(DeviceState *dev, Error **errp) +{ + OtEarlGreySoCState *s = RISCV_OT_EARLGREY_SOC(dev); + const MemMapEntry *memmap = &ot_earlgrey_soc_memories[0]; + + MachineState *ms = MACHINE(qdev_get_machine()); + MemoryRegion *sys_mem = get_system_memory(); + + /* RAM */ + memory_region_add_subregion(sys_mem, memmap[OT_EARLGREY_SOC_MEM_RAM].base, + ms->ram); + + /* Boot ROM */ + memory_region_init_rom(&s->memories[OT_EARLGREY_SOC_MEM_ROM], OBJECT(dev), + "ot-rom", memmap[OT_EARLGREY_SOC_MEM_ROM].size, + &error_fatal); + memory_region_add_subregion(sys_mem, memmap[OT_EARLGREY_SOC_MEM_ROM].base, + &s->memories[OT_EARLGREY_SOC_MEM_ROM]); + + /* Flash memory */ + memory_region_init_rom(&s->memories[OT_EARLGREY_SOC_MEM_FLASH], OBJECT(dev), + "ot-flash", memmap[OT_EARLGREY_SOC_MEM_FLASH].size, + &error_fatal); + memory_region_add_subregion(sys_mem, memmap[OT_EARLGREY_SOC_MEM_FLASH].base, + &s->memories[OT_EARLGREY_SOC_MEM_FLASH]); + + /* Link, define properties and realize devices, then connect GPIOs */ + ibex_link_devices(s->devices, ot_earlgrey_soc_devices, + ARRAY_SIZE(ot_earlgrey_soc_devices)); + ibex_define_device_props(s->devices, ot_earlgrey_soc_devices, + ARRAY_SIZE(ot_earlgrey_soc_devices)); + ibex_realize_system_devices(s->devices, ot_earlgrey_soc_devices, + ARRAY_SIZE(ot_earlgrey_soc_devices)); + ibex_connect_devices(s->devices, ot_earlgrey_soc_devices, + ARRAY_SIZE(ot_earlgrey_soc_devices)); + + /* load kernel if provided */ + ibex_load_kernel(NULL); +} + +static void ot_earlgrey_soc_init(Object *obj) +{ + OtEarlGreySoCState *s = RISCV_OT_EARLGREY_SOC(obj); + + s->devices = + ibex_create_devices(ot_earlgrey_soc_devices, + ARRAY_SIZE(ot_earlgrey_soc_devices), DEVICE(s)); + s->memories = g_new0(MemoryRegion, ARRAY_SIZE(ot_earlgrey_soc_memories)); +} + +static void ot_earlgrey_soc_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->legacy_reset = &ot_earlgrey_soc_reset; + dc->realize = &ot_earlgrey_soc_realize; + dc->user_creatable = false; +} + +static const TypeInfo ot_earlgrey_soc_type_info = { + .name = TYPE_RISCV_OT_EARLGREY_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtEarlGreySoCState), + .instance_init = &ot_earlgrey_soc_init, + .class_init = &ot_earlgrey_soc_class_init, +}; + +static void ot_earlgrey_soc_register_types(void) +{ + type_register_static(&ot_earlgrey_soc_type_info); +} + +type_init(ot_earlgrey_soc_register_types); + +/* ------------------------------------------------------------------------ */ +/* Board */ +/* ------------------------------------------------------------------------ */ + +static void ot_earlgrey_board_realize(DeviceState *dev, Error **errp) +{ + OtEarlGreyBoardState *board = RISCV_OT_EARLGREY_BOARD(dev); + + DeviceState *soc = board->devices[OT_EARLGREY_BOARD_DEV_SOC]; + object_property_add_child(OBJECT(board), "soc", OBJECT(soc)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(soc), &error_fatal); +} + +static void ot_earlgrey_board_init(Object *obj) +{ + OtEarlGreyBoardState *s = RISCV_OT_EARLGREY_BOARD(obj); + + s->devices = g_new0(DeviceState *, _OT_EARLGREY_BOARD_DEV_COUNT); + s->devices[OT_EARLGREY_BOARD_DEV_SOC] = + qdev_new(TYPE_RISCV_OT_EARLGREY_SOC); +} + +static void ot_earlgrey_board_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = &ot_earlgrey_board_realize; +} + +static const TypeInfo ot_earlgrey_board_type_info = { + .name = TYPE_RISCV_OT_EARLGREY_BOARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(OtEarlGreyBoardState), + .instance_init = &ot_earlgrey_board_init, + .class_init = &ot_earlgrey_board_class_init, +}; + +static void ot_earlgrey_board_register_types(void) +{ + type_register_static(&ot_earlgrey_board_type_info); +} + +type_init(ot_earlgrey_board_register_types); + +/* ------------------------------------------------------------------------ */ +/* Machine */ +/* ------------------------------------------------------------------------ */ + +static void ot_earlgrey_machine_instance_init(Object *obj) +{ + OtEarlGreyMachineState *s = RISCV_OT_EARLGREY_MACHINE(obj); + + /* nothing here */ + (void)s; +} + +static void ot_earlgrey_machine_init(MachineState *state) +{ + DeviceState *dev = qdev_new(TYPE_RISCV_OT_EARLGREY_BOARD); + + object_property_add_child(OBJECT(state), "board", OBJECT(dev)); + qdev_realize(dev, NULL, &error_fatal); +} + +static void ot_earlgrey_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "RISC-V Board compatible with OpenTitan EarlGrey FPGA platform"; + mc->init = ot_earlgrey_machine_init; + mc->max_cpus = 1u; + mc->default_cpu_type = + ot_earlgrey_soc_devices[OT_EARLGREY_SOC_DEV_HART].type; + mc->default_ram_id = "ot-ram"; + mc->default_ram_size = + ot_earlgrey_soc_memories[OT_EARLGREY_SOC_MEM_RAM].size; +} + +static const TypeInfo ot_earlgrey_machine_type_info = { + .name = TYPE_RISCV_OT_EARLGREY_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(OtEarlGreyMachineState), + .instance_init = &ot_earlgrey_machine_instance_init, + .class_init = &ot_earlgrey_machine_class_init, +}; + +static void ot_earlgrey_machine_register_types(void) +{ + type_register_static(&ot_earlgrey_machine_type_info); +} + +type_init(ot_earlgrey_machine_register_types); diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index 6c7dae5cbe275..963ab1fa78d3c 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qom/object.h" #include "exec/hwaddr.h" -#include "hw/qdev-core.h" +#include "hw/core/qdev.h" /* ------------------------------------------------------------------------ */ diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index 5b9016e1d897f..0da19c671fb82 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -19,13 +19,13 @@ #ifndef HW_OPENTITAN_H #define HW_OPENTITAN_H -#include "hw/riscv/riscv_hart.h" #include "hw/intc/sifive_plic.h" #include "hw/char/ibex_uart.h" #include "hw/timer/ibex_timer.h" #include "hw/ssi/ibex_spi_host.h" #include "hw/core/boards.h" #include "qom/object.h" +#include "target/riscv/cpu.h" #define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc" OBJECT_DECLARE_SIMPLE_TYPE(LowRISCIbexSoCState, RISCV_IBEX_SOC) @@ -41,7 +41,7 @@ struct LowRISCIbexSoCState { SysBusDevice parent_obj; /*< public >*/ - RISCVHartArrayState cpus; + RISCVCPU cpu; SiFivePLICState plic; IbexUartState uart; IbexTimerState timer; diff --git a/include/hw/riscv/ot_earlgrey.h b/include/hw/riscv/ot_earlgrey.h new file mode 100644 index 0000000000000..650f53a9ddfce --- /dev/null +++ b/include/hw/riscv/ot_earlgrey.h @@ -0,0 +1,27 @@ +/* + * QEMU RISC-V Board Compatible with OpenTitan EarlGrey FPGA platform + * + * Copyright (c) 2022-2023 Rivos, Inc. + * + * Author(s): + * Loïc Lefort + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef HW_RISCV_OT_EARLGREY_H +#define HW_RISCV_OT_EARLGREY_H + +#include "qom/object.h" + +#define TYPE_RISCV_OT_EARLGREY_MACHINE MACHINE_TYPE_NAME("ot-earlgrey") +OBJECT_DECLARE_SIMPLE_TYPE(OtEarlGreyMachineState, RISCV_OT_EARLGREY_MACHINE) + +#define TYPE_RISCV_OT_EARLGREY_BOARD "riscv.ot_earlgrey.board" +OBJECT_DECLARE_SIMPLE_TYPE(OtEarlGreyBoardState, RISCV_OT_EARLGREY_BOARD) + +#define TYPE_RISCV_OT_EARLGREY_SOC "riscv.ot_earlgrey.soc" +OBJECT_DECLARE_SIMPLE_TYPE(OtEarlGreySoCState, RISCV_OT_EARLGREY_SOC) + +#endif /* HW_RISCV_OT_EARLGREY_H */ diff --git a/meson.build b/meson.build index 4af32c3e1f2dd..55a85ec895d6a 100644 --- a/meson.build +++ b/meson.build @@ -3626,6 +3626,7 @@ if have_system 'hw/nubus', 'hw/nvme', 'hw/nvram', + 'hw/opentitan', 'hw/pci', 'hw/pci-host', 'hw/ppc', diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 3a6394fd11817..418b56d69ed11 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -43,6 +43,8 @@ #define TYPE_RISCV_CPU_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") #define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") #define TYPE_RISCV_CPU_LOWRISC_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") +#define TYPE_RISCV_CPU_LOWRISC_OPENTITAN \ + RISCV_CPU_TYPE_NAME("lowrisc-opentitan") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") #define TYPE_RISCV_CPU_SIFIVE_E RISCV_CPU_TYPE_NAME("sifive-e") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index dd30b21dd22cd..507061e8e99fe 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3043,13 +3043,25 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV32, ), - DEFINE_RISCV_CPU(TYPE_RISCV_CPU_LOWRISC_IBEX, TYPE_RISCV_VENDOR_CPU, + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_LOWRISC_IBEX, + TYPE_RISCV_VENDOR_CPU, .misa_mxl_max = MXL_RV32, .misa_ext = RVI | RVM | RVC | RVU, .priv_spec = PRIV_VERSION_1_12_0, .cfg.max_satp_mode = VM_1_10_MBARE, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, + + .cfg.marchid = 0x16u, + +#ifndef CONFIG_USER_ONLY + .custom_csrs = ibex_csr_list, +#endif + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_LOWRISC_OPENTITAN, + TYPE_RISCV_CPU_LOWRISC_IBEX, .cfg.pmp = true, .cfg.ext_smepmp = true, From 81da2a69ca663b93045a228bd43d8a5cb5a935a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 15 Feb 2023 18:17:31 +0100 Subject: [PATCH 08/12] [ot] target/riscv: add support for impl-defined initial PMP config Signed-off-by: Emmanuel Blot --- target/riscv/cpu.c | 24 ++++++++++++++++++++++++ target/riscv/cpu_cfg_fields.h.inc | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 507061e8e99fe..73aae7fd14219 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -792,6 +792,25 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) if (kvm_enabled()) { kvm_riscv_reset_vcpu(cpu); } + + /* default physical memory protection configuration */ + const RISCVCPUConfig *cfg = &cpu->cfg; + g_assert(cfg->pmp_cfg_count <= MAX_RISCV_PMPS); + g_assert(cfg->pmp_addr_count <= MAX_RISCV_PMPS); + for (i = 0; i < MAX_RISCV_PMPS; i++) { + env->pmp_state.pmp[i].cfg_reg = + i < cfg->pmp_cfg_count ? cfg->pmp_cfg[i] : 0; + } + for (i = 0; i < MAX_RISCV_PMPS; i++) { + env->pmp_state.pmp[i].addr_reg = + i < cfg->pmp_addr_count ? (target_ulong)cfg->pmp_addr[i] : 0; + } + for (i = 0; i < MAX_RISCV_PMPS; i++) { + pmp_update_rule_addr(env, i); + } + pmp_update_rule_nums(env); + + env->mseccfg = (target_ulong)cfg->mseccfg; #endif } @@ -2670,6 +2689,11 @@ static const Property riscv_cpu_properties[] = { DEFAULT_RNMI_IRQVEC), DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec, DEFAULT_RNMI_EXCPVEC), + DEFINE_PROP_UINT64("mseccfg", RISCVCPU, cfg.mseccfg, 0u), + DEFINE_PROP_ARRAY("pmp_cfg", RISCVCPU, cfg.pmp_cfg_count, cfg.pmp_cfg, + qdev_prop_uint8, uint8_t), + DEFINE_PROP_ARRAY("pmp_addr", RISCVCPU, cfg.pmp_addr_count, cfg.pmp_addr, + qdev_prop_uint64, uint64_t), #endif DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 70ec650abfa72..39ca957aa6114 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -176,5 +176,12 @@ TYPED_FIELD(uint32_t, pmp_granularity, 0) TYPED_FIELD(int8_t, max_satp_mode, -1) +/* physical memory protection HW configuration */ +TYPED_FIELD(uint8_t *, pmp_cfg, 0) +TYPED_FIELD(uint32_t, pmp_cfg_count, 0) +TYPED_FIELD(uint64_t *, pmp_addr, 0) +TYPED_FIELD(uint32_t, pmp_addr_count, 0) +TYPED_FIELD(uint64_t, mseccfg, 0) + #undef BOOL_FIELD #undef TYPED_FIELD From dbd76d259734174c9534bddd915a113b72931672 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 15 Feb 2023 18:18:54 +0100 Subject: [PATCH 09/12] [ot] hw/riscv: ot_earlgrey: add HW PMP configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit also adds property no_epmp_cfg on EarlGrey machine to disable default ePMP configuration. Usage: qemu-system-riscv32 -M ot-earlgrey,no-epmp-cfg=true [...] Signed-off-by: Emmanuel Blot Signed-off-by: Loïc Lefort --- hw/riscv/ot_earlgrey.c | 116 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 760a8d2c2e604..77fb1b207c80e 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -18,6 +18,7 @@ #include "qemu/cutils.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qobject/qlist.h" #include "cpu.h" #include "system/address-spaces.h" #include "hw/core/boards.h" @@ -28,6 +29,13 @@ #include "hw/riscv/ot_earlgrey.h" #include "system/system.h" +/* ------------------------------------------------------------------------ */ +/* Forward Declarations */ +/* ------------------------------------------------------------------------ */ + +static void ot_earlgrey_soc_hart_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); + /* ------------------------------------------------------------------------ */ /* Constants */ /* ------------------------------------------------------------------------ */ @@ -116,6 +124,7 @@ static const IbexDeviceDef ot_earlgrey_soc_devices[] = { /* clang-format off */ [OT_EARLGREY_SOC_DEV_HART] = { .type = TYPE_RISCV_CPU_LOWRISC_OPENTITAN, + .cfg = &ot_earlgrey_soc_hart_configure, .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_BOOL_PROP("zba", true), IBEX_DEV_BOOL_PROP("zbb", true), @@ -529,6 +538,60 @@ static const IbexDeviceDef ot_earlgrey_soc_devices[] = { /* clang-format on */ }; +#define PMP_CFG(_l_, _a_, _x_, _w_, _r_) \ + ((uint8_t)(((_l_) << 7u) | ((_a_) << 3u) | ((_x_) << 2u) | ((_w_) << 1u) | \ + ((_r_)))) +#define PMP_ADDR(_a_) ((_a_) >> 2u) + +#define MSECCFG(_rlb_, _mmwp_, _mml_) \ + (((_rlb_) << 2u) | ((_mmwp_) << 1u) | ((_mml_))) + +enum { PMP_MODE_OFF, PMP_MODE_TOR, PMP_MODE_NA4, PMP_MODE_NAPOT }; + +static const uint8_t ot_earlgrey_pmp_cfgs[] = { + /* clang-format off */ + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(1, PMP_MODE_NAPOT, 1, 0, 1), /* rgn 2 [ROM: LRX] */ + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(1, PMP_MODE_TOR, 0, 1, 1), /* rgn 11 [MMIO: LRW] */ + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(1, PMP_MODE_NAPOT, 1, 1, 1), /* rgn 13 [DV_ROM: LRWX] */ + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0), + PMP_CFG(0, PMP_MODE_OFF, 0, 0, 0) + /* clang-format on */ +}; + +static const uint32_t ot_earlgrey_pmp_addrs[] = { + /* clang-format off */ + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x000083fc), /* rgn 2 [ROM: base=0x0000_8000 size (2KiB)] */ + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000), + PMP_ADDR(0x40000000), /* rgn 10 [MMIO: lo=0x4000_0000] */ + PMP_ADDR(0x42010000), /* rgn 11 [MMIO: hi=0x4201_0000] */ + PMP_ADDR(0x00000000), + PMP_ADDR(0x000107fc), /* rgn 13 [DV_ROM: base=0x0001_0000 size (4KiB)] */ + PMP_ADDR(0x00000000), + PMP_ADDR(0x00000000) + /* clang-format on */ +}; + +#define OT_EARLGREY_MSECCFG MSECCFG(1, 1, 0) + enum OtEarlgreyBoardDevice { OT_EARLGREY_BOARD_DEV_SOC, _OT_EARLGREY_BOARD_DEV_COUNT, @@ -553,8 +616,38 @@ struct OtEarlGreyBoardState { struct OtEarlGreyMachineState { MachineState parent_obj; + + bool no_epmp_cfg; }; +/* ------------------------------------------------------------------------ */ +/* Device Configuration */ +/* ------------------------------------------------------------------------ */ + +static void ot_earlgrey_soc_hart_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + OtEarlGreyMachineState *ms = RISCV_OT_EARLGREY_MACHINE(qdev_get_machine()); + if (ms->no_epmp_cfg) { + /* skip default PMP config */ + return; + } + + QList *pmp_cfgs = qlist_new(); + QList *pmp_addrs = qlist_new(); + + for (unsigned ix = 0; ix < ARRAY_SIZE(ot_earlgrey_pmp_cfgs); ix++) { + qlist_append_int(pmp_cfgs, (uint64_t)ot_earlgrey_pmp_cfgs[ix]); + } + for (unsigned ix = 0; ix < ARRAY_SIZE(ot_earlgrey_pmp_addrs); ix++) { + qlist_append_int(pmp_addrs, (uint64_t)ot_earlgrey_pmp_addrs[ix]); + } + + qdev_prop_set_array(dev, "pmp_cfg", pmp_cfgs); + qdev_prop_set_array(dev, "pmp_addr", pmp_addrs); + qdev_prop_set_uint64(dev, "mseccfg", (uint64_t)OT_EARLGREY_MSECCFG); +} + /* ------------------------------------------------------------------------ */ /* SoC */ /* ------------------------------------------------------------------------ */ @@ -688,12 +781,31 @@ type_init(ot_earlgrey_board_register_types); /* Machine */ /* ------------------------------------------------------------------------ */ +static bool ot_earlgrey_machine_get_no_epmp_cfg(Object *obj, Error **errp) +{ + OtEarlGreyMachineState *s = RISCV_OT_EARLGREY_MACHINE(obj); + + return s->no_epmp_cfg; +} + +static void +ot_earlgrey_machine_set_no_epmp_cfg(Object *obj, bool value, Error **errp) +{ + OtEarlGreyMachineState *s = RISCV_OT_EARLGREY_MACHINE(obj); + + s->no_epmp_cfg = value; +} + static void ot_earlgrey_machine_instance_init(Object *obj) { OtEarlGreyMachineState *s = RISCV_OT_EARLGREY_MACHINE(obj); - /* nothing here */ - (void)s; + s->no_epmp_cfg = false; + object_property_add_bool(obj, "no-epmp-cfg", + &ot_earlgrey_machine_get_no_epmp_cfg, + &ot_earlgrey_machine_set_no_epmp_cfg); + object_property_set_description(obj, "no-epmp-cfg", + "Skip default ePMP configuration"); } static void ot_earlgrey_machine_init(MachineState *state) From c554eb83757cf98e24edfe214e1ba8b57ab39621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Fri, 17 Feb 2023 16:10:15 +0100 Subject: [PATCH 10/12] [ot] hw/opentitan: add OpenTitan shadow register helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Lefort --- include/hw/opentitan/ot_common.h | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 include/hw/opentitan/ot_common.h diff --git a/include/hw/opentitan/ot_common.h b/include/hw/opentitan/ot_common.h new file mode 100644 index 0000000000000..546a7d88a8772 --- /dev/null +++ b/include/hw/opentitan/ot_common.h @@ -0,0 +1,93 @@ +/* + * QEMU RISC-V Helpers for OpenTitan EarlGrey + * + * Copyright (c) 2023 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_RISCV_OT_COMMON_H +#define HW_RISCV_OT_COMMON_H + +#include "qemu/osdep.h" + +/* ------------------------------------------------------------------------ */ +/* Shadow Registers */ +/* ------------------------------------------------------------------------ */ + +/* + * Shadow register, concept documented at: + * https://docs.opentitan.org/doc/rm/register_tool/#shadow-registers + */ +typedef struct OtShadowReg { + /* committed register value */ + uint32_t committed; + /* staged register value */ + uint32_t staged; + /* true if 'staged' holds a value */ + bool staged_p; +} OtShadowReg; + +enum { + OT_SHADOW_REG_ERROR = -1, + OT_SHADOW_REG_COMMITTED = 0, + OT_SHADOW_REG_STAGED = 1, +}; + +/** + * Initialize a shadow register with a committed value and no staged value + */ +static inline void ot_shadow_reg_init(OtShadowReg *sreg, uint32_t value) +{ + sreg->committed = value; + sreg->staged_p = false; +} + +/** + * Write a new value to a shadow register. + * If no value was previously staged, the new value is only staged for next + * write and the function returns OT_SHADOW_REG_STAGED. + * If a value was previously staged and the new value is different, the function + * returns OT_SHADOW_REG_ERROR and the new value is ignored. Otherwise the value + * is committed, the staged value is discarded and the function returns + * OT_SHADOW_REG_COMMITTED. + */ +static inline int ot_shadow_reg_write(OtShadowReg *sreg, uint32_t value) +{ + if (sreg->staged_p) { + if (value != sreg->staged) { + /* second write is different, return error status */ + return OT_SHADOW_REG_ERROR; + } + sreg->committed = value; + sreg->staged_p = false; + return OT_SHADOW_REG_COMMITTED; + } else { + sreg->staged = value; + sreg->staged_p = true; + return OT_SHADOW_REG_STAGED; + } +} + +/** + * Return the current committed register value + */ +static inline uint32_t ot_shadow_reg_peek(const OtShadowReg *sreg) +{ + return sreg->committed; +} + +/** + * Discard the staged value and return the current committed register value + */ +static inline uint32_t ot_shadow_reg_read(OtShadowReg *sreg) +{ + sreg->staged_p = false; + return sreg->committed; +} + +#endif /* HW_RISCV_OT_COMMON_H */ From bbbcf08cf674743ad03a5880b558509ae78174e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 31 Jan 2023 14:52:45 +0100 Subject: [PATCH 11/12] [ot] hw/riscv: add an IRQ wrapper Signed-off-by: Emmanuel Blot Includes existing MIT licenced code (already published elsewhere) --- include/hw/riscv/ibex_irq.h | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 include/hw/riscv/ibex_irq.h diff --git a/include/hw/riscv/ibex_irq.h b/include/hw/riscv/ibex_irq.h new file mode 100644 index 0000000000000..22ccff6d5e13f --- /dev/null +++ b/include/hw/riscv/ibex_irq.h @@ -0,0 +1,69 @@ +/* + * QEMU lowRISC Ibex IRQ wrapper + * + * Copyright (c) 2022-2023 Rivos, Inc. + * + * SPDX-License-Identifier: MIT + * + */ + +#ifndef HW_RISCV_IBEX_IRQ_H +#define HW_RISCV_IBEX_IRQ_H + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "hw/irq.h" +#include "hw/qdev-core.h" +#include "hw/sysbus.h" + + +/** Simple IRQ wrapper to limit propagation of no-change calls */ +typedef struct { + qemu_irq irq; + int level; +} IbexIRQ; + +static inline bool ibex_irq_set(IbexIRQ *ibex_irq, int level) +{ + if (level != ibex_irq->level) { + ibex_irq->level = level; + qemu_set_irq(ibex_irq->irq, level); + return true; + } + + return false; +} + +static inline bool ibex_irq_raise(IbexIRQ *irq) +{ + return ibex_irq_set(irq, 1); +} + +static inline bool ibex_irq_lower(IbexIRQ *irq) +{ + return ibex_irq_set(irq, 0); +} + +static inline void ibex_qdev_init_irq(Object *obj, IbexIRQ *irq, + const char *name) +{ + irq->level = 0; + qdev_init_gpio_out_named(DEVICE(obj), &irq->irq, name, 1); +} + +static inline void ibex_qdev_init_irqs(Object *obj, IbexIRQ *irqs, + const char *name, unsigned count) +{ + for (unsigned ix = 0; ix < count; ix++) { + irqs[ix].level = 0; + qdev_init_gpio_out_named(DEVICE(obj), &irqs[ix].irq, name, 1); + } +} + +static inline void ibex_sysbus_init_irq(Object *obj, IbexIRQ *irq) +{ + irq->level = 0; + sysbus_init_irq(SYS_BUS_DEVICE(obj), &irq->irq); +} + +#endif /* HW_RISCV_IBEX_IRQ_H */ From 9ed7dac1c319d321e3e152dc52582fd3981e40a5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 Feb 2023 17:11:22 +0100 Subject: [PATCH 12/12] [ot] hw/riscv, hw/opentitan: add Opentitan Alert handler device Signed-off-by: Lex Bailey Includes existing MIT licenced code (already published elsewhere) --- hw/opentitan/Kconfig | 2 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_alert.c | 668 ++++++++++++++++++++++++++++++++ hw/opentitan/trace-events | 5 + hw/riscv/Kconfig | 1 + hw/riscv/ibex_common.c | 2 +- hw/riscv/ot_earlgrey.c | 11 +- include/hw/opentitan/ot_alert.h | 23 ++ include/hw/riscv/ibex_irq.h | 6 +- 9 files changed, 712 insertions(+), 7 deletions(-) create mode 100644 hw/opentitan/ot_alert.c create mode 100644 include/hw/opentitan/ot_alert.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 6bd1855a84383..6e9aa833b456d 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -1,2 +1,4 @@ # OpenTitan devices +config OT_ALERT + bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 6bd1855a84383..d24aae88fdf3f 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -1,2 +1,3 @@ # OpenTitan devices +system_ss.add(when: 'CONFIG_OT_ALERT', if_true: files('ot_alert.c')) diff --git a/hw/opentitan/ot_alert.c b/hw/opentitan/ot_alert.c new file mode 100644 index 0000000000000..b864fda0fa4ec --- /dev/null +++ b/hw/opentitan/ot_alert.c @@ -0,0 +1,668 @@ +/* + * QEMU OpenTitan Alert handler device + * + * Copyright (c) 2023-2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * + * SPDX-License-Identifier: MIT + * + */ + +#include "qemu/osdep.h" +#include "qemu/guest-random.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "qemu/typedefs.h" +#include "qapi/error.h" +#include "hw/opentitan/ot_alert.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/registerfields.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ibex_irq.h" +#include "hw/core/sysbus.h" +#include "trace.h" + +#define PARAM_N_ALERTS 65u +#define PARAM_N_LPG 24u +#define PARAM_N_LPG_WIDTH 5u +#define PARAM_ESC_CNT_DW 32u +#define PARAM_ACCU_CNT_DW 16u +#define PARAM_N_CLASSES 4u +#define PARAM_N_ESC_SEV 4u +#define PARAM_N_PHASES 4u +#define PARAM_N_LOC_ALERT 7u +#define PARAM_PING_CNT_DW 16u +#define PARAM_PHASE_DW 2u +#define PARAM_CLASS_DW 2u + +/* clang-format off */ +REG32(INTR_STATE, 0x0u) + SHARED_FIELD(INTR_STATE_CLASSA, 0u, 1u) + SHARED_FIELD(INTR_STATE_CLASSB, 1u, 1u) + SHARED_FIELD(INTR_STATE_CLASSC, 2u, 1u) + SHARED_FIELD(INTR_STATE_CLASSD, 3u, 1u) +REG32(INTR_ENABLE, 0x4u) +REG32(INTR_TEST, 0x8u) +REG32(PING_TIMER_REGWEN, 0xcu) + FIELD(PING_TIMER_REGWEN, EN, 0u, 1u) +REG32(PING_TIMEOUT_CYC_SHADOWED, 0x10u) + FIELD(PING_TIMEOUT_CYC_SHADOWED, VAL, 0u, 16u) +REG32(PING_TIMER_EN_SHADOWED, 0x14u) + FIELD(PING_TIMER_EN_SHADOWED, EN, 0u, 1u) +REG32(ALERT_REGWEN, 0x18u) + SHARED_FIELD(ALERT_REGWEN_EN, 0u, 1u) +REG32(ALERT_EN_SHADOWED, 0x11cu) + SHARED_FIELD(ALERT_EN_SHADOWED_EN, 0u, 1u) +REG32(ALERT_CLASS_SHADOWED, 0x220u) + SHARED_FIELD(ALERT_CLASS_SHADOWED_EN, 0u, 2u) +REG32(ALERT_CAUSE, 0x324u) + SHARED_FIELD(ALERT_CAUSE_EN, 0u, 1u) +REG32(LOC_ALERT_REGWEN, 0x428u) + SHARED_FIELD(LOC_ALERT_REGWEN_EN, 0u, 1u) +REG32(LOC_ALERT_EN_SHADOWED, 0x444u) + SHARED_FIELD(LOC_ALERT_EN_SHADOWED_EN, 0u, 1u) +REG32(LOC_ALERT_CLASS_SHADOWED, 0x460u) + SHARED_FIELD(LOC_ALERT_CLASS_SHADOWED_EN, 0u, 2u) +REG32(LOC_ALERT_CAUSE, 0x47cu) + SHARED_FIELD(LOC_ALERT_CAUSE_EN, 0u, 1u) +REG32(CLASS_REGWEN, 0x498u) + FIELD(CLASS_REGWEN, EN, 0u, 1u) +REG32(CLASS_CTRL_SHADOWED, 0x49cu) + SHARED_FIELD(CLASS_CTRL_SHADOWED_EN, 0u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_LOCK, 1u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E0, 2u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E1, 3u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E2, 4u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E3, 5u, 1u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E0, 6u, 2u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E1, 8u, 2u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E2, 10u, 2u) + SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E3, 12u, 2u) +REG32(CLASS_CLR_REGWEN, 0x4a0u) + SHARED_FIELD(CLASS_CLR_REGWEN_EN, 0u, 1u) +REG32(CLASS_CLR_SHADOWED, 0x4a4u) + SHARED_FIELD(CLASS_CLR_SHADOWED_EN, 0u, 1u) +REG32(CLASS_ACCUM_CNT, 0x4a8u) + SHARED_FIELD(CLASS_ACCUM_CNT, 0u, 16u) +REG32(CLASS_ACCUM_THRESH_SHADOWED, 0x4acu) + SHARED_FIELD(CLASS_ACCUM_THRESH_SHADOWED, 0u, 16u) +REG32(CLASS_TIMEOUT_CYC_SHADOWED, 0x4b0u) +REG32(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0x4b4u) + SHARED_FIELD(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0u, 2u) +REG32(CLASS_PHASE0_CYC_SHADOWED, 0x4b8u) +REG32(CLASS_PHASE1_CYC_SHADOWED, 0x4bcu) +REG32(CLASS_PHASE2_CYC_SHADOWED, 0x4c0u) +REG32(CLASS_PHASE3_CYC_SHADOWED, 0x4c4u) +REG32(CLASS_ESC_CNT, 0x4c8u) +REG32(CLASS_STATE, 0x4ccu) + FIELD(CLASS_STATE, VAL, 0u, 3u) +/* clang-format on */ + +enum { + ALERT_ID_ALERT_PINGFAIL, + ALERT_ID_ESC_PINGFAIL, + ALERT_ID_ALERT_INTEGFAIL, + ALERT_ID_ESC_INTEGFAIL, + ALERT_ID_BUS_INTEGFAIL, + ALERT_ID_SHADOW_REG_UPDATE_ERROR, + ALERT_ID_SHADOW_REG_STORAGE_ERROR, +}; + +enum { + ALERT_CLASSA, + ALERT_CLASSB, + ALERT_CLASSC, + ALERT_CLASSD, +}; + +enum { + STATE_IDLE, + STATE_TIMEOUT, + STATE_FSMERROR, + STATE_TERMINAL, + STATE_PHASE0, + STATE_PHASE1, + STATE_PHASE2, + STATE_PHASE3, +}; + +#define INTR_MASK ((1u << PARAM_N_CLASSES) - 1u) +#define CLASS_CTRL_SHADOWED_MASK \ + (CLASS_CTRL_SHADOWED_EN_MASK | CLASS_CTRL_SHADOWED_LOCK_MASK | \ + CLASS_CTRL_SHADOWED_EN_E0_MASK | CLASS_CTRL_SHADOWED_EN_E1_MASK | \ + CLASS_CTRL_SHADOWED_EN_E2_MASK | CLASS_CTRL_SHADOWED_EN_E3_MASK | \ + CLASS_CTRL_SHADOWED_MAP_E0_MASK | CLASS_CTRL_SHADOWED_MAP_E1_MASK | \ + CLASS_CTRL_SHADOWED_MAP_E2_MASK | CLASS_CTRL_SHADOWED_MAP_E3_MASK) + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) + +#define R_LAST_REG R32_OFF(0x574u) +#define REGS_COUNT (R_LAST_REG + 1u) +#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) + +#define ALERT_SLOT_SIZE R32_OFF(sizeof(struct alerts)) +#define LOC_ALERT_SLOT_SIZE R32_OFF(sizeof(struct loc_alerts)) +#define CLASS_SLOT_SIZE R32_OFF(sizeof(struct classes)) +#define CASE_RANGE(_reg_, _cnt_) (_reg_)...((_reg_) + (_cnt_) - (1u)) +#define CASE_STRIDE(_reg_, _cls_) ((_reg_) + (_cls_) * (CLASS_SLOT_SIZE)) +#define SLOT_OFFSET(_reg_, _base_, _kind_) \ + (((_reg_) - (_base_)) / _kind_##_SLOT_SIZE) +#define ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_ALERT_REGWEN, ALERT) +#define LOC_ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_LOC_ALERT_REGWEN, LOC_ALERT) +#define CLASS_SLOT(_reg_) SLOT_OFFSET(_reg_, R_CLASS_REGWEN, CLASS) + +#define CHECK_REGWEN(_reg_, _cond_) \ + ot_alert_check_regwen(__func__, (_reg_), (_cond_)) + +struct intr { + uint32_t state; + uint32_t enable; + uint32_t test; +}; + +struct ping { + uint32_t timer_regwen; + uint32_t timeout_cyc_shadowed; + uint32_t timer_en_shadowed; +}; + +struct alerts { + uint32_t regwen; + uint32_t en_shadowed; + uint32_t class_shadowed; + uint32_t cause; +}; + +struct loc_alerts { + uint32_t regwen; + uint32_t en_shadowed; + uint32_t class_shadowed; + uint32_t cause; +}; + +struct classes { + uint32_t regwen; + uint32_t ctrl_shadowed; + uint32_t clr_regwen; + uint32_t clr_shadowed; + uint32_t accum_cnt; + uint32_t accum_thresh_shadowed; + uint32_t timeout_cyc_shadowed; + uint32_t crashdump_trigger_shadowed; + uint32_t phase0_cyc_shadowed; + uint32_t phase1_cyc_shadowed; + uint32_t phase2_cyc_shadowed; + uint32_t phase3_cyc_shadowed; + uint32_t esc_cnt; + uint32_t state; +}; + +typedef struct OtAlertRegs { + struct intr intr; + struct ping ping; + struct alerts alerts[PARAM_N_ALERTS]; + struct loc_alerts loc_alerts[PARAM_N_LOC_ALERT]; + struct classes classes[PARAM_N_CLASSES]; +} OtAlertRegs; + +struct OtAlertState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + IbexIRQ irqs[PARAM_N_CLASSES]; + + OtAlertRegs *regs; +}; + +struct OtAlertClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + +static inline bool +ot_alert_check_regwen(const char *func, unsigned reg, bool cond) +{ + if (!cond) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: reg 0x%04x is write-protected\n", + func, (unsigned)(reg * sizeof(uint32_t))); + return false; + } + return true; +} + +static void ot_alert_update_irqs(OtAlertState *s) +{ + uint32_t level = s->regs->intr.state & s->regs->intr.enable; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { + ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); + } +} + +static uint64_t ot_alert_regs_read(void *opaque, hwaddr addr, unsigned size) +{ + OtAlertState *s = opaque; + OtAlertRegs *regs = s->regs; + uint32_t val32; + + hwaddr reg = R32_OFF(addr); + + switch (reg) { + case R_INTR_STATE: + val32 = regs->intr.state; + break; + case R_INTR_ENABLE: + val32 = regs->intr.enable; + break; + case R_PING_TIMER_REGWEN: + val32 = regs->ping.timer_regwen; + break; + case R_PING_TIMEOUT_CYC_SHADOWED: + val32 = regs->ping.timeout_cyc_shadowed; + break; + case R_PING_TIMER_EN_SHADOWED: + val32 = regs->ping.timer_en_shadowed; + break; + case R_INTR_TEST: + qemu_log_mask(LOG_GUEST_ERROR, "W/O register 0x02%" HWADDR_PRIx "\n", + addr); + val32 = 0; + break; + case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): + val32 = regs->alerts[ALERT_SLOT(reg)].regwen; + break; + case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): + val32 = regs->alerts[ALERT_SLOT(reg)].en_shadowed; + break; + case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): + val32 = regs->alerts[ALERT_SLOT(reg)].class_shadowed; + break; + case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): + val32 = regs->alerts[ALERT_SLOT(reg)].cause; + break; + case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): + val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen; + break; + case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): + val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed; + break; + case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): + val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].class_shadowed; + break; + case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): + val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause; + break; + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].regwen; + break; + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].ctrl_shadowed; + break; + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].clr_regwen; + break; + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].clr_shadowed; + break; + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].accum_cnt; + break; + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed; + break; + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed; + break; + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed; + break; + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed; + break; + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed; + break; + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed; + break; + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed; + break; + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): + val32 = regs->classes[CLASS_SLOT(reg)].esc_cnt; + break; + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): + val32 = + regs->classes[reg - CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA)].state; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + val32 = 0; + break; + } + + uint64_t pc = ibex_get_current_pc(); + trace_ot_alert_io_read_out((unsigned)addr, (uint64_t)val32, pc); + + return (uint64_t)val32; +}; + +static void ot_alert_regs_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) +{ + OtAlertState *s = opaque; + OtAlertRegs *regs = s->regs; + uint32_t val32 = (uint32_t)val64; + + hwaddr reg = R32_OFF(addr); + + uint64_t pc = ibex_get_current_pc(); + trace_ot_alert_io_write((unsigned)addr, val64, pc); + + switch (reg) { + case R_INTR_STATE: + val32 &= INTR_MASK; + regs->intr.state &= ~val32; /* RW1C */ + ot_alert_update_irqs(s); + break; + case R_INTR_ENABLE: + val32 &= INTR_MASK; + regs->intr.enable = val32; + ot_alert_update_irqs(s); + break; + case R_INTR_TEST: + val32 &= INTR_MASK; + regs->intr.state |= val32; + ot_alert_update_irqs(s); + break; + case R_PING_TIMER_REGWEN: + val32 &= R_PING_TIMER_REGWEN_EN_MASK; + regs->ping.timer_regwen &= ~val32; /* RW1C */ + break; + case R_PING_TIMEOUT_CYC_SHADOWED: + val32 &= R_PING_TIMEOUT_CYC_SHADOWED_VAL_MASK; + regs->ping.timeout_cyc_shadowed = val32; + break; + case R_PING_TIMER_EN_SHADOWED: + val32 = R_PING_TIMER_EN_SHADOWED_EN_MASK; /* RW1S */ + regs->ping.timer_en_shadowed |= val32; + break; + case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): + val32 &= ALERT_REGWEN_EN_MASK; + regs->alerts[ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ + break; + case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): + if (CHECK_REGWEN(reg, regs->alerts[reg - R_ALERT_EN_SHADOWED].regwen)) { + val32 &= ALERT_EN_SHADOWED_EN_MASK; + regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; + } + break; + case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): + if (CHECK_REGWEN(reg, + regs->alerts[reg - R_ALERT_CLASS_SHADOWED].regwen)) { + val32 &= ALERT_CLASS_SHADOWED_EN_MASK; + regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; + } + break; + case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): + val32 = ALERT_CAUSE_EN_MASK; + regs->alerts[ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ + break; + case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): + val32 &= LOC_ALERT_REGWEN_EN_MASK; + regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ + break; + case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): + if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { + val32 &= LOC_ALERT_EN_SHADOWED_EN_MASK; + regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; + } + break; + case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): + if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { + val32 &= LOC_ALERT_CLASS_SHADOWED_EN_MASK; + regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; + } + break; + case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): + val32 = LOC_ALERT_CAUSE_EN_MASK; + regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ + break; + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): + val32 = R_CLASS_REGWEN_EN_MASK; + regs->classes[CLASS_SLOT(reg)].regwen &= val32; /* RW0C */ + break; + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + val32 &= CLASS_CTRL_SHADOWED_MASK; + regs->classes[CLASS_SLOT(reg)].ctrl_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): + val32 &= CLASS_CLR_REGWEN_EN_MASK; + regs->classes[CLASS_SLOT(reg)].clr_regwen &= val32; /* RW0C */ + break; + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].clr_regwen)) { + val32 &= CLASS_CLR_SHADOWED_EN_MASK; + regs->classes[CLASS_SLOT(reg)].clr_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + val32 &= CLASS_ACCUM_THRESH_SHADOWED_MASK; + regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + val32 &= CLASS_CRASHDUMP_TRIGGER_SHADOWED_MASK; + regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): + if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { + regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed = val32; + } + break; + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): + case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): + qemu_log_mask(LOG_GUEST_ERROR, "R/O register 0x02%" HWADDR_PRIx "\n", + addr); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + break; + } +}; + +static const MemoryRegionOps ot_alert_regs_ops = { + .read = &ot_alert_regs_read, + .write = &ot_alert_regs_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static void ot_alert_reset_enter(Object *obj, ResetType type) +{ + OtAlertClass *c = OT_ALERT_GET_CLASS(obj); + OtAlertState *s = OT_ALERT(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + OtAlertRegs *regs = s->regs; + memset(regs, 0, sizeof(*regs)); + + regs->ping.timer_regwen = 0x1u; + regs->ping.timeout_cyc_shadowed = 0x100u; + for (unsigned ix = 0; ix < PARAM_N_ALERTS; ix++) { + regs->alerts[ix].regwen = 0x1u; + } + for (unsigned ix = 0; ix < PARAM_N_LOC_ALERT; ix++) { + regs->loc_alerts[ix].regwen = 0x1u; + } + for (unsigned ix = 0; ix < PARAM_N_CLASSES; ix++) { + regs->classes[ix].regwen = 0x1u; + regs->classes[ix].ctrl_shadowed = 0x393cu; + regs->classes[ix].clr_regwen = 0x1u; + } + + ot_alert_update_irqs(s); +} + +static void ot_alert_init(Object *obj) +{ + OtAlertState *s = OT_ALERT(obj); + + memory_region_init_io(&s->mmio, obj, &ot_alert_regs_ops, s, TYPE_OT_ALERT, + REGS_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); + + s->regs = g_new0(OtAlertRegs, 1); + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { + ibex_sysbus_init_irq(obj, &s->irqs[ix]); + } +} + +static void ot_alert_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtAlertClass *ac = OT_ALERT_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_alert_reset_enter, NULL, NULL, + &ac->parent_phases); +} + +static const TypeInfo ot_alert_info = { + .name = TYPE_OT_ALERT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtAlertState), + .instance_init = &ot_alert_init, + .class_size = sizeof(OtAlertClass), + .class_init = &ot_alert_class_init, +}; + +static void ot_alert_register_types(void) +{ + type_register_static(&ot_alert_info); +} + +type_init(ot_alert_register_types) diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 695debd3805e7..39dea92d2be7d 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -1 +1,6 @@ # OpenTitan EarlGrey Trace Events + +# ot_alert.c + +ot_alert_io_read_out(unsigned int addr, uint64_t val, uint64_t pc) "addr=0x%02x, val=0x%" PRIx64 ", pc=0x%" PRIx64 +ot_alert_io_write(unsigned int addr, uint64_t val, uint64_t pc) "addr=0x%02x, val=0x%" PRIx64 ", pc=0x%" PRIx64 diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 76588c43ae3c9..8fdb048ea7594 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -17,6 +17,7 @@ config OT_EARLGREY default y select IBEX select IBEX_COMMON + select OT_ALERT select SIFIVE_PLIC config MICROCHIP_PFSOC diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c index 6b53662a5b97c..dc0ae79777e66 100644 --- a/hw/riscv/ibex_common.c +++ b/hw/riscv/ibex_common.c @@ -1,5 +1,5 @@ /* - * QEMU RISC-V Helpers for LowRISC Ibex Demo System & OpenTitan EarlGrey + * QEMU RISC-V Helpers for LowRISC OpenTitan EarlGrey and related systems * * Copyright (c) 2022-2023 Rivos, Inc. * diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 77fb1b207c80e..ccb9ad0e7ea6d 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -24,6 +24,7 @@ #include "hw/core/boards.h" #include "hw/intc/sifive_plic.h" #include "hw/misc/unimp.h" +#include "hw/opentitan/ot_alert.h" #include "hw/core/qdev-properties.h" #include "hw/riscv/ibex_common.h" #include "hw/riscv/ot_earlgrey.h" @@ -261,12 +262,16 @@ static const IbexDeviceDef ot_earlgrey_soc_devices[] = { ), }, [OT_EARLGREY_SOC_DEV_ALERT_HANDLER] = { - .type = TYPE_UNIMPLEMENTED_DEVICE, - .name = "ot-alert_handler", - .cfg = &ibex_unimp_configure, + .type = TYPE_OT_ALERT, .memmap = MEMMAPENTRIES( { 0x40150000u, 0x800u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_EARLGREY_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 127), + OT_EARLGREY_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 128), + OT_EARLGREY_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 129), + OT_EARLGREY_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 130) + ), }, [OT_EARLGREY_SOC_DEV_SPI_HOST0] = { .type = TYPE_UNIMPLEMENTED_DEVICE, diff --git a/include/hw/opentitan/ot_alert.h b/include/hw/opentitan/ot_alert.h new file mode 100644 index 0000000000000..d68f4c9e6e527 --- /dev/null +++ b/include/hw/opentitan/ot_alert.h @@ -0,0 +1,23 @@ +/* + * QEMU OpenTitan Alert handler device + * + * Copyright (c) 2022-2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_OPENTITAN_OT_ALERT +#define HW_OPENTITAN_OT_ALERT + +#include "qom/object.h" + +#define OPENTITAN_DEVICE_ALERT "ot-alert" + +#define TYPE_OT_ALERT "ot-alert" +OBJECT_DECLARE_TYPE(OtAlertState, OtAlertClass, OT_ALERT) + +#endif /* HW_OPENTITAN_OT_ALERT */ diff --git a/include/hw/riscv/ibex_irq.h b/include/hw/riscv/ibex_irq.h index 22ccff6d5e13f..c1e11e20f621a 100644 --- a/include/hw/riscv/ibex_irq.h +++ b/include/hw/riscv/ibex_irq.h @@ -12,9 +12,9 @@ #include "qemu/osdep.h" #include "qom/object.h" -#include "hw/irq.h" -#include "hw/qdev-core.h" -#include "hw/sysbus.h" +#include "hw/core/irq.h" +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" /** Simple IRQ wrapper to limit propagation of no-change calls */