diff --git a/MAINTAINERS b/MAINTAINERS index e0c481e2125d8..c7f98e4b61e5e 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,19 @@ 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 + +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/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/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/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/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..6e9aa833b456d --- /dev/null +++ b/hw/opentitan/Kconfig @@ -0,0 +1,4 @@ +# OpenTitan devices + +config OT_ALERT + bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build new file mode 100644 index 0000000000000..d24aae88fdf3f --- /dev/null +++ b/hw/opentitan/meson.build @@ -0,0 +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 new file mode 100644 index 0000000000000..39dea92d2be7d --- /dev/null +++ b/hw/opentitan/trace-events @@ -0,0 +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/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 0222c93f878b3..8fdb048ea7594 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -7,8 +7,19 @@ config RISCV_NUMA config IBEX bool +config IBEX_COMMON + bool + # RISC-V machines in alphabetical order +config OT_EARLGREY + bool + default y + select IBEX + select IBEX_COMMON + select OT_ALERT + select SIFIVE_PLIC + config MICROCHIP_PFSOC bool default y @@ -38,6 +49,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/riscv/ibex_common.c b/hw/riscv/ibex_common.c new file mode 100644 index 0000000000000..dc0ae79777e66 --- /dev/null +++ b/hw/riscv/ibex_common.c @@ -0,0 +1,316 @@ +/* + * 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 + */ + +#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/core/boards.h" +#include "hw/core/loader.h" +#include "hw/misc/unimp.h" +#include "hw/core/qdev.h" +#include "hw/core/qdev-properties.h" +#include "hw/riscv/ibex_common.h" +#include "hw/core/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..76dc93147ae74 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -3,6 +3,8 @@ 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_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 309125e854bc7..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_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..ccb9ad0e7ea6d --- /dev/null +++ b/hw/riscv/ot_earlgrey.c @@ -0,0 +1,851 @@ +/* + * 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 "qobject/qlist.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/opentitan/ot_alert.h" +#include "hw/core/qdev-properties.h" +#include "hw/riscv/ibex_common.h" +#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 */ +/* ------------------------------------------------------------------------ */ + +#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, + .cfg = &ot_earlgrey_soc_hart_configure, + .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_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, + .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 */ +}; + +#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, +}; + +/* ------------------------------------------------------------------------ */ +/* Type definitions */ +/* ------------------------------------------------------------------------ */ + +struct OtEarlGreySoCState { + SysBusDevice parent_obj; + + DeviceState **devices; + MemoryRegion *memories; +}; + +struct OtEarlGreyBoardState { + DeviceState parent_obj; + + DeviceState **devices; +}; + +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 */ +/* ------------------------------------------------------------------------ */ + +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 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); + + 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) +{ + 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/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')) 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/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 */ diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h new file mode 100644 index 0000000000000..963ab1fa78d3c --- /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/core/qdev.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 */ diff --git a/include/hw/riscv/ibex_irq.h b/include/hw/riscv/ibex_irq.h new file mode 100644 index 0000000000000..c1e11e20f621a --- /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/core/irq.h" +#include "hw/core/qdev.h" +#include "hw/core/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 */ 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 30dcdcfaae516..418b56d69ed11 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -42,7 +42,9 @@ #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_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 e56470a374847..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), @@ -3043,20 +3067,32 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV32, ), - DEFINE_RISCV_CPU(TYPE_RISCV_CPU_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, .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/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 diff --git a/target/riscv/ibex_csr.c b/target/riscv/ibex_csr.c new file mode 100644 index 0000000000000..a6e815e78215c --- /dev/null +++ b/target/riscv/ibex_csr.c @@ -0,0 +1,115 @@ +/* + * 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; +} + +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 }, + }, + { + .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',