From 8ba0172f901c0eed878d31081f242b58c1a48923 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Mar 2025 10:32:48 +0100 Subject: [PATCH 01/37] [ot] hw/opentitan: ot_fifo32: update function signatures with const This departs from QEMU's fifo8 implementation, but enables to use the OT FIFO from a const context. Signed-off-by: Emmanuel Blot --- include/hw/opentitan/ot_fifo32.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/hw/opentitan/ot_fifo32.h b/include/hw/opentitan/ot_fifo32.h index 18eb3705b39ef..f0f961715376a 100644 --- a/include/hw/opentitan/ot_fifo32.h +++ b/include/hw/opentitan/ot_fifo32.h @@ -62,7 +62,7 @@ static inline uint32_t ot_fifo32_pop(OtFifo32 *fifo) return ret; } -static inline uint32_t ot_fifo32_peek(OtFifo32 *fifo) +static inline uint32_t ot_fifo32_peek(const OtFifo32 *fifo) { g_assert(fifo->num > 0); @@ -86,7 +86,7 @@ ot_fifo32_pop_buf(OtFifo32 *fifo, uint32_t max, uint32_t *num) } static inline const uint32_t * -ot_fifo32_peek_buf(OtFifo32 *fifo, uint32_t max, uint32_t *num) +ot_fifo32_peek_buf(const OtFifo32 *fifo, uint32_t max, uint32_t *num) { uint32_t *ret; @@ -110,22 +110,22 @@ static inline void ot_fifo32_reset(OtFifo32 *fifo) fifo->head = 0u; } -static inline bool ot_fifo32_is_empty(OtFifo32 *fifo) +static inline bool ot_fifo32_is_empty(const OtFifo32 *fifo) { return (fifo->num == 0u); } -static inline bool ot_fifo32_is_full(OtFifo32 *fifo) +static inline bool ot_fifo32_is_full(const OtFifo32 *fifo) { return (fifo->num == fifo->capacity); } -static inline uint32_t ot_fifo32_num_free(OtFifo32 *fifo) +static inline uint32_t ot_fifo32_num_free(const OtFifo32 *fifo) { return fifo->capacity - fifo->num; } -static inline uint32_t ot_fifo32_num_used(OtFifo32 *fifo) +static inline uint32_t ot_fifo32_num_used(const OtFifo32 *fifo) { return fifo->num; } From 11a61f1330dff74c8655771c9689d9eb23b20582 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 25 Mar 2025 15:39:47 +0100 Subject: [PATCH 02/37] [ot] hw/opentitan: ot_common: make ot_common_configure_device_opts public Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_common.c | 6 +++--- include/hw/opentitan/ot_common.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_common.c b/hw/opentitan/ot_common.c index 7cf57753f9931..32ff99042c597 100644 --- a/hw/opentitan/ot_common.c +++ b/hw/opentitan/ot_common.c @@ -1,7 +1,8 @@ /* * QEMU OpenTitan utilities * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -221,8 +222,7 @@ AddressSpace *ot_common_get_local_address_space(DeviceState *s) return cpu ? cpu->as : NULL; } -static void -ot_common_configure_device_opts(DeviceState **devices, unsigned count) +void ot_common_configure_device_opts(DeviceState **devices, unsigned count) { // TODO need to use qemu_find_opts_err if no config is ok QemuOptsList *optlist = qemu_find_opts("ot_device"); diff --git a/include/hw/opentitan/ot_common.h b/include/hw/opentitan/ot_common.h index a339db3be4547..7c0b63c7500f8 100644 --- a/include/hw/opentitan/ot_common.h +++ b/include/hw/opentitan/ot_common.h @@ -323,4 +323,6 @@ void ot_common_configure_devices_with_id( DeviceState **devices, BusState *bus, const char *id_value, bool id_prepend, const IbexDeviceDef *defs, size_t count); +void ot_common_configure_device_opts(DeviceState **devices, unsigned count); + #endif /* HW_OPENTITAN_OT_COMMON_H */ From 0aaf0feadae32861006d1e39511aafd98984c829 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Mar 2025 10:40:24 +0100 Subject: [PATCH 03/37] [ot] hw/riscv: ot_earlgrey: remove invalid EDN reference from LC_CTRL Signed-off-by: Emmanuel Blot --- hw/riscv/ot_earlgrey.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index a9035944bda5d..fd8d2ba12eb86 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -639,9 +639,6 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL), OT_EG_SOC_DEVLINK("kmac", KMAC) ), - .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("edn-ep", 4u) - ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("silicon_creator_id", 0x4001u), IBEX_DEV_UINT_PROP("product_id", 0x0002u), From 59e0a6a2202a1048e453b743d63045f66198321c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 21 Mar 2025 14:54:55 +0100 Subject: [PATCH 04/37] [ot] hw/riscv: ot_earlgrey: warn only once on dummy devices access Signed-off-by: Emmanuel Blot --- hw/riscv/ot_earlgrey.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index fd8d2ba12eb86..17868004e9b45 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -524,7 +524,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "i2c0"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 6) @@ -540,7 +541,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "i2c1"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 7) @@ -556,7 +558,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "i2c2"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 8) @@ -572,7 +575,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "pattgen"), IBEX_DEV_UINT_PROP("size", 0x40u), IBEX_DEV_UINT_PROP("irq-count", 2u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 9) @@ -729,7 +733,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "usbdev"), IBEX_DEV_UINT_PROP("size", 0x1000u), IBEX_DEV_UINT_PROP("irq-count", 18u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 21) @@ -790,7 +795,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "sysrst_ctrl"), IBEX_DEV_UINT_PROP("size", 0x100u), IBEX_DEV_UINT_PROP("irq-count", 1u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 27) @@ -806,7 +812,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "adc_ctrl"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 1u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 28) @@ -821,7 +828,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_STRING_PROP("ot_id", "pwm"), IBEX_DEV_UINT_PROP("size", 0x80u), - IBEX_DEV_UINT_PROP("alert-count", 1u) + IBEX_DEV_UINT_PROP("alert-count", 1u), + IBEX_DEV_BOOL_PROP("warn-once", true) ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO_ALERT(0, 29) From 8a55b0cfbc2101f958d6d8cf218ac14d3a2fdf07 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 25 Mar 2025 15:54:36 +0100 Subject: [PATCH 05/37] [ot] hw/riscv: ot_earlgrey: add a Verilator-compliant clock mode This enables runnning applications built for Verilator simulator on QEMU. Signed-off-by: Emmanuel Blot --- docs/opentitan/earlgrey.md | 3 ++ hw/riscv/ot_earlgrey.c | 100 +++++++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index 1d1c203f6e440..efce8c868efbd 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -128,6 +128,9 @@ See [`tools.md`](tools.md) update the vCPU reset vector at startup. When this option is used, with `-kernel` option for example, the application is loaded in memory but the default machine reset vector is used. +* `verilator=true` can be appended to the machine option switch, to select Verilator lowered clocks: + _i.e._ `-M ot-earlgrey,verilator=true` to select Verilator reduced clock rates. + * `-cpu lowrisc-ibex,x-zbr=false` can be used to force disable the Zbr experimental-and-deprecated RISC-V bitmap extension for CRC32 extension. diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 17868004e9b45..6442b97073d21 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -25,6 +25,8 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/typedefs.h" #include "qapi/error.h" #include "qapi/qmp/qlist.h" #include "qom/object.h" @@ -35,7 +37,6 @@ #include "hw/jtag/tap_ctrl.h" #include "hw/jtag/tap_ctrl_rbb.h" #include "hw/misc/pulp_rv_dm.h" -#include "hw/misc/unimp.h" #include "hw/opentitan/ot_aes.h" #include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_aon_timer.h" @@ -183,10 +184,16 @@ enum OtEGBoardDevice { #define OT_EG_CORE_CLK_HZ 24000000u /* EarlGrey/CW310 Peripheral clock is 6 MHz */ #define OT_EG_PERIPHERAL_CLK_HZ ((OT_EG_CORE_CLK_HZ) / 4u) - /* EarlGrey/CW310 AON clock is 250 kHz */ #define OT_EG_AON_CLK_HZ 250000u +/* Verilator Core clock is 500 kHz */ +#define OT_EG_VERILATOR_CORE_CLK_HZ 500000u +/* Verilator Peripheral clock is 125 kHz */ +#define OT_EG_VERILATOR_PERIPHERAL_CLK_HZ ((OT_EG_VERILATOR_CORE_CLK_HZ) / 4u) +/* Verilator AON clock is 125 kHz */ +#define OT_EG_VERILATOR_AON_CLK_HZ OT_EG_VERILATOR_PERIPHERAL_CLK_HZ + static const uint8_t ot_eg_pmp_cfgs[] = { /* clang-format off */ IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0), @@ -1217,6 +1224,7 @@ struct OtEGMachineState { bool no_epmp_cfg; bool ignore_elf_entry; + bool verilator; }; struct OtEGMachineClass { @@ -1397,6 +1405,59 @@ static void ot_eg_soc_reset_exit(Object *obj, ResetType type) resettable_release_reset(OBJECT(s->devices[OT_EG_SOC_DEV_ROM_CTRL]), type); } +static void +ot_earlgrey_update_device_clocks(DeviceState **devices, size_t count) +{ + for (unsigned ix = 0; ix < (unsigned)count; ix++) { + DeviceState *dev = devices[ix]; + if (!dev) { + continue; + } + Error *errp = NULL; + uint64_t pclk = object_property_get_uint(OBJECT(dev), "pclk", &errp); + if (errp) { + error_free(errp); + continue; + } + switch (pclk) { + case 0: + /* PCLK property exists, but is not used, skip it */ + continue; + case OT_EG_CORE_CLK_HZ: + pclk = OT_EG_VERILATOR_CORE_CLK_HZ; + break; + case OT_EG_PERIPHERAL_CLK_HZ: + pclk = OT_EG_VERILATOR_PERIPHERAL_CLK_HZ; + break; + case OT_EG_AON_CLK_HZ: + pclk = OT_EG_VERILATOR_AON_CLK_HZ; + break; + default: + warn_report("%s: OT device %s has invalid pclk value: %" PRIu64, + __func__, object_get_typename(OBJECT(dev)), pclk); + continue; + } + + if (!object_property_set_uint(OBJECT(dev), "pclk", pclk, &errp)) { + error_propagate(&error_fatal, errp); + g_assert_not_reached(); + } + } +} + +static void +ot_earlgrey_configure_verilator_devices(DeviceState **devices, BusState *bus, + const IbexDeviceDef *defs, size_t count) +{ + ibex_link_devices(devices, defs, count); + ibex_define_device_props(devices, defs, count); + ibex_identify_devices(devices, OT_COMMON_DEV_ID, "soc", false, count); + ot_common_configure_device_opts(devices, count); + ot_earlgrey_update_device_clocks(devices, count); + ibex_realize_devices(devices, bus, defs, count); + ibex_connect_devices(devices, defs, count); +} + static void ot_eg_soc_realize(DeviceState *dev, Error **errp) { OtEGSoCState *s = RISCV_OT_EG_SOC(dev); @@ -1404,9 +1465,19 @@ static void ot_eg_soc_realize(DeviceState *dev, Error **errp) /* Link, define properties and realize devices, then connect GPIOs */ BusState *bus = sysbus_get_default(); - ot_common_configure_devices_with_id(s->devices, bus, "soc", false, - ot_eg_soc_devices, - ARRAY_SIZE(ot_eg_soc_devices)); + bool verilator_mode; + + verilator_mode = + object_property_get_bool(qdev_get_machine(), "verilator", NULL); + if (!verilator_mode) { + ot_common_configure_devices_with_id(s->devices, bus, "soc", false, + ot_eg_soc_devices, + ARRAY_SIZE(ot_eg_soc_devices)); + } else { + ot_earlgrey_configure_verilator_devices(s->devices, bus, + ot_eg_soc_devices, + ARRAY_SIZE(ot_eg_soc_devices)); + } MemoryRegion *mrs[] = { get_system_memory(), NULL, NULL, NULL }; ibex_map_devices(s->devices, mrs, ot_eg_soc_devices, @@ -1624,6 +1695,22 @@ ot_eg_machine_set_ignore_elf_entry(Object *obj, bool value, Error **errp) s->ignore_elf_entry = value; } +static bool ot_eg_machine_get_verilator(Object *obj, Error **errp) +{ + OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj); + (void)errp; + + return s->verilator; +} + +static void ot_eg_machine_set_verilator(Object *obj, bool value, Error **errp) +{ + OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj); + (void)errp; + + s->verilator = value; +} + static ResettableState *ot_eg_get_reset_state(Object *obj) { OtEGMachineState *s = RISCV_OT_EG_MACHINE(obj); @@ -1669,6 +1756,9 @@ static void ot_eg_machine_instance_init(Object *obj) &ot_eg_machine_set_ignore_elf_entry); object_property_set_description(obj, "ignore-elf-entry", "Do not set vCPU PC with ELF entry point"); + object_property_add_bool(obj, "verilator", &ot_eg_machine_get_verilator, + &ot_eg_machine_set_verilator); + object_property_set_description(obj, "verilator", "Use Verilator clocks"); } static void ot_eg_machine_init(MachineState *state) From 2a00cb1c508ff9db3e3fd27eb5365018675ec145 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Mar 2025 10:37:28 +0100 Subject: [PATCH 06/37] [ot] hw/opentitan: ot_entropy_src: fix timing hint computation. A timer may be pending but already expired, in which case the remaining time is no longer valid. Do not try any longer to enforce the documented entropy stack power cycle: only emit a warning about discrepancy between the recommended, documented sequence and the actual guest behavior, but do not consider the invalid sequence as an error anymore. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 76be8b20768b9..7cc6eefc47516 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -476,7 +476,13 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, qemu_log_mask(LOG_GUEST_ERROR, "%s: entropy_src gennum mismatch req:%d cur:%u\n", __func__, genid, s->gennum); - return -2; + /* + * Continue anyway as it seems HW does not enforce what is documented. + * Force the generation id so the warning message is only shown once. + */ + if (genid != 0) { + s->gennum = (unsigned)genid; + } } bool fips_compliant; @@ -502,11 +508,12 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, case ENTROPY_SRC_STARTUP_FAIL1: { int wait_ns; if (timer_pending(s->scheduler)) { - wait_ns = 1; - } else { /* computed delay fits into a 31-bit value */ wait_ns = (int)(timer_expire_time_ns(s->scheduler) - qemu_clock_get_ns(OT_VIRTUAL_CLOCK)); + wait_ns = MAX(wait_ns, 1); + } else { + wait_ns = 1; } trace_ot_entropy_src_init_ongoing(STATE_NAME(s->state), s->state, wait_ns); @@ -1330,6 +1337,7 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, /* boot phase */ ot_entropy_src_change_state(s, ENTROPY_SRC_BOOT_HT_RUNNING); } + trace_ot_entropy_src_info("initial schedule"); uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); timer_mod(s->scheduler, (int64_t)(now + From 523fe74372760242e46ef347fc6889fea62f18cb Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 21 Mar 2025 14:53:46 +0100 Subject: [PATCH 07/37] [ot] hw/opentitan: ot_entropy_src: update register definitions Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 77 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 7cc6eefc47516..a3c5f445f765e 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -2,6 +2,7 @@ * QEMU OpenTitan Entropy Source device * * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -79,10 +80,12 @@ REG32(MODULE_ENABLE, 0x20u) FIELD(MODULE_ENABLE, MODULE_ENABLE, 0u, 4u) REG32(CONF, 0x24u) FIELD(CONF, FIPS_ENABLE, 0u, 4u) - FIELD(CONF, ENTROPY_DATA_REG_ENABLE, 4u, 4u) - FIELD(CONF, THRESHOLD_SCOPE, 12u, 4u) - FIELD(CONF, RNG_BIT_ENABLE, 20u, 4u) - FIELD(CONF, RNG_BIT_SEL, 24u, 2u) + FIELD(CONF, FIPS_FLAG, 4u, 4u) + FIELD(CONF, RNG_FIPS, 8u, 4u) + FIELD(CONF, RNG_BIT_ENABLE, 12u, 4u) + FIELD(CONF, RNG_BIT_SEL, 16u, 2u) + FIELD(CONF, THRESHOLD_SCOPE, 18u, 4u) + FIELD(CONF, ENTROPY_DATA_REG_ENABLE, 22u, 4u) REG32(ENTROPY_CONTROL, 0x28u) FIELD(ENTROPY_CONTROL, ES_ROUTE, 0u, 4u) FIELD(ENTROPY_CONTROL, ES_TYPE, 4u, 4u) @@ -149,11 +152,11 @@ REG32(FW_OV_RD_FIFO_OVERFLOW, 0xbcu) REG32(FW_OV_RD_DATA, 0xc0u) REG32(FW_OV_WR_DATA, 0xc4u) REG32(OBSERVE_FIFO_THRESH, 0xc8u) - FIELD(OBSERVE_FIFO_THRESH, VAL, 0u, 7u) + FIELD(OBSERVE_FIFO_THRESH, VAL, 0u, 6u) REG32(OBSERVE_FIFO_DEPTH, 0xccu) - FIELD(OBSERVE_FIFO_DEPTH, VAL, 0u, 7u) + FIELD(OBSERVE_FIFO_DEPTH, VAL, 0u, 6u) REG32(DEBUG_STATUS, 0xd0u) - FIELD(DEBUG_STATUS, ENTROPY_FIFO_DEPTH, 0u, 3u) + FIELD(DEBUG_STATUS, ENTROPY_FIFO_DEPTH, 0u, 2u) FIELD(DEBUG_STATUS, SHA3_FSM, 3u, 3u) FIELD(DEBUG_STATUS, SHA3_BLOCK_PR, 6u, 1u) FIELD(DEBUG_STATUS, SHA3_SQUEEZING, 7u, 1u) @@ -177,10 +180,14 @@ REG32(RECOV_ALERT_STS, 0xd4u) FIELD(RECOV_ALERT_STS, ES_THRESH_CFG_ALERT, 14u, 1u) FIELD(RECOV_ALERT_STS, ES_FW_OV_WR_ALERT, 15u, 1u) FIELD(RECOV_ALERT_STS, ES_FW_OV_DISABLE_ALERT, 16u, 1u) + FIELD(RECOV_ALERT_STS, FIPS_FLAG_FIELD_ALERT, 17u, 1u) + FIELD(RECOV_ALERT_STS, RNG_FIPS_FIELD_ALERT, 18u, 1u) + FIELD(RECOV_ALERT_STS, POSTHT_ENTROPY_DROP_ALERT, 31u, 1u) REG32(ERR_CODE, 0xd8u) FIELD(ERR_CODE, SFIFO_ESRNG_ERR, 0u, 1u) - FIELD(ERR_CODE, SFIFO_OBSERVE_ERR, 1u, 1u) - FIELD(ERR_CODE, SFIFO_ESFINAL_ERR, 2u, 1u) + FIELD(ERR_CODE, SFIFO_DISTR_ERR, 1u, 1u) + FIELD(ERR_CODE, SFIFO_OBSERVE_ERR, 2u, 1u) + FIELD(ERR_CODE, SFIFO_ESFINAL_ERR, 3u, 1u) FIELD(ERR_CODE, ES_ACK_SM_ERR, 20u, 1u) FIELD(ERR_CODE, ES_MAIN_SM_ERR, 21u, 1u) FIELD(ERR_CODE, ES_CNTR_ERR, 22u, 1u) @@ -209,9 +216,9 @@ REG32(MAIN_SM_STATE, 0xe0u) #define ALERT_TEST_MASK \ (R_ALERT_TEST_RECOV_ALERT_MASK | R_ALERT_TEST_FATAL_ALERT_MASK) #define CONF_MASK \ - (R_CONF_FIPS_ENABLE_MASK | R_CONF_ENTROPY_DATA_REG_ENABLE_MASK | \ - R_CONF_THRESHOLD_SCOPE_MASK | R_CONF_RNG_BIT_ENABLE_MASK | \ - R_CONF_RNG_BIT_SEL_MASK) + (R_CONF_FIPS_ENABLE_MASK | R_CONF_FIPS_FLAG_MASK | R_CONF_RNG_FIPS_MASK | \ + R_CONF_RNG_BIT_ENABLE_MASK | R_CONF_RNG_BIT_SEL_MASK | \ + R_CONF_THRESHOLD_SCOPE_MASK | R_CONF_ENTROPY_DATA_REG_ENABLE_MASK) #define ENTROPY_CONTROL_MASK \ (R_ENTROPY_CONTROL_ES_ROUTE_MASK | R_ENTROPY_CONTROL_ES_TYPE_MASK) #define FW_OV_CONTROL_MASK \ @@ -232,14 +239,17 @@ REG32(MAIN_SM_STATE, 0xe0u) R_RECOV_ALERT_STS_ES_BUS_CMP_ALERT_MASK | \ R_RECOV_ALERT_STS_ES_THRESH_CFG_ALERT_MASK | \ R_RECOV_ALERT_STS_ES_FW_OV_WR_ALERT_MASK | \ - R_RECOV_ALERT_STS_ES_FW_OV_DISABLE_ALERT_MASK) + R_RECOV_ALERT_STS_ES_FW_OV_DISABLE_ALERT_MASK | \ + R_RECOV_ALERT_STS_FIPS_FLAG_FIELD_ALERT_MASK | \ + R_RECOV_ALERT_STS_RNG_FIPS_FIELD_ALERT_MASK | \ + R_RECOV_ALERT_STS_POSTHT_ENTROPY_DROP_ALERT_MASK) #define ERR_CODE_MASK \ - (R_ERR_CODE_SFIFO_ESRNG_ERR_MASK | R_ERR_CODE_SFIFO_OBSERVE_ERR_MASK | \ - R_ERR_CODE_SFIFO_ESFINAL_ERR_MASK | R_ERR_CODE_ES_ACK_SM_ERR_MASK | \ - R_ERR_CODE_ES_MAIN_SM_ERR_MASK | R_ERR_CODE_ES_CNTR_ERR_MASK | \ - R_ERR_CODE_SHA3_STATE_ERR_MASK | R_ERR_CODE_SHA3_RST_STORAGE_ERR_MASK | \ - R_ERR_CODE_FIFO_WRITE_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK | \ - R_ERR_CODE_FIFO_STATE_ERR_MASK) + (R_ERR_CODE_SFIFO_ESRNG_ERR_MASK | R_ERR_CODE_SFIFO_DISTR_ERR_MASK | \ + R_ERR_CODE_SFIFO_OBSERVE_ERR_MASK | R_ERR_CODE_SFIFO_ESFINAL_ERR_MASK | \ + R_ERR_CODE_ES_ACK_SM_ERR_MASK | R_ERR_CODE_ES_MAIN_SM_ERR_MASK | \ + R_ERR_CODE_ES_CNTR_ERR_MASK | R_ERR_CODE_SHA3_STATE_ERR_MASK | \ + R_ERR_CODE_SHA3_RST_STORAGE_ERR_MASK | R_ERR_CODE_FIFO_WRITE_ERR_MASK | \ + R_ERR_CODE_FIFO_READ_ERR_MASK | R_ERR_CODE_FIFO_STATE_ERR_MASK) #define ERR_CODE_FATAL_ERROR_MASK \ (R_ERR_CODE_ES_ACK_SM_ERR_MASK | R_ERR_CODE_ES_MAIN_SM_ERR_MASK | \ R_ERR_CODE_ES_CNTR_ERR_MASK | R_ERR_CODE_SHA3_STATE_ERR_MASK | \ @@ -325,8 +335,17 @@ static const char *REG_NAMES[REGS_COUNT] = { #define ES_FINAL_FIFO_WORD_COUNT (ES_WORD_COUNT * ES_FINAL_FIFO_DEPTH) #define ES_HEXBUF_SIZE ((8U * 2u + 1u) * ES_WORD_COUNT + 4u) -/* see hw/ip/edn/doc/#multiple-edns-in-boot-time-request-mode */ -#define OT_ENTROPY_SRC_BOOT_DELAY_NS 2000000u /* 2 ms */ +/* + * see hw/ip/edn/doc/#multiple-edns-in-boot-time-request-mode + * reduce initial delay in QEMU since it takes time to manage the entropy + */ +#define OT_ENTROPY_SRC_BOOT_DELAY_NS 500000LL /* 500 us */ +/* + * default delay to pace the entropy src client (CSRNG) when no entropy is + * available. A better implementation would compute the remaining time before + * the next available entropy packet. + */ +#define OT_ENTROPY_SRC_WAIT_DELAY_NS 2000LL /* 2 us */ enum { ALERT_RECOVERABLE, @@ -506,19 +525,19 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, case ENTROPY_SRC_STARTUP_PHASE1: case ENTROPY_SRC_STARTUP_PASS1: case ENTROPY_SRC_STARTUP_FAIL1: { - int wait_ns; + int64_t wait_ns; if (timer_pending(s->scheduler)) { /* computed delay fits into a 31-bit value */ - wait_ns = (int)(timer_expire_time_ns(s->scheduler) - - qemu_clock_get_ns(OT_VIRTUAL_CLOCK)); - wait_ns = MAX(wait_ns, 1); + wait_ns = ((int64_t)timer_expire_time_ns(s->scheduler)) - + qemu_clock_get_ns(OT_VIRTUAL_CLOCK); + wait_ns = MAX(wait_ns, OT_ENTROPY_SRC_WAIT_DELAY_NS); } else { - wait_ns = 1; + wait_ns = OT_ENTROPY_SRC_WAIT_DELAY_NS; } trace_ot_entropy_src_init_ongoing(STATE_NAME(s->state), s->state, - wait_ns); + (int)wait_ns); /* not ready */ - return wait_ns; + return (int)wait_ns; } case ENTROPY_SRC_IDLE: qemu_log_mask(LOG_GUEST_ERROR, "%s: module is not enabled\n", __func__); @@ -542,7 +561,7 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, if (ot_fifo32_num_used(&s->final_fifo) < ES_WORD_COUNT) { trace_ot_entropy_src_no_entropy(ot_fifo32_num_used(&s->final_fifo)); - return 1; + return OT_ENTROPY_SRC_WAIT_DELAY_NS; } uint32_t *randu32 = (uint32_t *)random; From 1815a6928cabc723035fd74e90ca0b9f996a8049 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 6 Mar 2025 15:42:50 +0100 Subject: [PATCH 08/37] [ot] hw/opentitan: ot_csrng: rework CSRNG to match new implementation rework CSRNG public API: - use enumerated CSRNG results, which matches the actual HW - only accepts 32-bit word as input command, not a buffer. This better replicates the actual EDN-CSRNG HW interface - accepts up to 16 command words, such as the HW, even if only up to 13 are meaningful features update: - detect invalid FLAG0 multibit bool in ACMD commands - fix reseed_counter management based on HW wave observation - raise IRQ + ALERT for the same "fatal" cause - make ALERT edge-triggered rather than level-triggered - duplicate internal state value into RESEED_COUNTER registers - implement RESEED_INTERVAL check - implement FIPS_FORCE, INT_STATE_READ_ENABLE, NT_STATE_READ_ENABLE_REGEN register features - update REGWEN protected registers - do not try any longer to enforce the documented entropy stack power cycle: only emit a warning about discrepancy between the recommended, documented sequence and the actual guest behavior, but do not consider the invalid sequence as an error anymore - flush the application CMD FIFO queue on client disconnect Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 574 +++++++++++++++++++++----------- hw/opentitan/trace-events | 10 +- include/hw/opentitan/ot_csrng.h | 38 ++- 3 files changed, 412 insertions(+), 210 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index 948ea20ee407f..926a3717c7b6e 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -2,6 +2,7 @@ * QEMU OpenTitan Cryptographically Secure Random Number Generator * * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -28,7 +29,6 @@ */ #include "qemu/osdep.h" -#include "qemu/guest-random.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/queue.h" @@ -51,6 +51,7 @@ #define PARAM_NUM_IRQS 4u #define PARAM_NUM_ALERTS 2u +#define N_APP_COUNT (OT_CSRNG_HW_APP_MAX + 1u) /* clang-format off */ REG32(INTR_STATE, 0x0u) @@ -84,16 +85,16 @@ REG32(GENBITS_VLD, 0x30u) FIELD(GENBITS_VLD, GENBITS_FIPS, 1u, 1u) REG32(GENBITS, 0x34u) REG32(INT_STATE_READ_ENABLE, 0x38u) - FIELD(INT_STATE_READ_ENABLE, INT_STATE_READ_ENABLE, 0u, 3u) + FIELD(INT_STATE_READ_ENABLE, VAL, 0u, N_APP_COUNT) REG32(INT_STATE_READ_ENABLE_REGWEN, 0x3cu) - FIELD(INT_STATE_READ_ENABLE, INT_STATE_READ_ENABLE_REGWEN, 0u, 1u) + FIELD(INT_STATE_READ_ENABLE_REGWEN, EN, 0u, 1u) REG32(INT_STATE_NUM, 0x40u) FIELD(INT_STATE_NUM, VAL, 0u, 4u) REG32(INT_STATE_VAL, 0x44u) REG32(FIPS_FORCE, 0x48u) - FIELD(FIPS_FORCE, FIPS_FORCE, 0u, 2u) + FIELD(FIPS_FORCE, VAL, 0u, N_APP_COUNT) REG32(HW_EXC_STS, 0x4cu) - FIELD(HW_EXC_STS, HW_EXC_STS, 0u, 16u) + FIELD(HW_EXC_STS, VAL, 0u, 16u) REG32(RECOV_ALERT_STS, 0x50u) FIELD(RECOV_ALERT_STS, ENABLE_FIELD_ALERT, 0u, 1u) FIELD(RECOV_ALERT_STS, SW_APP_ENABLE_FIELD_ALERT, 1u, 1u) @@ -152,7 +153,7 @@ REG32(MAIN_SM_STATE, 0x5cu) (R_ALERT_TEST_RECOV_ALERT_MASK | R_ALERT_TEST_FATAL_ALERT_MASK) #define CTRL_MASK \ (R_CTRL_ENABLE_MASK | R_CTRL_SW_APP_ENABLE_MASK | \ - R_CTRL_READ_INT_STATE_MASK) + R_CTRL_READ_INT_STATE_MASK | R_CTRL_FIPS_FORCE_ENABLE_MASK) #define GENBITS_VLD_MASK \ (R_GENBITS_VLD_GENBITS_VLD_MASK | R_GENBITS_VLD_GENBITS_FIPS_MASK) #define RECOV_ALERT_STS_MASK \ @@ -173,6 +174,16 @@ REG32(MAIN_SM_STATE, 0x5cu) #define OT_CSRNG_AES_BLOCK_WORD (OT_CSRNG_AES_BLOCK_SIZE / sizeof(uint32_t)) #define OT_CSRNG_AES_BLOCK_DWORD (OT_CSRNG_AES_BLOCK_SIZE / sizeof(uint64_t)) +/* + * Should be limited to OT_CSRNG_CMD_WORD_MAX, but the HW does not enforce it + * so be sure to accept CLEN up to 15, not 12. CLEN being coded on 4 bits, and + * command using 1 slot, the actual CLEN is limited to 15. HW ignores the + * trailing additional data words ([12..15]) + */ +#define CMD_FIFO_CAPACITY (R_OT_CSNRG_CMD_CLEN_MASK + 1u) + +#define SW_INSTANCE_ID OT_CSRNG_HW_APP_MAX + #define ALERT_STATUS_BIT(_x_) R_RECOV_ALERT_STS_##_x_##_FIELD_ALERT_MASK #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) @@ -231,8 +242,6 @@ static_assert(OT_CSRNG_AES_BLOCK_SIZE + OT_CSRNG_AES_KEY_SIZE == #define xtrace_ot_csrng_show_buffer(_id_, _msg_, _buf_, _len_) \ ot_csrng_show_buffer(__func__, __LINE__, _id_, _msg_, _buf_, _len_) -#define SW_INSTANCE_ID OT_CSRNG_HW_APP_MAX - #define ENTROPY_SRC_INITIAL_REQUEST_COUNT 5u enum { @@ -243,12 +252,27 @@ enum { static_assert(ALERT_COUNT == PARAM_NUM_ALERTS, "Invalid alert count"); +/* + * Command execution result. + * + * Zero value signals the success and immediate completion of a command + * Positive values signal a deferred completion of a command + * Negative values signal an error, encoding a OtCSRNGCmdStatus value + */ typedef enum { - CSRNG_CMD_STALLED = -2, /* entropy stack is stalled */ - CSRNG_CMD_ERROR = -1, /* command error, not recoverable */ - CSRNG_CMD_OK = 0, /* command completed ok */ - CSRNG_CMD_RETRY = 1, /* command cannot be executed for now */ - CSRNG_CMD_DEFERRED = 2, /* command completion deferred */ + /* entropy stack is stalled */ + CSRNG_CMD_STALLED = -CSRNG_STATUS_COUNT, + /* command errors, not recoverable */ + CSRNG_CMD_RESEED_CNT_EXCEEDED = -CSRNG_STATUS_RESEED_CNT_EXCEEDED, + CSRNG_CMD_INVALID_CMD_SEQ = -CSRNG_STATUS_INVALID_CMD_SEQ, + CSRNG_CMD_INVALID_GEN_CMD = -CSRNG_STATUS_INVALID_GEN_CMD, + CSRNG_CMD_INVALID_ACMD = -CSRNG_STATUS_INVALID_ACMD, + /* command completed ok */ + CSRNG_CMD_OK = 0, + /* command cannot be executed for now */ + CSRNG_CMD_RETRY = 1, + /* command completion deferred */ + CSRNG_CMD_DEFERRED = 2, } OtCSRNDCmdResult; typedef enum { @@ -275,12 +299,12 @@ typedef struct { uint8_t key[OT_CSRNG_AES_KEY_SIZE]; uint32_t material[OT_CSRNG_SEED_WORD_COUNT]; unsigned material_len; /* in word count */ - /* See https://github.com/lowRISC/opentitan/issues/16499 */ - unsigned reseed_counter; + unsigned reseed_counter; /* generate command count since last reseed */ unsigned rem_packet_count; /* remaining packets to generate */ bool instantiated; bool seeded; /* ready to generate randomness */ bool fips; + bool force_fips; } OtCSRNGDrng; typedef struct OtCSRNGInstance { @@ -319,7 +343,7 @@ struct OtCSRNGState { bool enabled; bool sw_app_granted; bool read_int_granted; - bool es_available; + bool es_available; /* guest warning if entropy power cycling is invalid */ uint32_t scheduled_cmd; unsigned entropy_delay; unsigned es_retry_count; @@ -334,16 +358,25 @@ struct OtCSRNGState { OtOTPState *otp_ctrl; }; +/* clang-format off */ static const uint8_t OtCSRNGFsmStateCode[] = { - [CSRNG_IDLE] = 0b01001110, [CSRNG_PARSE_CMD] = 0b10111011, - [CSRNG_INSTANT_PREP] = 0b11000001, [CSRNG_INSTANT_REQ] = 0b01010100, - [CSRNG_RESEED_PREP] = 0b11011101, [CSRNG_RESEED_REQ] = 0b01011011, - [CSRNG_GENERATE_PREP] = 0b11101111, [CSRNG_GENERATE_REQ] = 0b00100100, - [CSRNG_UPDATE_PREP] = 0b00110001, [CSRNG_UPDATE_REQ] = 0b10010000, - [CSRNG_UNINSTANT_PREP] = 0b11110110, [CSRNG_UNINSTANT_REQ] = 0b01100011, - [CSRNG_CLR_A_DATA] = 0b00000010, [CSRNG_CMD_COMP_WAIT] = 0b10111100, - [CSRNG_ERROR] = 0b01111000 + [CSRNG_IDLE] = 0b01001110, // 0x4e: idle + [CSRNG_PARSE_CMD] = 0b10111011, // 0xbb: parse the cmd + [CSRNG_INSTANT_PREP] = 0b11000001, // 0xc1: instantiate prep + [CSRNG_INSTANT_REQ] = 0b01010100, // 0x54: instantiate request + [CSRNG_RESEED_PREP] = 0b11011101, // 0xdd: reseed prep + [CSRNG_RESEED_REQ] = 0b01011011, // 0x5b: reseed request + [CSRNG_GENERATE_PREP] = 0b11101111, // 0xef: generate prep + [CSRNG_GENERATE_REQ] = 0b00100100, // 0x24: generate request + [CSRNG_UPDATE_PREP] = 0b00110001, // 0x31: update prep + [CSRNG_UPDATE_REQ] = 0b00100100, // 0x24: update request + [CSRNG_UNINSTANT_PREP] = 0b11110110, // 0xf6: uninstantiate prep + [CSRNG_UNINSTANT_REQ] = 0b01100011, // 0x63: uninstantiate request + [CSRNG_CLR_A_DATA] = 0b00000010, // 0x02: clear out the add. data fifo + [CSRNG_CMD_COMP_WAIT] = 0b10111100, // 0xbc: wait for command to complete + [CSRNG_ERROR] = 0b01111000 // 0x78: error state }; +/* clang-format on */ #define STATE_NAME_ENTRY(_st_) [_st_] = stringify(_st_) static const char *STATE_NAMES[] = { @@ -378,12 +411,18 @@ static const char *STATE_NAMES[] = { static bool ot_csrng_check_multibitboot(OtCSRNGState *s, uint8_t mbbool, uint32_t alert_bit); static void ot_csrng_command_schedule(OtCSRNGState *s, OtCSRNGInstance *inst); -static bool ot_csrng_instance_is_command_ready(OtCSRNGInstance *inst); -static void ot_csrng_complete_command(OtCSRNGInstance *inst, int res); +static bool +ot_csrng_instance_is_command_ready(const OtCSRNGInstance *inst, bool fatal); +static void ot_csrng_complete_command(OtCSRNGInstance *inst, + OtCSRNGCmdStatus sts); static void ot_csrng_change_state_line(OtCSRNGState *s, OtCSRNGFsmState state, int line); -static unsigned ot_csrng_get_slot(OtCSRNGInstance *inst); -static bool ot_csrng_drng_is_instantiated(OtCSRNGInstance *inst); +static unsigned ot_csrng_get_slot(const OtCSRNGInstance *inst); +static bool ot_csrng_drng_is_instantiated(const OtCSRNGInstance *inst); +static void ot_csrng_release_hw_app(OtCSRNGInstance *inst); +static void ot_csrng_update_irqs(OtCSRNGState *s); +static void ot_csrng_update_alerts(OtCSRNGState *s); + static OtCSRNDCmdResult ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0); @@ -391,58 +430,132 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0); /* Public API */ /* -------------------------------------------------------------------------- */ -qemu_irq ot_csnrg_connect_hw_app(OtCSRNGState *s, unsigned app_id, - qemu_irq req_sts, ot_csrng_genbit_filler_fn fn, - void *opaque) +qemu_irq +ot_csnrg_connect_hw_app(OtCSRNGState *s, unsigned app_id, qemu_irq req_sts, + ot_csrng_genbit_filler_fn filler_fn, void *opaque) { g_assert(app_id < OT_CSRNG_HW_APP_MAX); - g_assert(req_sts); - g_assert(fn); OtCSRNGInstance *inst = &s->instances[app_id]; + + if (!filler_fn) { + if (inst->hw.filler) { + xtrace_ot_csrng_info("HW app was not connected", app_id); + return NULL; + } + + trace_ot_csrng_disconnection(app_id, true); + ot_csrng_release_hw_app(inst); + + return NULL; + } + + g_assert(req_sts); + /* if connection is invoked many times, there is no reason for changes */ if (inst->hw.filler) { - g_assert(inst->hw.filler == fn); + g_assert(inst->hw.filler == filler_fn); } if (inst->hw.req_sts) { g_assert(inst->hw.req_sts == req_sts); } - inst->hw.filler = fn; + + trace_ot_csrng_connection(app_id, inst->hw.filler == NULL); + + inst->hw.filler = filler_fn; inst->hw.opaque = opaque; inst->hw.req_sts = req_sts; - trace_ot_csrng_connection(app_id); - return qdev_get_gpio_in_named(DEVICE(s), TYPE_OT_CSRNG "-genbits_ready", (int)app_id); } -int ot_csrng_push_command(OtCSRNGState *s, unsigned app_id, - const uint32_t *command) +OtCSRNGCmdStatus ot_csrng_push_command(OtCSRNGState *s, unsigned app_id, + uint32_t word) { g_assert(app_id < OT_CSRNG_HW_APP_MAX); if (s->state == CSRNG_ERROR) { - return -1; + return CSRNG_STATUS_INVALID_CMD_SEQ; } OtCSRNGInstance *inst = &s->instances[app_id]; g_assert(inst->hw.filler); g_assert(inst->hw.req_sts); - ot_fifo32_reset(&inst->cmd_fifo); - uint32_t acmd = FIELD_EX32(command[0], OT_CSNRG_CMD, ACMD); - uint32_t length = FIELD_EX32(command[0], OT_CSNRG_CMD, CLEN) + 1u; + /* FIFO is emptied in #ot_csrng_complete_command */ + if (ot_fifo32_is_full(&inst->cmd_fifo)) { + xtrace_ot_csrng_error("Command FIFO is full"); + return CSRNG_STATUS_INVALID_CMD_SEQ; + } + + bool check_cmd = ot_fifo32_is_empty(&inst->cmd_fifo); + + ot_fifo32_push(&inst->cmd_fifo, word); + uint32_t cmd = ot_fifo32_peek(&inst->cmd_fifo); + + uint32_t acmd = FIELD_EX32(cmd, OT_CSNRG_CMD, ACMD); + uint32_t length = FIELD_EX32(cmd, OT_CSNRG_CMD, CLEN) + 1u; + + if (check_cmd) { + /* NOLINTNEXTLINE */ + switch (acmd) { + case OT_CSRNG_CMD_INSTANTIATE: + case OT_CSRNG_CMD_RESEED: + ot_csrng_check_multibitboot(s, + (uint8_t)FIELD_EX32(cmd, OT_CSNRG_CMD, + FLAG0), + ALERT_STATUS_BIT(ACMD_FLAG0)); + break; + case OT_CSRNG_CMD_GENERATE ... OT_CSRNG_CMD_UNINSTANTIATE: + break; + default: + xtrace_ot_csrng_error("Invalid command opcode"); + ot_fifo32_reset(&inst->cmd_fifo); + return CSRNG_STATUS_INVALID_ACMD; + } + + if (length > CMD_FIFO_CAPACITY) { + xtrace_ot_csrng_error("Invalid command length (overflow)"); + /* + * as CLEN width cannot encode more than CMD_FIFO_CAPACITY, this + * error should never occur. + */ + g_assert_not_reached(); + } + } + + if (ot_fifo32_num_used(&inst->cmd_fifo) > length) { + xtrace_ot_csrng_error("Invalid command length (too many)"); + /* + * as the FIFO should have been handled and emptied in a previous call + * to this function, this error should never occur. + */ + g_assert_not_reached(); + } + if (ot_fifo32_num_used(&inst->cmd_fifo) < length) { + /* more payload is expected */ + return CSRNG_STATUS_SUCCESS; + } + if (acmd == OT_CSRNG_CMD_GENERATE) { - uint32_t glen = FIELD_EX32(command[0], OT_CSNRG_CMD, GLEN); + uint32_t glen = FIELD_EX32(cmd, OT_CSNRG_CMD, GLEN); trace_ot_csrng_push_command(app_id, CMD_NAME(acmd), acmd, 'g', glen); + + const OtCSRNGDrng *drng = &inst->drng; + if (drng->reseed_counter >= s->regs[R_RESEED_INTERVAL]) { + s->regs[R_INTR_STATE] |= INTR_CS_HW_INST_EXC_MASK; + s->regs[R_RECOV_ALERT_STS] |= + R_RECOV_ALERT_STS_CMD_STAGE_INVALID_RESEED_CNT_ALERT_MASK; + ot_fifo32_reset(&inst->cmd_fifo); + ot_csrng_update_irqs(s); + ot_csrng_update_alerts(s); + return CSRNG_STATUS_RESEED_CNT_EXCEEDED; + } } else { trace_ot_csrng_push_command(app_id, CMD_NAME(acmd), acmd, 'c', length); } - if (length > OT_CSRNG_CMD_WORD_MAX) { - xtrace_ot_csrng_error("Invalid command length"); - return -1; - } + if (acmd == OT_CSRNG_CMD_UNINSTANTIATE) { if (!ot_csrng_drng_is_instantiated(inst)) { /* @@ -468,13 +581,9 @@ int ot_csrng_push_command(OtCSRNGState *s, unsigned app_id, } } - while (length--) { - ot_fifo32_push(&inst->cmd_fifo, *command++); - } - ot_csrng_command_schedule(s, inst); - return 0; + return CSRNG_STATUS_SUCCESS; } /* -------------------------------------------------------------------------- */ @@ -530,18 +639,12 @@ static void ot_csrng_drng_clear_material(OtCSRNGInstance *inst) drng->material_len = 0; } -static unsigned ot_csrng_drng_remaining_count(OtCSRNGInstance *inst) +static unsigned ot_csrng_drng_remaining_count(const OtCSRNGInstance *inst) { return inst->drng.rem_packet_count; } -static void ot_csrng_drng_set_count(OtCSRNGInstance *inst, - unsigned packet_count) -{ - inst->drng.rem_packet_count = packet_count; -} - -static bool ot_csrng_drng_is_instantiated(OtCSRNGInstance *inst) +static bool ot_csrng_drng_is_instantiated(const OtCSRNGInstance *inst) { return inst->drng.instantiated; } @@ -562,7 +665,7 @@ static OtCSRNDCmdResult ot_csrng_drng_instantiate( { OtCSRNGDrng *drng = &inst->drng; if (drng->instantiated) { - return CSRNG_CMD_ERROR; + return CSRNG_CMD_INVALID_CMD_SEQ; } memset(drng->v_counter, 0, sizeof(drng->v_counter)); @@ -666,17 +769,24 @@ static void ot_csrng_drng_update(OtCSRNGInstance *inst) static OtCSRNDCmdResult ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) { + OtCSRNGState *s = inst->parent; OtCSRNGDrng *drng = &inst->drng; g_assert(drng->instantiated); + unsigned slot = ot_csrng_get_slot(inst); drng->seeded = false; if (!flag0) { - if (!inst->parent->es_available) { + if (!s->es_available) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: Cannot request entropy w/o power cycling ES\n", + "%s: Requesting entropy w/o power cycling ES\n", __func__); - return CSRNG_CMD_ERROR; + /* + * Continue anyway as it seems HW does not enforce what is + * documented. Force the flag so the warning message is only + * shown once (it does not serve any other purpose). + */ + s->es_available = true; } uint64_t buffer[OT_RANDOM_SRC_DWORD_COUNT]; @@ -684,25 +794,26 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) unsigned len = drng->material_len * sizeof(uint32_t); memcpy(buffer, drng->material, MIN(len, sizeof(buffer))); - OtCSRNGState *s = inst->parent; + uint64_t entropy[OT_RANDOM_SRC_DWORD_COUNT]; int res; bool fips; - trace_ot_csrng_request_entropy(ot_csrng_get_slot(inst), - s->entropy_gennum); + trace_ot_csrng_request_entropy(slot, s->entropy_gennum); OtRandomSrcIfClass *cls = OT_RANDOM_SRC_IF_GET_CLASS(rand_dev); OtRandomSrcIf *randif = OT_RANDOM_SRC_IF(rand_dev); res = cls->get_random_values(randif, s->entropy_gennum, entropy, &fips); if (res) { s->entropy_delay = (res > 1) ? (unsigned)res : 0; - trace_ot_csrng_entropy_rejected(ot_csrng_get_slot(inst), + trace_ot_csrng_entropy_rejected(slot, res < 0 ? (res == -2 ? "stalled" : "error") : "not ready", res); - return res < 0 ? (res == -2 ? CSRNG_CMD_STALLED : CSRNG_CMD_ERROR) : + return res < 0 ? (res == -2 ? CSRNG_CMD_STALLED : + CSRNG_CMD_RESEED_CNT_EXCEEDED) : CSRNG_CMD_RETRY; } + /* always perform XOR which is a no-op if material_len is zero */ for (unsigned ix = 0; ix < OT_RANDOM_SRC_DWORD_COUNT; ix++) { buffer[ix] ^= entropy[ix]; @@ -716,9 +827,9 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) drng->fips = false; } - drng->reseed_counter = 1u; - + drng->reseed_counter = 0u; drng->seeded = true; + drng->force_fips = (bool)((s->regs[R_FIPS_FORCE] >> slot) & 0x1u); return CSRNG_CMD_OK; } @@ -740,10 +851,11 @@ static void ot_csrng_drng_generate(OtCSRNGInstance *inst, uint32_t *out, xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "out", out, OT_CSRNG_AES_BLOCK_SIZE); - *fips = drng->fips; + *fips = drng->fips || drng->force_fips; if (!ot_csrng_drng_remaining_count(inst)) { ot_csrng_drng_update(inst); + /* last packet generation for the current command */ drng->reseed_counter += 1u; } } @@ -752,7 +864,7 @@ static void ot_csrng_drng_generate(OtCSRNGInstance *inst, uint32_t *out, /* Private implementation */ /* -------------------------------------------------------------------------- */ -static unsigned ot_csrng_get_slot(OtCSRNGInstance *inst) +static unsigned ot_csrng_get_slot(const OtCSRNGInstance *inst) { unsigned slot = (unsigned)(uintptr_t)(inst - &inst->parent->instances[0]); g_assert(slot <= SW_INSTANCE_ID); @@ -771,6 +883,7 @@ static void ot_csrng_update_irqs(OtCSRNGState *s) static void ot_csrng_update_alerts(OtCSRNGState *s) { uint32_t level = s->regs[R_ALERT_TEST]; + s->regs[R_ALERT_TEST] = 0u; if (__builtin_popcount(s->regs[R_RECOV_ALERT_STS])) { level |= 1u << ALERT_RECOVERABLE; @@ -782,6 +895,7 @@ static void ot_csrng_update_alerts(OtCSRNGState *s) for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + ibex_irq_set(&s->alerts[ix], 0); } } @@ -794,6 +908,8 @@ static void ot_csrng_change_state_line(OtCSRNGState *s, OtCSRNGFsmState state, s->state = state; if (s->state == CSRNG_ERROR) { + s->regs[R_INTR_STATE] |= INTR_CS_FATAL_ERR_MASK; + ot_csrng_update_irqs(s); ot_csrng_update_alerts(s); } } @@ -844,36 +960,29 @@ static bool ot_csrng_is_in_queue(OtCSRNGInstance *inst) cmd_request) == inst; } -static bool ot_csrng_expedite_uninstantiation(OtCSRNGInstance *inst) +static void ot_csrng_release_hw_app(OtCSRNGInstance *inst) { - /* check if the instance has been flagged for uninstantiation */ - if (ot_fifo32_is_empty(&inst->cmd_fifo)) { - return false; - } - - uint32_t cmd = ot_fifo32_peek(&inst->cmd_fifo); - uint32_t acmd = FIELD_EX32(cmd, OT_CSNRG_CMD, ACMD); - if (acmd != OT_CSRNG_CMD_UNINSTANTIATE) { - return false; - } - - unsigned slot = ot_csrng_get_slot(inst); - trace_ot_csrng_expedite_uninstantiation(slot); - /* remove the command from the queue since it is handled right here */ OtCSRNGState *s = inst->parent; - QSIMPLEQ_REMOVE(&s->cmd_requests, inst, OtCSRNGInstance, cmd_request); + if (ot_csrng_is_in_queue(inst)) { + QSIMPLEQ_REMOVE(&s->cmd_requests, inst, OtCSRNGInstance, cmd_request); + } if (QSIMPLEQ_EMPTY(&s->cmd_requests)) { qemu_bh_cancel(s->cmd_scheduler); timer_del(s->entropy_scheduler); } - trace_ot_csrng_instantiate(slot, false); + unsigned slot = ot_csrng_get_slot(inst); + trace_ot_csrng_uninstantiate(slot, false); ot_csrng_drng_uninstantiate(inst); - ot_csrng_complete_command(inst, 0); - return true; + ot_fifo32_reset(&inst->cmd_fifo); + inst->defer_completion = false; + + inst->hw.filler = NULL; + inst->hw.opaque = NULL; + inst->hw.req_sts = NULL; } static void ot_csrng_handle_enable(OtCSRNGState *s) @@ -919,19 +1028,13 @@ static void ot_csrng_handle_enable(OtCSRNGState *s) for (unsigned ix = 0u; ix < OT_CSRNG_HW_APP_MAX; ix++) { OtCSRNGInstance *inst = &s->instances[ix]; if (ot_csrng_drng_is_instantiated(inst)) { - /* - * the instance may have received an unistantiation command - * that has not been dequeued yet. If this is the case, proceed - * with uninstantiation rather than rejecting the disablement of - * the CSRNG - */ - if (!ot_csrng_expedite_uninstantiation(inst)) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: Cannot disable CSRNG as EDN #%u still active\n", - __func__, ot_csrng_get_slot(inst)); - return; - } + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Forcing CSRNG disablement while EDN #%u " + "still active\n", + __func__, ot_csrng_get_slot(inst)); + + trace_ot_csrng_disconnection(ix, false); + ot_csrng_release_hw_app(inst); } } s->enabled = false; @@ -955,22 +1058,24 @@ static void ot_csrng_handle_enable(OtCSRNGState *s) * is an unstantiate command, in which case it is immediately * completed */ - bool success; + OtCSRNGCmdStatus sts; if (!ot_fifo32_is_empty(&inst->cmd_fifo)) { uint32_t cmd = ot_fifo32_peek(&inst->cmd_fifo); uint32_t acmd = FIELD_EX32(cmd, OT_CSNRG_CMD, ACMD); - success = acmd == OT_CSRNG_CMD_UNINSTANTIATE; + sts = acmd == OT_CSRNG_CMD_UNINSTANTIATE ? + CSRNG_STATUS_SUCCESS : + CSRNG_STATUS_INVALID_CMD_SEQ; } else { - success = false; + sts = CSRNG_STATUS_INVALID_CMD_SEQ; } - qemu_set_irq(inst->hw.req_sts, (int)!success); + qemu_set_irq(inst->hw.req_sts, (int)sts); } QSIMPLEQ_REMOVE_HEAD(&s->cmd_requests, cmd_request); } /* reset all instances */ - for (unsigned ix = 0u; ix < OT_CSRNG_HW_APP_MAX + 1u; ix++) { + for (unsigned ix = 0u; ix < N_APP_COUNT; ix++) { inst = &s->instances[ix]; ot_csrng_drng_uninstantiate(inst); ot_fifo32_reset(&inst->cmd_fifo); @@ -1003,12 +1108,14 @@ static void ot_csrng_complete_sw_command(OtCSRNGInstance *inst, bool res) ot_csrng_update_irqs(s); } -static void ot_csrng_complete_hw_command(OtCSRNGInstance *inst, int res) +static void +ot_csrng_complete_hw_command(OtCSRNGInstance *inst, OtCSRNGCmdStatus sts) { - qemu_set_irq(inst->hw.req_sts, res); + qemu_set_irq(inst->hw.req_sts, (int)sts); } -static void ot_csrng_complete_command(OtCSRNGInstance *inst, int res) +static void ot_csrng_complete_command(OtCSRNGInstance *inst, + OtCSRNGCmdStatus sts) { uint32_t num; const uint32_t *buffer = @@ -1032,19 +1139,14 @@ static void ot_csrng_complete_command(OtCSRNGInstance *inst, int res) trace_ot_csrng_show_command("complete", slot, CMD_NAME(acmd), acmd); if (slot == SW_INSTANCE_ID) { - trace_ot_csrng_complete_command(slot, "sw", CMD_NAME(acmd), acmd, res); - ot_csrng_complete_sw_command(inst, res); + trace_ot_csrng_complete_command(slot, "sw", CMD_NAME(acmd), acmd, sts); + ot_csrng_complete_sw_command(inst, sts); } else { - trace_ot_csrng_complete_command(slot, "hw", CMD_NAME(acmd), acmd, res); - ot_csrng_complete_hw_command(inst, res); + trace_ot_csrng_complete_command(slot, "hw", CMD_NAME(acmd), acmd, sts); + ot_csrng_complete_hw_command(inst, sts); } - if (res == 0) { - CHANGE_STATE(s, CSRNG_IDLE); - } else { - CHANGE_STATE(s, CSRNG_ERROR); - ot_csrng_update_alerts(s); - } + CHANGE_STATE(s, CSRNG_IDLE); } static OtCSRNDCmdResult @@ -1052,7 +1154,7 @@ ot_csrng_handle_instantiate(OtCSRNGState *s, unsigned slot) { OtCSRNGInstance *inst = &s->instances[slot]; - trace_ot_csrng_instantiate(slot, true); + trace_ot_csrng_instantiate(slot); uint32_t command = ot_fifo32_peek(&inst->cmd_fifo); uint32_t clen = FIELD_EX32(command, OT_CSNRG_CMD, CLEN); @@ -1067,6 +1169,8 @@ ot_csrng_handle_instantiate(OtCSRNGState *s, unsigned slot) buffer += 1u; if (clen) { + /* ignore trailing additional words, as HW does */ + clen = MIN(clen, OT_CSRNG_CMD_WORD_MAX); xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "mat", buffer, clen * sizeof(uint32_t)); } @@ -1090,14 +1194,14 @@ ot_csrng_handle_uninstantiate(OtCSRNGState *s, unsigned slot) { OtCSRNGInstance *inst = &s->instances[slot]; - trace_ot_csrng_instantiate(slot, false); + trace_ot_csrng_uninstantiate(slot, true); ot_csrng_drng_uninstantiate(inst); return CSRNG_CMD_OK; } -static int ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) +static OtCSRNDCmdResult ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) { OtCSRNGInstance *inst = &s->instances[slot]; @@ -1108,7 +1212,7 @@ static int ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) xtrace_ot_csrng_error("generation for no packet"); CHANGE_STATE(s, CSRNG_ERROR); ot_csrng_update_alerts(s); - return -1; + return CSRNG_CMD_INVALID_GEN_CMD; } uint32_t clen = FIELD_EX32(command, OT_CSNRG_CMD, CLEN); @@ -1119,6 +1223,9 @@ static int ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) ot_fifo32_num_used(&inst->cmd_fifo), &num); g_assert(num - 1u == clen); buffer += 1u; + /* ignore trailing additional words, as HW does */ + clen = MIN(clen, OT_CSRNG_CMD_WORD_MAX); + xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "mat", buffer, clen * sizeof(uint32_t)); ot_csrng_drng_store_material(inst, buffer, clen); @@ -1136,7 +1243,7 @@ static int ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) /* should we resume? */ } - ot_csrng_drng_set_count(inst, packet_count); + inst->drng.rem_packet_count = packet_count; /* * do not ack command yet, @@ -1163,6 +1270,8 @@ static OtCSRNDCmdResult ot_csrng_handle_reseed(OtCSRNGState *s, unsigned slot) ot_fifo32_num_used(&inst->cmd_fifo), &num); g_assert(num - 1u == clen); buffer += 1u; + /* ignore trailing additional words, as HW does */ + clen = MIN(clen, OT_CSRNG_CMD_WORD_MAX); xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "mat", buffer, clen * sizeof(uint32_t)); @@ -1197,6 +1306,8 @@ static OtCSRNDCmdResult ot_csrng_handle_update(OtCSRNGState *s, unsigned slot) ot_fifo32_num_used(&inst->cmd_fifo), &num); g_assert(num - 1u == clen); buffer += 1u; + /* ignore trailing additional words, as HW does */ + clen = MIN(clen, OT_CSRNG_CMD_WORD_MAX); xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "mat", buffer, clen * sizeof(uint32_t)); @@ -1275,7 +1386,7 @@ static void ot_csrng_hwapp_filler_bh(void *opaque) * its readiness status (only generate commands complete async.) */ if (!ot_csrng_drng_remaining_count(inst)) { - ot_csrng_complete_command(inst, 0); + ot_csrng_complete_command(inst, CSRNG_STATUS_SUCCESS); } } } @@ -1299,12 +1410,13 @@ static void ot_csrng_swapp_fill(OtCSRNGInstance *inst) } else { /* check if the instance is running an deferred completion command */ if (inst->defer_completion) { - ot_csrng_complete_command(inst, 0); + ot_csrng_complete_command(inst, CSRNG_STATUS_SUCCESS); } } } -static bool ot_csrng_instance_is_command_ready(OtCSRNGInstance *inst) +static bool +ot_csrng_instance_is_command_ready(const OtCSRNGInstance *inst, bool fatal) { /* there should be a full command stored in the command FIFO */ if (ot_fifo32_is_empty(&inst->cmd_fifo)) { @@ -1313,10 +1425,25 @@ static bool ot_csrng_instance_is_command_ready(OtCSRNGInstance *inst) uint32_t command = ot_fifo32_peek(&inst->cmd_fifo); uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - return ot_fifo32_num_used(&inst->cmd_fifo) == length; + bool is_ready = ot_fifo32_num_used(&inst->cmd_fifo) == length; + + if (fatal && !is_ready) { + unsigned word_count = ot_fifo32_num_used(&inst->cmd_fifo); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %u: command 0x%06x empty: %u, length %u, exp: %u\n", + __func__, ot_csrng_get_slot(inst), command, + ot_fifo32_is_empty(&inst->cmd_fifo), word_count, length); + if (word_count < length) { + xtrace_ot_csrng_error("cannot execute an incomplete command"); + } else { + xtrace_ot_csrng_error("cannot execute an overflowed command"); + } + } + + return is_ready; } -static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) +static OtCSRNDCmdResult ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) { OtCSRNGInstance *inst = &s->instances[slot]; @@ -1345,12 +1472,12 @@ static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) R_RECOV_ALERT_STS_CMD_STAGE_INVALID_ACMD_ALERT_MASK; CHANGE_STATE(s, CSRNG_ERROR); ot_csrng_update_alerts(s); - return -1; + return CSRNG_CMD_INVALID_ACMD; } if (!s->enabled) { qemu_log_mask(LOG_GUEST_ERROR, "%s: not enabled\n", __func__); - return -1; + return CSRNG_CMD_INVALID_ACMD; } switch (acmd) { @@ -1358,7 +1485,7 @@ static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) if (ot_csrng_drng_is_instantiated(inst)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: instance %u already active\n", __func__, slot); - return -1; + return CSRNG_CMD_INVALID_CMD_SEQ; } break; case OT_CSRNG_CMD_UNINSTANTIATE: @@ -1369,11 +1496,11 @@ static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) __func__, slot); CHANGE_STATE(s, CSRNG_ERROR); ot_csrng_update_alerts(s); - return -1; + return CSRNG_CMD_INVALID_CMD_SEQ; } } - int res; + OtCSRNDCmdResult res; switch (acmd) { case OT_CSRNG_CMD_INSTANTIATE: @@ -1428,6 +1555,7 @@ static void ot_csrng_command_schedule(OtCSRNGState *s, OtCSRNGInstance *inst) __func__); s->regs[R_INTR_STATE] |= INTR_CS_HW_INST_EXC_MASK; ot_csrng_update_irqs(s); + g_assert(false); return; } @@ -1460,13 +1588,7 @@ static void ot_csrng_command_scheduler(void *opaque) trace_ot_csrng_show_command("pop", slot, CMD_NAME(acmd), acmd); - if (!ot_csrng_instance_is_command_ready(inst)) { - xtrace_ot_csrng_error("cannot execute an incomplete command"); - uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - qemu_log_mask(LOG_GUEST_ERROR, "empty: %u, length %u, exp: %u\n", - ot_fifo32_is_empty(&inst->cmd_fifo), - ot_fifo32_num_used(&inst->cmd_fifo), length); - + if (!ot_csrng_instance_is_command_ready(inst, true)) { g_assert_not_reached(); } @@ -1486,7 +1608,7 @@ static void ot_csrng_command_scheduler(void *opaque) * in this round. */ trace_ot_csrng_command_scheduler(slot, "cmd ready, execute"); - int res; + OtCSRNDCmdResult res; res = ot_csrng_handle_command(s, slot); switch (res) { case CSRNG_CMD_RETRY: @@ -1507,10 +1629,16 @@ static void ot_csrng_command_scheduler(void *opaque) } /* do not complete command either */ break; - case CSRNG_CMD_ERROR: case CSRNG_CMD_OK: + ot_csrng_complete_command(inst, CSRNG_STATUS_SUCCESS); + break; default: - ot_csrng_complete_command(inst, res); + trace_ot_csrng_reject_command(slot, command, res); + /* + * Negative res values are errors, encoding the type of command error. + * Convert the encoded error back into a OtCSRNGCmdStatus. + */ + ot_csrng_complete_command(inst, -res); break; } @@ -1534,7 +1662,19 @@ static void ot_csrng_command_scheduler(void *opaque) static uint32_t ot_csrng_read_state_db(OtCSRNGState *s) { unsigned appid = s->regs[R_INT_STATE_NUM]; - if (appid > OT_CSRNG_HW_APP_MAX) { + if (appid >= N_APP_COUNT) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid appid %d\n", __func__, + appid); + return 0; + } + if (!s->read_int_granted) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read state db disabled\n", + __func__); + return 0; + } + if (!((s->regs[R_INT_STATE_READ_ENABLE] >> appid) & 0x1)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read state db not enable for %d\n", + __func__, appid); return 0; } @@ -1553,7 +1693,7 @@ static uint32_t ot_csrng_read_state_db(OtCSRNGState *s) break; case 5u ... 12u: /* Key */ /* use big endian and reverse order to match OpenTitan order */ - base = 12 - s->state_db_ix; + base = 12u - s->state_db_ix; val32 = ldl_be_p(&drng->key[base * sizeof(uint32_t)]); break; case 13u: /* Status + Compliance, only 8 LSBs matter */ @@ -1590,6 +1730,7 @@ static uint64_t ot_csrng_regs_read(void *opaque, hwaddr addr, unsigned size) case R_INTR_ENABLE: case R_REGWEN: case R_CTRL: + case R_RESEED_INTERVAL: case R_SW_CMD_STS: case R_INT_STATE_NUM: case R_HW_EXC_STS: @@ -1598,8 +1739,17 @@ static uint64_t ot_csrng_regs_read(void *opaque, hwaddr addr, unsigned size) case R_ERR_CODE_TEST: val32 = s->regs[reg]; break; + case R_RESEED_COUNTER_0: + case R_RESEED_COUNTER_1: + case R_RESEED_COUNTER_2: { + unsigned appid = reg - R_RESEED_COUNTER_0; + g_assert(appid < N_APP_COUNT); + inst = &s->instances[appid]; + const OtCSRNGDrng *drng = &inst->drng; + val32 = drng->reseed_counter; + } break; case R_INT_STATE_VAL: - val32 = s->read_int_granted ? ot_csrng_read_state_db(s) : 0u; + val32 = ot_csrng_read_state_db(s); break; case R_MAIN_SM_STATE: switch (s->state) { @@ -1698,52 +1848,53 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_REGWEN: val32 &= R_REGWEN_EN_MASK; - s->regs[reg] &= val32; + s->regs[reg] &= val32; /* rw0c */ break; case R_CTRL: - if (s->regs[R_REGWEN]) { - uint32_t prev = s->regs[reg]; - val32 &= CTRL_MASK; - s->regs[reg] = val32; - CHECK_MULTIBOOT(s, CTRL, ENABLE); - CHECK_MULTIBOOT(s, CTRL, SW_APP_ENABLE); - CHECK_MULTIBOOT(s, CTRL, READ_INT_STATE); - uint32_t change = val32 ^ prev; - if (change) { - xtrace_ot_csrng_info("handling CTRL change", val32); - ot_csrng_handle_enable(s); - bool granted; - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); - const OtOTPEntropyCfg *entropy_cfg = - oc->get_entropy_cfg(s->otp_ctrl); - if (entropy_cfg) { - granted = entropy_cfg->en_csrng_sw_app_read == - OT_MULTIBITBOOL8_TRUE; - } else { - /* defaults to granted if no entropy config in OTP */ - granted = true; - } - if (granted) { - uint32_t sw_app_en = FIELD_EX32(val32, CTRL, SW_APP_ENABLE); - s->sw_app_granted = sw_app_en == OT_MULTIBITBOOL4_TRUE; - uint32_t read_int = FIELD_EX32(val32, CTRL, READ_INT_STATE); - s->read_int_granted = read_int == OT_MULTIBITBOOL4_TRUE; - } else { - s->sw_app_granted = false; - s->read_int_granted = false; - } + if (!s->regs[R_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s protected w/ REGWEN\n", + __func__, REG_NAME(reg)); + break; + } + uint32_t prev = s->regs[reg]; + val32 &= CTRL_MASK; + s->regs[reg] = val32; + CHECK_MULTIBOOT(s, CTRL, ENABLE); + CHECK_MULTIBOOT(s, CTRL, SW_APP_ENABLE); + CHECK_MULTIBOOT(s, CTRL, READ_INT_STATE); + CHECK_MULTIBOOT(s, CTRL, FIPS_FORCE_ENABLE); + uint32_t change = val32 ^ prev; + if (change) { + xtrace_ot_csrng_info("handling CTRL change", val32); + ot_csrng_handle_enable(s); + bool granted; + OtOTPStateClass *oc = + OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + const OtOTPEntropyCfg *entropy_cfg = + oc->get_entropy_cfg(s->otp_ctrl); + if (entropy_cfg) { + granted = + entropy_cfg->en_csrng_sw_app_read == OT_MULTIBITBOOL8_TRUE; + } else { + /* defaults to granted if no entropy config in OTP */ + granted = true; + } + if (granted) { + uint32_t sw_app_en = FIELD_EX32(val32, CTRL, SW_APP_ENABLE); + s->sw_app_granted = sw_app_en == OT_MULTIBITBOOL4_TRUE; + uint32_t read_int = FIELD_EX32(val32, CTRL, READ_INT_STATE); + s->read_int_granted = read_int == OT_MULTIBITBOOL4_TRUE; + } else { + s->sw_app_granted = false; + s->read_int_granted = false; } - } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: CTRL protected w/ REGWEN\n", - __func__); } break; case R_CMD_REQ: inst = &s->instances[SW_INSTANCE_ID]; if (!ot_fifo32_is_full(&inst->cmd_fifo)) { ot_fifo32_push(&inst->cmd_fifo, val32); - if (ot_csrng_instance_is_command_ready(inst)) { + if (ot_csrng_instance_is_command_ready(inst, false)) { /* * assume CMD RDY works the same way as the csrng_req_ready * wire, which is a blind guess, need to check RTL here @@ -1757,6 +1908,23 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, /* TBC: how to signal this error */ } break; + case R_RESEED_INTERVAL: + s->regs[reg] = val32; + ot_csrng_update_irqs(s); + break; + case R_INT_STATE_READ_ENABLE: + if (!s->regs[R_INT_STATE_READ_ENABLE_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s protected w/ REGWEN\n", + __func__, REG_NAME(reg)); + break; + } + val32 &= R_INT_STATE_READ_ENABLE_VAL_MASK; + s->regs[reg] = val32; + break; + case R_INT_STATE_READ_ENABLE_REGWEN: + val32 &= R_INT_STATE_READ_ENABLE_REGWEN_EN_MASK; + s->regs[reg] &= val32; /* rw0c */ + break; case R_INT_STATE_NUM: if (s->read_int_granted) { val32 &= R_INT_STATE_NUM_VAL_MASK; @@ -1768,12 +1936,29 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, } } break; + case R_FIPS_FORCE: + if (!s->regs[R_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s protected w/ REGWEN\n", + __func__, REG_NAME(reg)); + break; + } + val32 &= R_FIPS_FORCE_VAL_MASK; + s->regs[reg] = val32; + break; case R_ERR_CODE_TEST: + if (!s->regs[R_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s protected w/ REGWEN\n", + __func__, REG_NAME(reg)); + break; + } val32 &= R_ERR_CODE_TEST_VAL_MASK; val32 = 1u << val32; val32 &= ERR_CODE_MASK; s->regs[R_ERR_CODE] = val32; break; + case R_RESEED_COUNTER_0: + case R_RESEED_COUNTER_1: + case R_RESEED_COUNTER_2: case R_SW_CMD_STS: case R_GENBITS_VLD: case R_GENBITS: @@ -1827,6 +2012,7 @@ static void ot_csrng_reset(DeviceState *dev) s->regs[R_CTRL] = 0x9999u; s->regs[R_RESEED_INTERVAL] = 0xffffffffu; s->regs[R_INT_STATE_READ_ENABLE_REGWEN] = 0x1u; + s->regs[R_INT_STATE_READ_ENABLE] = 0x7u; s->regs[R_MAIN_SM_STATE] = 0x4eu; s->enabled = false; s->es_available = false; @@ -1836,7 +2022,7 @@ static void ot_csrng_reset(DeviceState *dev) s->es_retry_count = 0; s->state = CSRNG_IDLE; - for (unsigned ix = 0; ix < OT_CSRNG_HW_APP_MAX + 1u; ix++) { + for (unsigned ix = 0; ix < N_APP_COUNT; ix++) { OtCSRNGInstance *inst = &s->instances[ix]; g_assert(inst->parent); ot_fifo32_reset(&inst->cmd_fifo); @@ -1883,13 +2069,15 @@ static void ot_csrng_init(Object *obj) TYPE_OT_CSRNG "-genbits_ready", OT_CSRNG_HW_APP_MAX); + static_assert(CMD_FIFO_CAPACITY >= OT_CSRNG_CMD_WORD_MAX, + "Invalid CMD FIFO size"); /* HW instances + 1 internal SW instance */ - s->instances = g_new0(OtCSRNGInstance, OT_CSRNG_HW_APP_MAX + 1u); + s->instances = g_new0(OtCSRNGInstance, N_APP_COUNT); OtCSRNGInstance *inst = &s->instances[SW_INSTANCE_ID]; - for (unsigned ix = 0; ix < OT_CSRNG_HW_APP_MAX + 1u; ix++) { + for (unsigned ix = 0; ix < N_APP_COUNT; ix++) { inst = &s->instances[ix]; inst->parent = s; - ot_fifo32_create(&inst->cmd_fifo, OT_CSRNG_CMD_WORD_MAX); + ot_fifo32_create(&inst->cmd_fifo, CMD_FIFO_CAPACITY); if (ix != SW_INSTANCE_ID) { inst->hw.filler_bh = qemu_bh_new(&ot_csrng_hwapp_filler_bh, inst); } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index db083211d5ff3..312c01d690937 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -62,34 +62,36 @@ ot_common_configure_device_uint(const char *objid, const char *key, uint64_t val ot_csrng_change_state(int line, const char *old, int nold, const char *new, int nnew) "@ %d [%s:%d] -> [%s:%d]" ot_csrng_command_scheduler(unsigned slot, const char *action) "#%u: %s" ot_csrng_complete_command(unsigned slot, const char *kind, const char *cmd, unsigned acmd, int res) "#%u (%s) acmd: %s(%u): %d" -ot_csrng_connection(unsigned slot) "#%u" +ot_csrng_connection(unsigned slot, bool newc) "#%u new:%u" ot_csrng_defer_generation(unsigned slot) "#%u instanciation not yet completed" +ot_csrng_disconnection(unsigned slot, bool explicit) "#%u explicit:%u" ot_csrng_end_of_gen(unsigned slot, unsigned rempack) "#%u rem %u" ot_csrng_entropy_injecter(unsigned slot, const char *action) "#%u: %s" ot_csrng_entropy_rejected(unsigned slot, const char *reason, int res) "#%u: %s (%d)" ot_csrng_error(const char *func, int line, const char *err) "%s:%d %s" -ot_csrng_expedite_uninstantiation(unsigned slot) "#%u" ot_csrng_fill_entropy(unsigned slot, bool fips) "#%u fips %u" ot_csrng_generate(unsigned slot, unsigned count) "#%u %u packets to generate" ot_csrng_hwapp_need_entropy(unsigned slot, const char *msg) "#%u: %s" ot_csrng_hwapp_ready(unsigned slot, bool ready, unsigned rem) "#%u: %u rem %u" ot_csrng_info(const char *func, int line, const char *msg, uint32_t value) "%s:%d %s: 0x%08x" -ot_csrng_instantiate(unsigned slot, bool on) "#%u: %u" +ot_csrng_instantiate(unsigned slot) "#%u" ot_csrng_invalid_state(const char *func, const char *state, int st) "%s [%s:%d]" ot_csrng_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_csrng_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_csrng_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%08x eff:0x%08x" ot_csrng_push_command(unsigned slot, const char *cmd, unsigned acmd, char code, unsigned len) "#%u: %s(%u) %clen: %u" ot_csrng_read_state_db(unsigned slot, unsigned pos, uint32_t val) "#%u [%u] = 0x%08x" +ot_csrng_reject_command(unsigned slot, uint32_t command, int res) "#%u: cmd: 0x%08x res: %d" ot_csrng_request_entropy(unsigned slot, int gen) "#%u gen %d" -ot_csrng_retry_es_init(unsigned retry_count) "rescheduling initial ES request: %u to go" ot_csrng_reset(void) "" +ot_csrng_retry_es_init(unsigned retry_count) "rescheduling initial ES request: %u to go" ot_csrng_schedule(unsigned slot, const char *kind) "#%u: %s" ot_csrng_scheduling_command(unsigned slot) "#%u" ot_csrng_show_buffer(const char *func, int line, unsigned appid, const char *msg, const char *hexstr) "%s:%u #%u %s: %s" ot_csrng_show_command(const char *msg, unsigned slot, const char *cmd, unsigned acmd) "%s slot #%u, acmd: %s(%u)" ot_csrng_swapp_fill(unsigned count) "%u to go" ot_csrng_try_schedule_genbits(unsigned slot, bool ready, bool queued, unsigned rem) "? #%u rdy:%u q:%u rem:%u" +ot_csrng_uninstantiate(unsigned slot, bool explicit) "#%u explicit:%u" # ot_dev_proxy.c diff --git a/include/hw/opentitan/ot_csrng.h b/include/hw/opentitan/ot_csrng.h index c6f437bc5a6a6..5e609f773f466 100644 --- a/include/hw/opentitan/ot_csrng.h +++ b/include/hw/opentitan/ot_csrng.h @@ -2,6 +2,7 @@ * QEMU OpenTitan Cryptographically Secure Random Number Generator * * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -54,7 +55,16 @@ typedef enum { OT_CSRNG_CMD_GENERATE = 3, OT_CSRNG_CMD_UPDATE = 4, OT_CSRNG_CMD_UNINSTANTIATE = 5, -} OtCsrngCmd; +} OtCSRNGCmd; + +typedef enum { + CSRNG_STATUS_SUCCESS, + CSRNG_STATUS_INVALID_ACMD, + CSRNG_STATUS_INVALID_GEN_CMD, + CSRNG_STATUS_INVALID_CMD_SEQ, + CSRNG_STATUS_RESEED_CNT_EXCEEDED, + CSRNG_STATUS_COUNT, +} OtCSRNGCmdStatus; /* clang-format off */ REG32(OT_CSNRG_CMD, 0) @@ -80,22 +90,24 @@ typedef void (*ot_csrng_genbit_filler_fn)(void *opaque, const uint32_t *bits, bool fips); /** - * Connect a HW application to the CSRNG device. + * Connect or disconnect a HW application to the CSRNG device. * * @s the CSRNG device * @app_id the HW application unique identifier * @req_sts the IRQ to signal once a command is completed. The IRQ level signal * the completion status of the command: 0 indicates a sucessful - * completion, non-zero a failed one. - * @fn the filler function to call with one or more entropy packet, when a - * generate command is called. - * @opaque a opaque pointer to forward to the filler function + * completion, non-zero a failed one. Ignored if filler_fn is NULL. + * @filler_fn the filler function to call with one or more entropy packet, when + * a generate command is called. If filler_fn is NULL, the HW + * application is disconnected. + * @opaque a opaque pointer to forward to the filler_fn function * @return an IRQ line that signals whether the HW application is ready to * receive entropy, i.e. genbits_ready */ -qemu_irq ot_csnrg_connect_hw_app(OtCSRNGState *s, unsigned app_id, - qemu_irq req_sts, ot_csrng_genbit_filler_fn fn, - void *opaque); +qemu_irq +ot_csnrg_connect_hw_app(OtCSRNGState *s, unsigned app_id, qemu_irq req_sts, + ot_csrng_genbit_filler_fn filler_fn, void *opaque); + /** * Request a generated entropy block. @@ -116,11 +128,11 @@ int ot_csrng_request_entropy(OtCSRNGState *s, unsigned app_id); * @s the CSRNG device * @app_id the HW application unique identifier, as provided with the connect * command - * @command the command to execute (with any payload) - * @return 0 on success, -1 otherwise. If non-zero, the req_sts is not + * @word a command or payload chunk + * @return CSRNG_STATUS_SUCCESS on success. If failure, the req_sts is not * signalled for this command. */ -int ot_csrng_push_command(OtCSRNGState *s, unsigned app_id, - const uint32_t *command); +OtCSRNGCmdStatus ot_csrng_push_command(OtCSRNGState *s, unsigned app_id, + uint32_t word); #endif /* HW_OPENTITAN_OT_CSRNG_H */ From 47a1a1b990b2ea0dfd0fc42034ed16728ac6ad56 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 6 Mar 2025 15:42:35 +0100 Subject: [PATCH 09/37] [ot] hw/opentitan: ot_edn: rework EDN to match new HW implementation - new internal FSM states - new registers (HW vs. SW commands) - new alert on CSRNG error - reworked instantiation/uninstantiation sequences - SW command check whether a GENERATE command is transmitted, and preset the expected entropy packet count as done with Boot and Auto GENERATE commands. - CSRNG error management - bug fixing Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 954 ++++++++++++++++++++++---------------- hw/opentitan/trace-events | 9 +- 2 files changed, 559 insertions(+), 404 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index 99fece34c026a..2d26dae939f43 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -2,6 +2,7 @@ * QEMU OpenTitan Entropy Distribution Network device * * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -23,9 +24,6 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * - * Note: for now, only a minimalist subset of EDN device is implemented in order - * to enable OpenTitan's ROM boot to progress */ #include "qemu/osdep.h" @@ -42,7 +40,6 @@ #include "hw/riscv/ibex_common.h" #include "hw/riscv/ibex_irq.h" #include "hw/sysbus.h" -#include "sysemu/runstate.h" #include "trace.h" @@ -54,8 +51,8 @@ /* clang-format off */ REG32(INTR_STATE, 0x0u) - SHARED_FIELD(INTR_EDN_CMD_REQ_DONE_BIT, 0u, 1u) - SHARED_FIELD(INTR_EDN_FATAL_ERR_BIT, 1u, 1u) + SHARED_FIELD(INTR_EDN_CMD_REQ_DONE, 0u, 1u) + SHARED_FIELD(INTR_EDN_FATAL_ERR, 1u, 1u) REG32(INTR_ENABLE, 0x4u) REG32(INTR_TEST, 0x8u) REG32(ALERT_TEST, 0xcu) @@ -77,6 +74,11 @@ REG32(SW_CMD_STS, 0x24u) FIELD(SW_CMD_STS, CMD_ACK, 2u, 1u) FIELD(SW_CMD_STS, CMD_STS, 3u, 3u) REG32(HW_CMD_STS, 0x28u) + FIELD(HW_CMD_STS, BOOT_MODE, 0u, 1u) + FIELD(HW_CMD_STS, AUTO_MODE, 1u, 1u) + FIELD(HW_CMD_STS, CMD_TYPE, 2u, 4u) + FIELD(HW_CMD_STS, CMD_ACK, 6u, 1u) + FIELD(HW_CMD_STS, CMD_STS, 7u, 3u) REG32(RESEED_CMD, 0x2cu) REG32(GENERATE_CMD, 0x30u) REG32(MAX_NUM_REQS_BETWEEN_RESEEDS, 0x34u) @@ -86,10 +88,10 @@ REG32(RECOV_ALERT_STS, 0x38u) FIELD(RECOV_ALERT_STS, AUTO_REQ_MODE_FIELD_ALERT, 2u, 1u) FIELD(RECOV_ALERT_STS, CMD_FIFO_RST_FIELD_ALERT, 3u, 1u) FIELD(RECOV_ALERT_STS, EDN_BUS_CMP_ALERT, 12u, 1u) + FIELD(RECOV_ALERT_STS, CSRNG_ACK_ERR, 13u, 1u) REG32(ERR_CODE, 0x3cu) FIELD(ERR_CODE, SFIFO_RESCMD_ERR, 0u, 1u) FIELD(ERR_CODE, SFIFO_GENCMD_ERR, 1u, 1u) - FIELD(ERR_CODE, SFIFO_OUTPUT_ERR, 2u, 1u) FIELD(ERR_CODE, EDN_ACK_SM_ERR, 20u, 1u) FIELD(ERR_CODE, EDN_MAIN_SM_ERR, 21u, 1u) FIELD(ERR_CODE, EDN_CNTR_ERR, 22u, 1u) @@ -110,7 +112,7 @@ REG32(MAIN_SM_STATE, 0x44u) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") -#define INTR_MASK (INTR_EDN_CMD_REQ_DONE_BIT_MASK | INTR_EDN_FATAL_ERR_BIT_MASK) +#define INTR_MASK (INTR_EDN_CMD_REQ_DONE_MASK | INTR_EDN_FATAL_ERR_MASK) #define ALERT_TEST_MASK \ (R_ALERT_TEST_RECOV_ALERT_MASK | R_ALERT_TEST_FATAL_ALERT_MASK) #define CTRL_MASK \ @@ -124,14 +126,12 @@ REG32(MAIN_SM_STATE, 0x44u) R_RECOV_ALERT_STS_EDN_BUS_CMP_ALERT_MASK) #define ERR_CODE_MASK \ (R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | \ - R_ERR_CODE_SFIFO_OUTPUT_ERR_MASK | R_ERR_CODE_EDN_ACK_SM_ERR_MASK | \ - R_ERR_CODE_EDN_MAIN_SM_ERR_MASK | R_ERR_CODE_EDN_CNTR_ERR_MASK | \ - R_ERR_CODE_FIFO_WRITE_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK | \ - R_ERR_CODE_FIFO_STATE_ERR_MASK) -#define ERR_CODE_FIFO_MASK \ - (R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | \ - R_ERR_CODE_SFIFO_OUTPUT_ERR_MASK | R_ERR_CODE_FIFO_WRITE_ERR_MASK | \ + R_ERR_CODE_EDN_ACK_SM_ERR_MASK | R_ERR_CODE_EDN_MAIN_SM_ERR_MASK | \ + R_ERR_CODE_EDN_CNTR_ERR_MASK | R_ERR_CODE_FIFO_WRITE_ERR_MASK | \ R_ERR_CODE_FIFO_READ_ERR_MASK | R_ERR_CODE_FIFO_STATE_ERR_MASK) +#define ERR_CODE_ACTIVE_MASK \ + (R_ERR_CODE_EDN_ACK_SM_ERR_MASK | R_ERR_CODE_EDN_MAIN_SM_ERR_MASK | \ + R_ERR_CODE_EDN_CNTR_ERR_MASK) #define ALERT_STATUS_BIT(_x_) R_RECOV_ALERT_STS_##_x_##_FIELD_ALERT_MASK @@ -177,57 +177,56 @@ static_assert(ALERT_COUNT == PARAM_NUM_ALERTS, "Invalid alert count"); typedef enum { EDN_IDLE, /* idle */ + /* Boot */ EDN_BOOT_LOAD_INS, /* boot: load the instantiate command */ - EDN_BOOT_LOAD_GEN, /* boot: load the generate command */ EDN_BOOT_INS_ACK_WAIT, /* boot: wait for instantiate command ack */ - EDN_BOOT_CAPT_GEN_CNT, /* boot: capture the gen fifo count */ - EDN_BOOT_SEND_GEN_CMD, /* boot: send the generate command */ + EDN_BOOT_LOAD_GEN, /* boot: load the generate command */ EDN_BOOT_GEN_ACK_WAIT, /* boot: wait for generate command ack */ EDN_BOOT_PULSE, /* boot: signal a done pulse */ - EDN_BOOT_DONE, /* boot: stay in done state until reset */ + EDN_BOOT_DONE, /* boot: stay in done state until leaving boot */ + EDN_BOOT_LOAD_UNI, /* boot: load the uninstantiate command */ + EDN_BOOT_UNI_ACK_WAIT, /* boot: wait for uninstantiate command ack */ + /* Auto */ EDN_AUTO_LOAD_INS, /* auto: load the instantiate command */ EDN_AUTO_FIRST_ACK_WAIT, /* auto: wait for first instantiate command ack */ - EDN_AUTO_ACK_WAIT, /* auto: wait for auto command ack */ + EDN_AUTO_ACK_WAIT, /* auto: wait for instantiate command ack */ EDN_AUTO_DISPATCH, /* auto: determine next command to be sent */ EDN_AUTO_CAPT_GEN_CNT, /* auto: capture the gen fifo count */ EDN_AUTO_SEND_GEN_CMD, /* auto: send the generate command */ EDN_AUTO_CAPT_RESEED_CNT, /* auto: capture the reseed fifo count */ EDN_AUTO_SEND_RESEED_CMD, /* auto: send the reseed command */ + /* Misc */ EDN_SW_PORT_MODE, /* swport: no hw request mode */ + EDN_REJECT_CSRNG_ENTROPY, /* stop accepting entropy from CSRNG */ EDN_ERROR, /* illegal state reached and hang */ } OtEDNFsmState; -typedef enum { - CSRNG_STATUS_SUCCESS, - CSRNG_STATUS_INVALID_ACMD, - CSRNG_STATUS_INVALID_GEN_CMD, - CSRNG_STATUS_INVALID_CMD_SEQ, - CSRNG_STATUS_RESEED_CNT_EXCEEDED, -} OtCSRNGCmdStatus; - typedef struct { OtCSRNGState *device; /* CSRNG instance */ - OtCSRNGCmdStatus last_cmd_status; /* status of the last CSRNG command */ qemu_irq genbits_ready; /* Set when ready to receive entropy */ uint32_t appid; /* unique HW application id to identify on CSRNG */ - bool instantiated; /* instantiated state, not yet uninstantiated */ - bool no_fips; /* true if 1+ rcv entropy packets were no FIPS-compliant */ unsigned rem_packet_count; /* remaining expected packets in generate cmd */ + OtCSRNGCmdStatus hw_cmd_status; /* status of the last CSRNG command */ + OtCSRNGCmdStatus sw_cmd_status; /* status of the last SW command */ uint32_t buffer[OT_CSRNG_CMD_WORD_MAX]; /* temp buffer for commands */ OtFifo32 bits_fifo; /* input FIFO with entropy received from CSRNG */ - OtFifo32 sw_cmd_fifo; /* generic command output FIFO */ - OtFifo32 cmd_gen_fifo; /* FIFO to store replayed generate command */ - OtFifo32 cmd_reseed_fifo; /* FIFO to store replayed reseed command */ + OtFifo32 cmd_gen_fifo; /* "Replay" FIFO to store generate command */ + OtFifo32 cmd_reseed_fifo; /* "Replay" FIFO to store reseed command */ + uint8_t hw_cmd_type; /* type of the last CSRNG HW command */ + bool hw_ack; /* last SW command has been completed */ + bool sw_ack; /* last SW command has been completed */ + bool instantiated; /* instantiated state, not yet uninstantiated */ + bool no_fips; /* true if 1+ rcv entropy packets were no FIPS-compliant */ } OtEDNCSRNG; typedef struct OtEDNEndPoint { ot_edn_push_entropy_fn fn; /* function to call when entropy is available */ void *opaque; /* opaque pointer forwaded with the fn() call */ OtFifo32 fifo; /* output unpacker */ - bool fips; /* whether stored entropy is FIPS-compliant */ size_t gen_count; /* Number of 32-bit entropy word in current generation */ size_t total_count; /* Total number of 32-bit entropy words */ QSIMPLEQ_ENTRY(OtEDNEndPoint) request; + bool fips; /* whether stored entropy is FIPS-compliant */ } OtEDNEndPoint; typedef QSIMPLEQ_HEAD(OtEndpointQueue, OtEDNEndPoint) OtEndpointQueue; @@ -241,48 +240,50 @@ struct OtEDNState { QEMUBH *ep_bh; /**< Endpoint requests */ uint32_t *regs; + uint32_t recov_alert_sts; /* track signalled recovery alert */ - unsigned reseed_counter; /* track remaining requests before reseeding */ - bool sw_cmd_ready; /* ready to receive command in SW port mode */ + unsigned max_reqs_cnt; /* track remaining requests before reseeding */ OtEDNFsmState state; /* Main FSM state */ OtEDNCSRNG rng; OtEDNEndPoint endpoints[ENDPOINT_COUNT_MAX]; OtEndpointQueue ep_requests; + bool sw_cmd_ready; /* ready to receive command in SW port mode */ }; static const uint16_t OtEDNFsmStateCode[] = { - [EDN_IDLE] = 0b110000101, - [EDN_BOOT_LOAD_INS] = 0b110110111, + [EDN_IDLE] = 0b011000001, + [EDN_BOOT_LOAD_INS] = 0b111000111, + [EDN_BOOT_INS_ACK_WAIT] = 0b001111001, [EDN_BOOT_LOAD_GEN] = 0b000000011, - [EDN_BOOT_INS_ACK_WAIT] = 0b011010010, - [EDN_BOOT_CAPT_GEN_CNT] = 0b010111010, - [EDN_BOOT_SEND_GEN_CMD] = 0b011100100, - [EDN_BOOT_GEN_ACK_WAIT] = 0b101101100, - [EDN_BOOT_PULSE] = 0b100001010, - [EDN_BOOT_DONE] = 0b011011111, - [EDN_AUTO_LOAD_INS] = 0b001110000, - [EDN_AUTO_FIRST_ACK_WAIT] = 0b001001101, - [EDN_AUTO_ACK_WAIT] = 0b101100011, - [EDN_AUTO_DISPATCH] = 0b110101110, - [EDN_AUTO_CAPT_GEN_CNT] = 0b000110101, - [EDN_AUTO_SEND_GEN_CMD] = 0b111111000, - [EDN_AUTO_CAPT_RESEED_CNT] = 0b000100110, - [EDN_AUTO_SEND_RESEED_CMD] = 0b101010110, - [EDN_SW_PORT_MODE] = 0b100111001, - [EDN_ERROR] = 0b010010001, + [EDN_BOOT_GEN_ACK_WAIT] = 0b001110111, + [EDN_BOOT_PULSE] = 0b010101001, + [EDN_BOOT_DONE] = 0b011110000, + [EDN_BOOT_LOAD_UNI] = 0b100110101, + [EDN_BOOT_UNI_ACK_WAIT] = 0b000101100, + [EDN_AUTO_LOAD_INS] = 0b110111100, + [EDN_AUTO_FIRST_ACK_WAIT] = 0b110100011, + [EDN_AUTO_ACK_WAIT] = 0b010010010, + [EDN_AUTO_DISPATCH] = 0b101100001, + [EDN_AUTO_CAPT_GEN_CNT] = 0b100001110, + [EDN_AUTO_SEND_GEN_CMD] = 0b111011101, + [EDN_AUTO_CAPT_RESEED_CNT] = 0b010111111, + [EDN_AUTO_SEND_RESEED_CMD] = 0b001101010, + [EDN_SW_PORT_MODE] = 0b010010101, + [EDN_REJECT_CSRNG_ENTROPY] = 0b000011000, + [EDN_ERROR] = 0b101111110, }; #define STATE_NAME_ENTRY(_st_) [_st_] = stringify(_st_) static const char *STATE_NAMES[] = { STATE_NAME_ENTRY(EDN_IDLE), STATE_NAME_ENTRY(EDN_BOOT_LOAD_INS), - STATE_NAME_ENTRY(EDN_BOOT_LOAD_GEN), STATE_NAME_ENTRY(EDN_BOOT_INS_ACK_WAIT), - STATE_NAME_ENTRY(EDN_BOOT_CAPT_GEN_CNT), - STATE_NAME_ENTRY(EDN_BOOT_SEND_GEN_CMD), + STATE_NAME_ENTRY(EDN_BOOT_LOAD_GEN), STATE_NAME_ENTRY(EDN_BOOT_GEN_ACK_WAIT), STATE_NAME_ENTRY(EDN_BOOT_PULSE), STATE_NAME_ENTRY(EDN_BOOT_DONE), + STATE_NAME_ENTRY(EDN_BOOT_LOAD_UNI), + STATE_NAME_ENTRY(EDN_BOOT_UNI_ACK_WAIT), STATE_NAME_ENTRY(EDN_AUTO_LOAD_INS), STATE_NAME_ENTRY(EDN_AUTO_FIRST_ACK_WAIT), STATE_NAME_ENTRY(EDN_AUTO_ACK_WAIT), @@ -292,6 +293,7 @@ static const char *STATE_NAMES[] = { STATE_NAME_ENTRY(EDN_AUTO_CAPT_RESEED_CNT), STATE_NAME_ENTRY(EDN_AUTO_SEND_RESEED_CMD), STATE_NAME_ENTRY(EDN_SW_PORT_MODE), + STATE_NAME_ENTRY(EDN_REJECT_CSRNG_ENTROPY), STATE_NAME_ENTRY(EDN_ERROR), }; #undef STATE_NAME_ENTRY @@ -366,75 +368,112 @@ static void ot_edn_update_irqs(OtEDNState *s) static void ot_edn_update_alerts(OtEDNState *s) { uint32_t level = s->regs[R_ALERT_TEST]; - if (s->regs[R_ERR_CODE] & ERR_CODE_MASK) { - /* ERR_CODE is sticky */ + s->regs[R_ALERT_TEST] = 0u; + + /* only these errors seem to generate an alert (from HW observation) */ + if (s->regs[R_ERR_CODE] & ERR_CODE_ACTIVE_MASK) { level |= 1u << ALERT_FATAL; } - if (s->regs[R_ERR_CODE_TEST] & ERR_CODE_MASK) { + if (s->regs[R_ERR_CODE_TEST] & ERR_CODE_ACTIVE_MASK) { level |= 1u << ALERT_FATAL; + /* + * "The action of writing this register will force an error pulse." + * This documented assertion does not seem to hold true. Alert seems + * sticky + */ } if (s->regs[R_RECOV_ALERT_STS] & RECOV_ALERT_STS_MASK) { - level |= 1u << ALERT_RECOVERABLE; + /* recoverable alerts do not trigger stick alert */ + if (!(s->recov_alert_sts & (1u << ALERT_RECOVERABLE))) { + level |= 1u << ALERT_RECOVERABLE; + s->recov_alert_sts |= 1u << ALERT_RECOVERABLE; + } } for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); } } -static void ot_edn_check_multibitboot(OtEDNState *s, uint8_t mbbool, +static bool ot_edn_check_multibitboot(OtEDNState *s, uint8_t mbbool, uint32_t alert_bit) { switch (mbbool) { case OT_MULTIBITBOOL4_TRUE: + return true; case OT_MULTIBITBOOL4_FALSE: - return; + return false; default: break; } s->regs[R_RECOV_ALERT_STS] |= 1u << alert_bit; ot_edn_update_alerts(s); + return false; } -static bool ot_edn_is_enabled(OtEDNState *s) +static bool ot_edn_is_enabled(const OtEDNState *s) { uint32_t enable = FIELD_EX32(s->regs[R_CTRL], CTRL, EDN_ENABLE); return enable == OT_MULTIBITBOOL4_TRUE; } -static bool ot_edn_is_disabled(OtEDNState *s) +static bool ot_edn_is_boot_req_mode(const OtEDNState *s) { - uint32_t enable = FIELD_EX32(s->regs[R_CTRL], CTRL, EDN_ENABLE); + uint32_t auto_req = FIELD_EX32(s->regs[R_CTRL], CTRL, BOOT_REQ_MODE); - return enable == OT_MULTIBITBOOL4_FALSE; + return auto_req == OT_MULTIBITBOOL4_TRUE; } -static bool ot_edn_is_boot_req_mode(OtEDNState *s) +static bool ot_edn_is_auto_req_mode(const OtEDNState *s) { - uint32_t auto_req = FIELD_EX32(s->regs[R_CTRL], CTRL, BOOT_REQ_MODE); + uint32_t auto_req = FIELD_EX32(s->regs[R_CTRL], CTRL, AUTO_REQ_MODE); return auto_req == OT_MULTIBITBOOL4_TRUE; } -static bool ot_edn_is_auto_req_mode(OtEDNState *s) +static bool ot_edn_is_boot_mode(const OtEDNState *s) { - uint32_t auto_req = FIELD_EX32(s->regs[R_CTRL], CTRL, AUTO_REQ_MODE); + /* NOLINTNEXTLINE */ + switch (s->state) { + case EDN_BOOT_INS_ACK_WAIT ... EDN_BOOT_UNI_ACK_WAIT: + return true; + default: + return false; + } +} - return auto_req == OT_MULTIBITBOOL4_TRUE; +static bool ot_edn_is_auto_mode(const OtEDNState *s) +{ + /* NOLINTNEXTLINE */ + switch (s->state) { + case EDN_AUTO_ACK_WAIT ... EDN_AUTO_SEND_RESEED_CMD: + return true; + default: + return false; + } } -static bool ot_edn_is_sw_port_mode(OtEDNState *s) +static bool ot_edn_is_sw_cmd_mode(const OtEDNState *s) { - return !ot_edn_is_boot_req_mode(s) && !ot_edn_is_auto_req_mode(s); + /* NOLINTNEXTLINE */ + switch (s->state) { + case EDN_AUTO_LOAD_INS: + case EDN_AUTO_FIRST_ACK_WAIT: + case EDN_SW_PORT_MODE: + return true; + default: + return false; + } } -static OtCsrngCmd ot_edn_get_last_csrng_command(OtEDNState *s) +static OtCSRNGCmd ot_edn_get_last_csrng_command(const OtEDNState *s) { const OtEDNCSRNG *c = &s->rng; uint32_t last_cmd = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); + /* NOLINTNEXTLINE */ switch (last_cmd) { case OT_CSRNG_CMD_NONE: case OT_CSRNG_CMD_INSTANTIATE: @@ -442,26 +481,33 @@ static OtCsrngCmd ot_edn_get_last_csrng_command(OtEDNState *s) case OT_CSRNG_CMD_GENERATE: case OT_CSRNG_CMD_UPDATE: case OT_CSRNG_CMD_UNINSTANTIATE: - return (OtCsrngCmd)last_cmd; + return (OtCSRNGCmd)last_cmd; default: g_assert_not_reached(); } } -static bool ot_edn_is_cmd_rdy(OtEDNState *s, bool check_fifo) +static bool ot_edn_is_cmd_reg_rdy(const OtEDNState *s) { - if (!ot_edn_is_enabled(s)) { - return false; - } - if (check_fifo && ot_fifo32_is_full(&s->rng.sw_cmd_fifo)) { + /* NOLINTNEXTLINE */ + switch (s->state) { + case EDN_AUTO_LOAD_INS: + case EDN_AUTO_FIRST_ACK_WAIT: + case EDN_AUTO_DISPATCH: + return true; + case EDN_SW_PORT_MODE: + return s->sw_cmd_ready; + default: return false; } +} + +static bool ot_edn_is_cmd_rdy(const OtEDNState *s) +{ + /* NOLINTNEXTLINE */ switch (s->state) { - case EDN_IDLE: - case EDN_BOOT_PULSE: - case EDN_BOOT_DONE: case EDN_AUTO_LOAD_INS: - case EDN_AUTO_FIRST_ACK_WAIT: + case EDN_AUTO_DISPATCH: return true; case EDN_SW_PORT_MODE: return s->sw_cmd_ready; @@ -470,23 +516,77 @@ static bool ot_edn_is_cmd_rdy(OtEDNState *s, bool check_fifo) } } +static void ot_edn_reset_replay_fifos(OtEDNState *s) +{ + /* + * "The generate and reseed FIFOs are reset under four circumstances. These + * circumstances are: + * (a) when the EDN is disabled, + * (b) when the SWPortMode state is entered, + * (c) when the boot sequence has completed, or + * (d) when the EDN enters the Idle state after it finishes operation in + * auto mode." + */ + OtEDNCSRNG *c = &s->rng; + + trace_ot_edn_reset_replay_fifos(c->appid); + + ot_fifo32_reset(&c->cmd_gen_fifo); + ot_fifo32_reset(&c->cmd_reseed_fifo); +} + +static void ot_edn_manage_error(OtEDNState *s) +{ + s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_MASK; + if (s->regs[R_ERR_CODE] & R_ERR_CODE_EDN_MAIN_SM_ERR_MASK) { + /* real HW seems to add this error on clock cycle after main error */ + s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_ACK_SM_ERR_MASK; + } + ot_edn_update_irqs(s); + ot_edn_update_alerts(s); +} + static void ot_edn_change_state_line(OtEDNState *s, OtEDNFsmState state, int line) { - trace_ot_edn_change_state(s->rng.appid, line, STATE_NAME(s->state), - s->state, STATE_NAME(state), state); + if (state != s->state) { + trace_ot_edn_change_state(s->rng.appid, line, STATE_NAME(s->state), + s->state, STATE_NAME(state), state); + } s->state = state; - if (s->state == EDN_ERROR) { - s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_MAIN_SM_ERR_MASK; - ot_edn_update_alerts(s); + switch (s->state) { + case EDN_ERROR: + s->rng.hw_cmd_type = (uint8_t)OT_CSRNG_CMD_NONE; + ot_edn_manage_error(s); + break; + case EDN_IDLE: + case EDN_REJECT_CSRNG_ENTROPY: + s->rng.hw_cmd_type = (uint8_t)OT_CSRNG_CMD_NONE; + break; + default: + break; } } #define ot_edn_change_state(_s_, _st_) \ ot_edn_change_state_line(_s_, _st_, __LINE__) +static void ot_edn_connect_csrng(OtEDNState *s) +{ + OtEDNCSRNG *c = &s->rng; + + /* if the IRQ is not initialized, the EDN has yet to connected to CSRNG */ + if (!c->genbits_ready) { + qemu_irq req_sts; + req_sts = qdev_get_gpio_in_named(DEVICE(s), TYPE_OT_EDN "-req_sts", 0); + c->genbits_ready = ot_csnrg_connect_hw_app(c->device, c->appid, req_sts, + &ot_edn_fill_bits, s); + g_assert(c->genbits_ready); + } +} + static bool ot_edn_update_genbits_ready(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; @@ -504,24 +604,27 @@ static bool ot_edn_update_genbits_ready(OtEDNState *s) return accept_entropy; } -static int ot_edn_push_csrng_request(OtEDNState *s) +static OtCSRNGCmdStatus +ot_edn_push_csrng_request(OtEDNState *s, uint32_t length) { + ot_edn_connect_csrng(s); + OtEDNCSRNG *c = &s->rng; - /* if the IRQ is not initialized, the EDN has yet to connected to CSRNG */ - if (!c->genbits_ready) { - qemu_irq req_sts; - req_sts = qdev_get_gpio_in_named(DEVICE(s), TYPE_OT_EDN "-req_sts", 0); - c->genbits_ready = ot_csnrg_connect_hw_app(c->device, c->appid, req_sts, - &ot_edn_fill_bits, s); - g_assert(c->genbits_ready); - } - s->regs[R_SW_CMD_STS] |= R_SW_CMD_STS_CMD_ACK_MASK; - int res = ot_csrng_push_command(c->device, c->appid, c->buffer); - if (res) { - xtrace_ot_edn_error(c->appid, "CSRNG rejected command"); - /* do not expect any delayed completion */ - memset(c->buffer, 0, sizeof(*c->buffer)); + OtCSRNGCmdStatus res = CSRNG_STATUS_INVALID_ACMD; + + for (unsigned cix = 0; cix < length; cix++) { + trace_ot_edn_push_csrng_command(c->appid, c->buffer[cix]); + res = ot_csrng_push_command(c->device, c->appid, c->buffer[cix]); + if (res != CSRNG_STATUS_SUCCESS) { + trace_ot_edn_push_csrng_error(c->appid, (int)res); + ot_edn_change_state(s, EDN_REJECT_CSRNG_ENTROPY); + s->regs[R_RECOV_ALERT_STS] |= R_RECOV_ALERT_STS_CSRNG_ACK_ERR_MASK; + /* do not expect any delayed completion */ + memset(c->buffer, 0, sizeof(*c->buffer)); + ot_edn_update_alerts(s); + break; + } } return res; @@ -534,26 +637,38 @@ static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) uint32_t command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); if (command != OT_CSRNG_CMD_NONE) { xtrace_ot_edn_error(c->appid, "Another command is already scheduled"); + s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_MAIN_SM_ERR_MASK; ot_edn_change_state(s, EDN_ERROR); return; } c->buffer[0u] = s->regs[reg]; command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); + uint32_t clen = FIELD_EX32(command, OT_CSNRG_CMD, CLEN); + if (clen) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %u: boot command CLEN is non-zero\n", __func__, + c->appid); + /* + * the HW does not consider this case as an error and resume execution, + * which causes the CSRNG to stall + */ + } if (command == OT_CSRNG_CMD_GENERATE) { c->rem_packet_count = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, GLEN); xtrace_ot_edn_dinfo(c->appid, "Boot generation w/ packets", c->rem_packet_count); ot_edn_update_genbits_ready(s); - ot_edn_change_state(s, EDN_BOOT_CAPT_GEN_CNT); + ot_edn_change_state(s, EDN_BOOT_LOAD_GEN); for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); epix++) { s->endpoints[epix].gen_count = 0; } } - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot push command"); - ot_edn_change_state(s, EDN_ERROR); + + c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); + c->hw_cmd_status = ot_edn_push_csrng_request(s, 1u); + if (c->hw_cmd_status) { return; } @@ -573,187 +688,134 @@ static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) } } -static void ot_edn_try_auto_instantiate(OtEDNState *s) +static void ot_edn_send_auto_reseed_cmd(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; - if (ot_fifo32_is_empty(&c->sw_cmd_fifo)) { - /* instantiate command not yet loaded into the SW_CMD_REQ */ - xtrace_ot_edn_xinfo(c->appid, "no SW cmd in FIFO", 0); - return; - } - - uint32_t command = ot_fifo32_peek(&c->sw_cmd_fifo); - uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - if (ot_fifo32_num_used(&c->sw_cmd_fifo) < length) { - /* instantiate command not fully loaded into the SW_CMD_REQ */ - xtrace_ot_edn_xinfo(c->appid, "SW cmd FIFO incomplete", 0); - return; - } - - uint32_t num = length; - const uint32_t *cmd = ot_fifo32_pop_buf(&c->sw_cmd_fifo, num, &num); - g_assert(num == length); - memcpy(c->buffer, cmd, length * sizeof(uint32_t)); - - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot execute command"); - ot_edn_change_state(s, EDN_ERROR); - return; - } - - ot_edn_change_state(s, EDN_AUTO_FIRST_ACK_WAIT); -} - -static void ot_edn_send_reseed_cmd(OtEDNState *s) -{ - OtEDNCSRNG *c = &s->rng; + bool fatal_error = false; + uint32_t command; + uint32_t length; + memset(c->buffer, 0, sizeof(c->buffer)); if (ot_fifo32_is_empty(&c->cmd_reseed_fifo)) { s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; - s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; - ot_edn_update_irqs(s); - ot_edn_update_alerts(s); - return; + fatal_error = true; + command = (uint32_t)OT_CSRNG_CMD_NONE; + length = 1u; /* always push the command */ + } else { + command = ot_fifo32_peek(&c->cmd_reseed_fifo); + length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; + if (ot_fifo32_num_used(&c->cmd_reseed_fifo) < length) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | + R_ERR_CODE_FIFO_READ_ERR_MASK; + fatal_error = true; + } + uint32_t num = length; + const uint32_t *cmd; + cmd = ot_fifo32_peek_buf(&c->cmd_reseed_fifo, num, &length); + if (num != length) { + xtrace_ot_edn_error(c->appid, "incoherent reseed FIFO length"); + } + memcpy(c->buffer, cmd, length * sizeof(uint32_t)); } - uint32_t command = ot_fifo32_peek(&c->cmd_reseed_fifo); - uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - if (ot_fifo32_num_used(&c->cmd_reseed_fifo) < length) { - s->regs[R_ERR_CODE] |= - R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; - s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; - ot_edn_update_irqs(s); - ot_edn_update_alerts(s); + if (fatal_error) { + ot_edn_change_state(s, EDN_ERROR); return; } ot_edn_change_state(s, EDN_AUTO_SEND_RESEED_CMD); - uint32_t num = length; - const uint32_t *cmd = ot_fifo32_peek_buf(&c->cmd_reseed_fifo, num, &num); - g_assert(num == length); - memcpy(c->buffer, cmd, length * sizeof(uint32_t)); - - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot execute command"); - ot_edn_change_state(s, EDN_ERROR); + c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); + c->hw_cmd_status = ot_edn_push_csrng_request(s, length); + if (c->hw_cmd_status) { return; } ot_edn_change_state(s, EDN_AUTO_ACK_WAIT); } -static void ot_edn_send_generate_cmd(OtEDNState *s) +static void ot_edn_send_auto_generate_cmd(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; + bool fatal_error = false; + uint32_t command; + uint32_t length; + + memset(c->buffer, 0, sizeof(c->buffer)); if (ot_fifo32_is_empty(&c->cmd_gen_fifo)) { s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; - s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; - ot_edn_update_irqs(s); - ot_edn_update_alerts(s); - return; + fatal_error = true; + command = (uint32_t)OT_CSRNG_CMD_NONE; + length = 1u; /* always push the command */ + } else { + command = ot_fifo32_peek(&c->cmd_gen_fifo); + length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; + if (ot_fifo32_num_used(&c->cmd_gen_fifo) < length) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | + R_ERR_CODE_FIFO_READ_ERR_MASK; + fatal_error = true; + } + uint32_t num = length; + const uint32_t *cmd = + ot_fifo32_peek_buf(&c->cmd_gen_fifo, num, &length); + if (num != length) { + xtrace_ot_edn_error(c->appid, "incoherent generate FIFO length"); + } + memcpy(c->buffer, cmd, length * sizeof(uint32_t)); + c->rem_packet_count = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, GLEN); + xtrace_ot_edn_dinfo(c->appid, "Generate cmd w/ packets", + c->rem_packet_count); } - uint32_t command = ot_fifo32_peek(&c->cmd_gen_fifo); - xtrace_ot_edn_xinfo(c->appid, "COMMAND", command); - uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - if (ot_fifo32_num_used(&c->cmd_gen_fifo) < length) { - s->regs[R_ERR_CODE] |= - R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; - s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; - ot_edn_update_irqs(s); - ot_edn_update_alerts(s); + if (fatal_error) { + ot_edn_change_state(s, EDN_ERROR); return; } - uint32_t num = length; - const uint32_t *cmd = ot_fifo32_peek_buf(&c->cmd_gen_fifo, num, &num); - g_assert(num == length); - memcpy(c->buffer, cmd, length * sizeof(uint32_t)); - c->rem_packet_count = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, GLEN); - xtrace_ot_edn_dinfo(c->appid, "Generate cmd w/ packets", - c->rem_packet_count); - ot_edn_update_genbits_ready(s); + xtrace_ot_edn_xinfo(c->appid, "COMMAND", c->buffer[0]); - ot_edn_change_state(s, EDN_AUTO_SEND_GEN_CMD); + ot_edn_update_genbits_ready(s); + ot_edn_change_state(s, EDN_AUTO_SEND_RESEED_CMD); for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); epix++) { s->endpoints[epix].gen_count = 0; } - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot execute command"); - ot_edn_change_state(s, EDN_ERROR); + + c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); + c->hw_cmd_status = ot_edn_push_csrng_request(s, length); + if (c->hw_cmd_status) { return; } - if (s->reseed_counter) { - s->reseed_counter -= 1u; + if (s->max_reqs_cnt) { + s->max_reqs_cnt -= 1u; } ot_edn_change_state(s, EDN_AUTO_ACK_WAIT); } -static void ot_edn_send_uninstanciate_cmd(OtEDNState *s) +static void ot_edn_send_boot_uninstanciate_cmd(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; + g_assert(s->state == EDN_BOOT_LOAD_UNI); + memset(c->buffer, 0, sizeof(c->buffer)); c->buffer[0u] = FIELD_DP32(0, OT_CSNRG_CMD, ACMD, OT_CSRNG_CMD_UNINSTANTIATE); - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot execute command"); - ot_edn_change_state(s, EDN_ERROR); - return; - } - - /* TODO: what is the actual state of uninstantiating from boot mode? */ - ot_edn_change_state(s, EDN_AUTO_ACK_WAIT); -} - -static void ot_edn_try_sw_request(OtEDNState *s) -{ - OtEDNCSRNG *c = &s->rng; - - uint32_t command = ot_fifo32_peek(&c->sw_cmd_fifo); - uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u; - if (ot_fifo32_num_used(&c->sw_cmd_fifo) != length) { - /* command not yet complete */ + c->hw_cmd_type = (uint8_t)OT_CSRNG_CMD_UNINSTANTIATE; + c->hw_cmd_status = ot_edn_push_csrng_request(s, 1u); + if (c->hw_cmd_status) { return; } - g_assert(s->state == EDN_SW_PORT_MODE); - - uint32_t num = length; - const uint32_t *cmd = ot_fifo32_peek_buf(&c->sw_cmd_fifo, num, &num); - memcpy(c->buffer, cmd, length * sizeof(uint32_t)); - ot_fifo32_reset(&c->sw_cmd_fifo); - - s->sw_cmd_ready = false; - - command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); - - if (command == OT_CSRNG_CMD_GENERATE) { - c->rem_packet_count = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, GLEN); - xtrace_ot_edn_dinfo(c->appid, "SW generation w/ packets", - c->rem_packet_count); - ot_edn_update_genbits_ready(s); - for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); epix++) { - s->endpoints[epix].gen_count = 0; - } - } - - if (ot_edn_push_csrng_request(s)) { - xtrace_ot_edn_error(c->appid, "Cannot execute command"); - ot_edn_change_state(s, EDN_ERROR); - s->sw_cmd_ready = true; - return; - } + ot_edn_change_state(s, EDN_BOOT_UNI_ACK_WAIT); } static void ot_edn_handle_disable(OtEDNState *s) @@ -785,87 +847,222 @@ static void ot_edn_handle_disable(OtEDNState *s) /* signal CSRNG that EDN is no longer ready to receive entropy */ ot_edn_update_genbits_ready(s); - switch (s->state) { - case EDN_BOOT_GEN_ACK_WAIT: - /* no longer expect a generate ack */ - ot_edn_change_state(s, EDN_BOOT_DONE); - break; - case EDN_AUTO_ACK_WAIT: - /* no longer expect a generate ack */ - ot_edn_change_state(s, EDN_IDLE); - break; - default: - break; - } + /* disconnect */ + qemu_irq rdy; + rdy = ot_csnrg_connect_hw_app(c->device, c->appid, NULL, NULL, NULL); + g_assert(!rdy); - ot_edn_send_uninstanciate_cmd(s); + c->genbits_ready = NULL; } -static void ot_edn_complete_sw_req(OtEDNState *s) +static void ot_edn_clean_up(OtEDNState *s, bool discard_requests) { - s->sw_cmd_ready = true; - s->regs[R_INTR_STATE] |= INTR_EDN_CMD_REQ_DONE_BIT_MASK; + OtEDNCSRNG *c = &s->rng; + + trace_ot_edn_clean_up(c->appid, discard_requests); + + c->instantiated = false; + s->sw_cmd_ready = false; + c->hw_cmd_status = CSRNG_STATUS_SUCCESS; + c->sw_cmd_status = CSRNG_STATUS_SUCCESS; + c->hw_ack = false; + c->sw_ack = false; + s->recov_alert_sts = 0u; + s->max_reqs_cnt = 0; + memset(c->buffer, 0, sizeof(*c->buffer)); + ot_fifo32_reset(&c->bits_fifo); ot_edn_update_irqs(s); + ot_edn_update_alerts(s); + + if (discard_requests) { + /* clear all pending end point requests */ + while (QSIMPLEQ_FIRST(&s->ep_requests)) { + QSIMPLEQ_REMOVE_HEAD(&s->ep_requests, request); + } + } + + for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); epix++) { + ot_fifo32_reset(&s->endpoints[epix].fifo); + s->endpoints[epix].fips = false; + } + + bool accept_entropy = ot_edn_update_genbits_ready(s); + g_assert(!accept_entropy); + c->genbits_ready = NULL; + + ot_edn_change_state(s, EDN_IDLE); } -static void ot_edn_dispatch(OtEDNState *s) +static bool ot_edn_update_mode(OtEDNState *s) { + if (!ot_edn_is_enabled(s)) { + if (s->state != EDN_IDLE && s->state != EDN_ERROR) { + ot_edn_change_state(s, EDN_IDLE); + ot_edn_handle_disable(s); + } + return true; + } + + /* EDN is enabled */ + OtEDNCSRNG *c = &s->rng; - if (ot_edn_is_boot_req_mode(s)) { - xtrace_ot_edn_dinfo(c->appid, "Dispatch in boot mode", - c->rem_packet_count); - s->reseed_counter = s->regs[R_MAX_NUM_REQS_BETWEEN_RESEEDS]; - ot_edn_change_state(s, EDN_IDLE); - return; + if (s->state == EDN_IDLE) { + if (ot_edn_is_boot_req_mode(s)) { + trace_ot_edn_enable(c->appid, "boot mode"); + ot_edn_change_state(s, EDN_BOOT_LOAD_INS); + ot_edn_send_boot_req(s, R_BOOT_INS_CMD); + } else if (ot_edn_is_auto_req_mode(s)) { + trace_ot_edn_enable(c->appid, "auto mode"); + s->sw_cmd_ready = true; + ot_edn_change_state(s, EDN_AUTO_LOAD_INS); + } else { + trace_ot_edn_enable(c->appid, "sw mode"); + s->sw_cmd_ready = true; + ot_edn_reset_replay_fifos(s); + ot_edn_change_state(s, EDN_SW_PORT_MODE); + } + return true; } - if (ot_edn_is_sw_port_mode(s)) { - xtrace_ot_edn_dinfo(c->appid, "Dispatch in sw port mode", 0); - ot_edn_complete_sw_req(s); - return; + if (s->state == EDN_BOOT_DONE) { + trace_ot_edn_enable(c->appid, "boot uninstantiate"); + ot_edn_change_state(s, EDN_BOOT_LOAD_UNI); + ot_edn_send_boot_uninstanciate_cmd(s); + return true; } - xtrace_ot_edn_dinfo(c->appid, "Dispatch in auto mode", c->rem_packet_count); - if (s->reseed_counter == 0) { - ot_edn_change_state(s, EDN_AUTO_CAPT_RESEED_CNT); - ot_edn_send_reseed_cmd(s); - s->reseed_counter = s->regs[R_MAX_NUM_REQS_BETWEEN_RESEEDS]; - } else { - ot_edn_change_state(s, EDN_AUTO_CAPT_GEN_CNT); - ot_edn_send_generate_cmd(s); + return s->state == EDN_SW_PORT_MODE; +} + +static void ot_edn_handle_ctrl(OtEDNState *s, uint32_t val32) +{ +#define CHECK_MULTIBOOT(_s_, _r_, _b_) \ + ot_edn_check_multibitboot((_s_), FIELD_EX32(val32, _r_, _b_), \ + ALERT_STATUS_BIT(_b_)); + bool enable = CHECK_MULTIBOOT(s, CTRL, EDN_ENABLE); + bool boot_req_mode = CHECK_MULTIBOOT(s, CTRL, BOOT_REQ_MODE); + bool auto_req_mode = CHECK_MULTIBOOT(s, CTRL, AUTO_REQ_MODE); + bool cmd_fifo_rst = CHECK_MULTIBOOT(s, CTRL, CMD_FIFO_RST); + + bool disabling = (s->regs[R_CTRL] & R_CTRL_EDN_ENABLE_MASK) && + !(val32 & R_CTRL_EDN_ENABLE_MASK); + + s->regs[R_CTRL] = val32; + + OtEDNCSRNG *c = &s->rng; + + trace_ot_edn_ctrl_in_state(c->appid, STATE_NAME(s->state), s->state, enable, + boot_req_mode, auto_req_mode, cmd_fifo_rst); + + if ((FIELD_EX32(s->regs[R_CTRL], CTRL, CMD_FIFO_RST) == + OT_MULTIBITBOOL4_TRUE) || + disabling) { + ot_edn_reset_replay_fifos(s); + } + + if (disabling) { + ot_edn_clean_up(s, true); + } + + if (!ot_edn_update_mode(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %u: EDN in %s, write 0x%08x to CTRL is delayed\n", + __func__, c->appid, STATE_NAME(s->state), val32); } } -static void ot_edn_clean_up(OtEDNState *s, bool discard_requests) +static void ot_edn_complete_sw_req(OtEDNState *s, OtCSRNGCmdStatus cmd_status) { OtEDNCSRNG *c = &s->rng; - c->instantiated = false; - s->sw_cmd_ready = false; - s->rng.last_cmd_status = CSRNG_STATUS_SUCCESS; - s->reseed_counter = 0; - memset(c->buffer, 0, sizeof(*c->buffer)); - ot_fifo32_reset(&c->bits_fifo); - ot_fifo32_reset(&c->sw_cmd_fifo); + c->sw_cmd_status = cmd_status; + c->sw_ack = true; + s->sw_cmd_ready = cmd_status == CSRNG_STATUS_SUCCESS; + s->regs[R_INTR_STATE] |= INTR_EDN_CMD_REQ_DONE_MASK; + ot_edn_update_irqs(s); - ot_edn_update_alerts(s); +} - if (discard_requests) { - /* clear all pending end point requests */ - while (QSIMPLEQ_FIRST(&s->ep_requests)) { - QSIMPLEQ_REMOVE_HEAD(&s->ep_requests, request); +static void ot_edn_handle_sw_cmd_req(OtEDNState *s, uint32_t value) +{ + OtEDNCSRNG *c = &s->rng; + + if (!ot_edn_is_sw_cmd_mode(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %u: ignore SW REQ in %s\n", + __func__, c->appid, STATE_NAME(s->state)); + return; + } + + ot_edn_connect_csrng(s); + + c->sw_ack = false; + + trace_ot_edn_push_csrng_command(c->appid, value); + OtCSRNGCmdStatus res = ot_csrng_push_command(c->device, c->appid, value); + if (res != CSRNG_STATUS_SUCCESS) { + xtrace_ot_edn_error(c->appid, "CSRNG rejected command"); + s->sw_cmd_ready = false; + s->regs[R_RECOV_ALERT_STS] |= R_RECOV_ALERT_STS_CSRNG_ACK_ERR_MASK; + ot_edn_change_state(s, EDN_REJECT_CSRNG_ENTROPY); + ot_edn_complete_sw_req(s, res); + ot_edn_update_alerts(s); + return; + } + + if (s->state == EDN_SW_PORT_MODE) { + if (s->sw_cmd_ready) { + /* + * first word for this command sequence: + * 1. value contains the actual command + * 2. flag SW command as not ready till the command is completed + */ + s->sw_cmd_ready = false; + uint32_t command = FIELD_EX32(value, OT_CSNRG_CMD, ACMD); + if (command == OT_CSRNG_CMD_GENERATE) { + c->rem_packet_count = FIELD_EX32(value, OT_CSNRG_CMD, GLEN); + xtrace_ot_edn_dinfo(c->appid, "SW generation w/ packets", + c->rem_packet_count); + ot_edn_update_genbits_ready(s); + for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); + epix++) { + s->endpoints[epix].gen_count = 0; + } + } } } - for (unsigned epix = 0; epix < ARRAY_SIZE(s->endpoints); epix++) { - ot_fifo32_reset(&s->endpoints[epix].fifo); - s->endpoints[epix].fips = false; + if (s->state == EDN_AUTO_LOAD_INS) { + ot_edn_change_state(s, EDN_AUTO_FIRST_ACK_WAIT); + } +} + +static void ot_edn_auto_dispatch(OtEDNState *s) +{ + const OtEDNCSRNG *c = &s->rng; + + bool auto_req_mode = ot_edn_is_auto_req_mode(s); + trace_ot_edn_auto_dispatch(c->appid, auto_req_mode, s->max_reqs_cnt); + + ot_edn_update_irqs(s); + + if (!auto_req_mode) { + ot_edn_change_state(s, EDN_IDLE); + ot_edn_reset_replay_fifos(s); + ot_edn_update_mode(s); + return; } - if (c->genbits_ready) { - qemu_set_irq(c->genbits_ready, 0); + + if (s->state == EDN_AUTO_DISPATCH) { + if (s->max_reqs_cnt == 0) { + ot_edn_change_state(s, EDN_AUTO_CAPT_RESEED_CNT); + ot_edn_send_auto_reseed_cmd(s); + s->max_reqs_cnt = s->regs[R_MAX_NUM_REQS_BETWEEN_RESEEDS]; + } else { + ot_edn_change_state(s, EDN_AUTO_CAPT_GEN_CNT); + ot_edn_send_auto_generate_cmd(s); + } } - ot_edn_change_state(s, EDN_IDLE); } static void ot_edn_fill_bits(void *opaque, const uint32_t *bits, bool fips) @@ -880,8 +1077,8 @@ static void ot_edn_fill_bits(void *opaque, const uint32_t *bits, bool fips) break; default: xtrace_ot_edn_error(c->appid, "unexpected state"); + s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_MAIN_SM_ERR_MASK; ot_edn_change_state(s, EDN_ERROR); - qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_PANIC, 1); break; } @@ -896,7 +1093,7 @@ static void ot_edn_fill_bits(void *opaque, const uint32_t *bits, bool fips) xtrace_ot_edn_error(c->appid, "entropy input fifo overflow"); s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_WRITE_ERR_MASK; - s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; + s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_MASK; ot_edn_update_irqs(s); ot_edn_update_alerts(s); return; @@ -996,76 +1193,76 @@ static void ot_edn_csrng_ack_irq(void *opaque, int n, int level) trace_ot_edn_csrng_ack(c->appid, STATE_NAME(s->state), level); - OtCsrngCmd last_cmd = ot_edn_get_last_csrng_command(s); /* * cleaning up the first world would be enough, clearing the whole buffer * help debugging */ memset(c->buffer, 0, sizeof(*c->buffer)); - c->last_cmd_status = - level != 0 ? CSRNG_STATUS_INVALID_ACMD : CSRNG_STATUS_SUCCESS; + OtCSRNGCmdStatus cmd_status; - if (level) { - xtrace_ot_edn_error(c->appid, "last command failed"); - ot_edn_change_state(s, EDN_ERROR); - /* TODO: better error handling is required (IRQ signalling, ...)*/ + /* NOLINTNEXTLINE */ + switch (level) { + case CSRNG_STATUS_SUCCESS: + case CSRNG_STATUS_INVALID_ACMD: + case CSRNG_STATUS_INVALID_GEN_CMD: + case CSRNG_STATUS_INVALID_CMD_SEQ: + case CSRNG_STATUS_RESEED_CNT_EXCEEDED: + cmd_status = (OtCSRNGCmdStatus)level; + break; + default: + qemu_log("%s: unexpected CSRNG ack value: %d\n", __func__, level); g_assert_not_reached(); return; } - /* success */ - switch (last_cmd) { - case OT_CSRNG_CMD_INSTANTIATE: - c->instantiated = true; - break; - case OT_CSRNG_CMD_UNINSTANTIATE: - if (ot_edn_is_sw_port_mode(s)) { - ot_edn_complete_sw_req(s); + if (cmd_status != CSRNG_STATUS_SUCCESS) { + trace_ot_edn_push_csrng_error(c->appid, level); + if (s->state != EDN_ERROR) { + ot_edn_change_state(s, EDN_REJECT_CSRNG_ENTROPY); } -#ifdef EDN_DISCARD_PENDING_REQUEST_ON_DISABLE - ot_edn_clean_up(s, true); -#else - ot_edn_clean_up(s, false); -#endif - return; - default: - break; } switch (s->state) { case EDN_BOOT_INS_ACK_WAIT: ot_edn_change_state(s, EDN_BOOT_LOAD_GEN); ot_edn_send_boot_req(s, R_BOOT_GEN_CMD); + c->hw_cmd_status = cmd_status; break; case EDN_BOOT_GEN_ACK_WAIT: - /* todo: boot pulse/done only activated once the generation is over? */ ot_edn_change_state(s, EDN_BOOT_PULSE); ot_edn_change_state(s, EDN_BOOT_DONE); + c->hw_cmd_status = cmd_status; + ot_edn_update_mode(s); + break; + case EDN_BOOT_UNI_ACK_WAIT: + ot_edn_reset_replay_fifos(s); + ot_edn_change_state(s, EDN_IDLE); + c->hw_cmd_status = cmd_status; + ot_edn_update_mode(s); break; case EDN_AUTO_FIRST_ACK_WAIT: ot_edn_change_state(s, EDN_AUTO_DISPATCH); - ot_edn_dispatch(s); + ot_edn_complete_sw_req(s, cmd_status); + ot_edn_auto_dispatch(s); break; case EDN_AUTO_ACK_WAIT: ot_edn_change_state(s, EDN_AUTO_DISPATCH); - ot_edn_dispatch(s); + c->hw_cmd_status = cmd_status; + ot_edn_auto_dispatch(s); break; case EDN_SW_PORT_MODE: - ot_edn_dispatch(s); + ot_edn_complete_sw_req(s, cmd_status); break; default: - trace_ot_edn_invalid_state(c->appid, __func__, STATE_NAME(s->state), - s->state); - s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_ACK_SM_ERR_MASK; - ot_edn_update_alerts(s); - g_assert_not_reached(); + break; } } static uint64_t ot_edn_regs_read(void *opaque, hwaddr addr, unsigned size) { OtEDNState *s = opaque; + OtEDNCSRNG *c = &s->rng; (void)size; uint32_t val32; @@ -1084,15 +1281,22 @@ static uint64_t ot_edn_regs_read(void *opaque, hwaddr addr, unsigned size) case R_ERR_CODE_TEST: val32 = s->regs[reg]; break; - case R_SW_CMD_STS: { - uint32_t rdy = (uint32_t)ot_edn_is_cmd_rdy(s, false); - uint32_t reg_rdy = (uint32_t)ot_edn_is_cmd_rdy(s, true); - val32 = s->regs[R_SW_CMD_STS]; - val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_STS, s->rng.last_cmd_status); - val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_RDY, rdy); - val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_REG_RDY, reg_rdy); + case R_HW_CMD_STS: + val32 = FIELD_DP32(0u, HW_CMD_STS, BOOT_MODE, ot_edn_is_boot_mode(s)); + val32 = + FIELD_DP32(val32, HW_CMD_STS, AUTO_MODE, ot_edn_is_auto_mode(s)); + val32 = + FIELD_DP32(val32, HW_CMD_STS, CMD_TYPE, (uint32_t)c->hw_cmd_type); + val32 = FIELD_DP32(val32, HW_CMD_STS, CMD_ACK, c->hw_ack); + val32 = FIELD_DP32(val32, HW_CMD_STS, CMD_STS, c->hw_cmd_status); + break; + case R_SW_CMD_STS: + val32 = + FIELD_DP32(0u, SW_CMD_STS, CMD_REG_RDY, ot_edn_is_cmd_reg_rdy(s)); + val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_RDY, ot_edn_is_cmd_rdy(s)); + val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_ACK, c->sw_ack); + val32 = FIELD_DP32(val32, SW_CMD_STS, CMD_STS, c->sw_cmd_status); break; - } case R_MAIN_SM_STATE: switch (s->state) { case EDN_IDLE ... EDN_ERROR: @@ -1127,10 +1331,6 @@ static uint64_t ot_edn_regs_read(void *opaque, hwaddr addr, unsigned size) return (uint64_t)val32; }; -#define CHECK_MULTIBOOT(_s_, _r_, _b_) \ - ot_edn_check_multibitboot((_s_), FIELD_EX32(s->regs[R_##_r_], _r_, _b_), \ - ALERT_STATUS_BIT(_b_)); - static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, unsigned size) { @@ -1170,76 +1370,20 @@ static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, s->regs[reg] &= val32; break; case R_CTRL: - if (s->regs[R_REGWEN]) { - val32 &= CTRL_MASK; - s->regs[reg] = val32; - CHECK_MULTIBOOT(s, CTRL, EDN_ENABLE); - CHECK_MULTIBOOT(s, CTRL, BOOT_REQ_MODE); - CHECK_MULTIBOOT(s, CTRL, AUTO_REQ_MODE); - CHECK_MULTIBOOT(s, CTRL, CMD_FIFO_RST); - if (FIELD_EX32(s->regs[reg], CTRL, CMD_FIFO_RST) == - OT_MULTIBITBOOL4_TRUE) { - ot_fifo32_reset(&s->rng.cmd_gen_fifo); - ot_fifo32_reset(&s->rng.cmd_reseed_fifo); - } - trace_ot_edn_ctrl_in_state(c->appid, STATE_NAME(s->state), - s->state); - if (s->state == EDN_IDLE) { - if (ot_edn_is_enabled(s)) { - if (ot_edn_is_boot_req_mode(s)) { - trace_ot_edn_enable(c->appid, "boot mode"); - ot_edn_change_state(s, EDN_BOOT_LOAD_INS); - ot_edn_send_boot_req(s, R_BOOT_INS_CMD); - } else if (ot_edn_is_auto_req_mode(s)) { - trace_ot_edn_enable(c->appid, "auto mode"); - ot_edn_change_state(s, EDN_AUTO_LOAD_INS); - ot_edn_try_auto_instantiate(s); - } else { - trace_ot_edn_enable(c->appid, "sw mode"); - ot_fifo32_reset(&c->sw_cmd_fifo); - s->sw_cmd_ready = true; - ot_edn_change_state(s, EDN_SW_PORT_MODE); - } - } - } else if (ot_edn_is_disabled(s)) { - OtCsrngCmd last_cmd = ot_edn_get_last_csrng_command(s); - /* do not pile up uninstanciation requests */ - if (last_cmd != OT_CSRNG_CMD_UNINSTANTIATE) { - trace_ot_edn_enable(c->appid, "disabling"); - ot_edn_handle_disable(s); - } - } - } else { + if (!s->regs[R_REGWEN]) { qemu_log_mask(LOG_GUEST_ERROR, "Cannot change CTRL, REGWEN disabled"); + break; } + val32 &= CTRL_MASK; + ot_edn_handle_ctrl(s, val32); break; case R_BOOT_INS_CMD: case R_BOOT_GEN_CMD: s->regs[reg] = val32; break; case R_SW_CMD_REQ: - s->regs[R_SW_CMD_STS] &= ~R_SW_CMD_STS_CMD_ACK_MASK; - /* ignore all sw commands in auto req mode once instantiated */ - if (ot_edn_is_auto_req_mode(s) && c->instantiated) { - xtrace_ot_edn_dinfo(c->appid, "ignore SW REQ", c->appid); - break; - } - if (!ot_fifo32_is_full(&c->sw_cmd_fifo)) { - ot_fifo32_push(&c->sw_cmd_fifo, val32); - if (s->state == EDN_AUTO_LOAD_INS) { - ot_edn_try_auto_instantiate(s); - break; - } - if (ot_edn_is_sw_port_mode(s)) { - ot_edn_try_sw_request(s); - break; - } - } else { - s->regs[R_ERR_CODE] |= R_ERR_CODE_FIFO_WRITE_ERR_MASK; - ot_edn_update_alerts(s); - xtrace_ot_edn_error(c->appid, "sw fifo full"); - } + ot_edn_handle_sw_cmd_req(s, val32); break; case R_RESEED_CMD: if (ot_fifo32_is_full(&c->cmd_reseed_fifo)) { @@ -1260,15 +1404,24 @@ static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_MAX_NUM_REQS_BETWEEN_RESEEDS: s->regs[reg] = val32; - s->reseed_counter = val32; + s->max_reqs_cnt = val32; break; case R_RECOV_ALERT_STS: val32 &= RECOV_ALERT_STS_MASK; - s->regs[reg] = val32; + s->regs[reg] &= val32; /* rw0c */ + s->recov_alert_sts &= val32; + ot_edn_update_alerts(s); break; case R_ERR_CODE_TEST: val32 &= R_ERR_CODE_TEST_VAL_MASK; s->regs[reg] = val32; + if ((1u << val32) & ERR_CODE_ACTIVE_MASK) { + if (s->state != EDN_ERROR) { + s->regs[R_ERR_CODE] |= 1u << val32; + ot_edn_change_state(s, EDN_ERROR); + } + } + ot_edn_update_irqs(s); ot_edn_update_alerts(s); break; case R_SW_CMD_STS: @@ -1315,8 +1468,6 @@ static void ot_edn_reset(DeviceState *dev) ot_edn_clean_up(s, true); - /* do not reset connection info since reset order is not known */ - (void)c->genbits_ready; ot_fifo32_reset(&c->cmd_gen_fifo); ot_fifo32_reset(&c->cmd_reseed_fifo); @@ -1347,7 +1498,6 @@ static void ot_edn_init(Object *obj) s->ep_bh = qemu_bh_new(&ot_edn_handle_ep_request, s); ot_fifo32_create(&c->bits_fifo, OT_CSRNG_PACKET_WORD_COUNT * 2u); - ot_fifo32_create(&c->sw_cmd_fifo, OT_CSRNG_CMD_WORD_MAX); ot_fifo32_create(&c->cmd_gen_fifo, OT_CSRNG_CMD_WORD_MAX); ot_fifo32_create(&c->cmd_reseed_fifo, OT_CSRNG_CMD_WORD_MAX); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 312c01d690937..fce8ee718ac80 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -133,10 +133,12 @@ ot_dma_transfer(const char *id, const char *dir, const char *asname, uint64_t ad # ot_edn.c +ot_edn_auto_dispatch(unsigned appid, bool auto_, unsigned max_reqs_cnt) "a#%u auto:%u max_reqs_cnt:%u" +ot_edn_clean_up(unsigned appid, bool discard) "a#%u discard:%u" ot_edn_change_state(unsigned appid, int line, const char *old, int nold, const char *new, int nnew) "a#%u @ %d [%s:%d] -> [%s:%d]" ot_edn_connect_endpoint(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_csrng_ack(unsigned appid, const char *state, int level) "a#%u %s %d" -ot_edn_ctrl_in_state(unsigned appid, const char *state, int nstate) "a#%u [%s:%d]" +ot_edn_ctrl_in_state(unsigned appid, const char *state, int nstate, bool en, bool boot, bool auto_, bool clr) "a#%u [%s:%d] en:%u boot:%u auto:%u clr:%u" ot_edn_dinfo(unsigned appid, const char *func, int line, const char *msg, uint32_t value) "a#%u %s:%d %s %u" ot_edn_enable(unsigned appid, const char *msg) "a#%u: %s" ot_edn_ep_fifo(unsigned appid, const char *msg, unsigned remwslot) "a#%u %s rem:%u" @@ -148,9 +150,12 @@ ot_edn_handle_ep_request(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_invalid_state(unsigned appid, const char *func, const char *state, int st) "a#%u %s [%s:%d]" ot_edn_io_read_out(unsigned appid, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "a#%u addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_edn_io_write(unsigned appid, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "a#%u addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_edn_irqs(unsigned appid, uint32_t active, uint32_t mask, uint32_t eff) "#%u act:0x%08x msk:0x%08x eff:0x%08x" +ot_edn_irqs(unsigned appid, uint32_t active, uint32_t mask, uint32_t eff) "a#%u act:0x%08x msk:0x%08x eff:0x%08x" +ot_edn_push_csrng_command(unsigned appid, uint32_t value) "a#%u cmd:0x%08x" +ot_edn_push_csrng_error(unsigned appid, int res) "a#%u res:%d" ot_edn_request_entropy(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_reset(unsigned appid) "a#%u" +ot_edn_reset_replay_fifos(unsigned appid) "a#%u" ot_edn_schedule(unsigned appid, const char *cause) "a#%u %s" ot_edn_update_genbits_ready(unsigned appid, unsigned rem, unsigned fslot, bool accept) "a#%u rem packet %u, free slot %u, accept? %u" ot_edn_xinfo(unsigned appid, const char *func, int line, const char *msg, uint32_t value) "a#%u %s:%d %s 0x%08x" From 82b2434827555e0ec6e310502605fd22711efcbc Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 25 Mar 2025 16:51:54 +0100 Subject: [PATCH 10/37] [ot] hw/opentitan: ot_aes: add trace for selected reseed rates Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_aes.c | 2 ++ hw/opentitan/trace-events | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/opentitan/ot_aes.c b/hw/opentitan/ot_aes.c index 43c0cd51f8868..c977fb09dd870 100644 --- a/hw/opentitan/ot_aes.c +++ b/hw/opentitan/ot_aes.c @@ -399,6 +399,8 @@ static inline void ot_aes_load_reseed_rate(OtAESState *s) break; } + trace_ot_aes_reseed_rate(reseed); + s->reseed_count = reseed; } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index fce8ee718ac80..4f33478f5dfd3 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -12,6 +12,7 @@ ot_aes_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t p ot_aes_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_aes_request_entropy(void) "" ot_aes_reseed(const char *reason) "%s" +ot_aes_reseed_rate(unsigned rate) "%u" ot_aes_schedule(void) "" # ot_alert.c From 9ce285aa2ba3037309930e2363f0004f2d547fc4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 26 Mar 2025 14:13:49 +0100 Subject: [PATCH 11/37] [ot] hw/opentitan: ot_aes: delay status_output_valid bit When AES forces a PRNG reseed, the EDN may take some time to deliver an entropy packet. However, unit tests assume that as soon as data out is available, once output data have been read, the AES IP should return to the IDLE state. IDLE state cannot be reached till a PRNG reseed is ongoing. This means that the output data should not be made available to the vCPU until any PRNG reseed has completed. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_aes.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_aes.c b/hw/opentitan/ot_aes.c index c977fb09dd870..e0124f1b82acc 100644 --- a/hw/opentitan/ot_aes.c +++ b/hw/opentitan/ot_aes.c @@ -201,6 +201,7 @@ typedef struct OtAESRegisters { DECLARE_BITMAP(data_in_bm, PARAM_NUM_REGS_DATA); DECLARE_BITMAP(data_out_bm, PARAM_NUM_REGS_DATA); uint32_t key[PARAM_NUM_REGS_KEY]; + bool data_out_rdy; /* AES output data exist, not yet published */ } OtAESRegisters; typedef struct OtAESContext { @@ -805,9 +806,7 @@ static void ot_aes_push(OtAESState *s) memcpy(r->data_out, c->dst, sizeof(c->dst)); memcpy(r->iv, c->iv, sizeof(c->iv)); - bitmap_fill(r->data_out_bm, PARAM_NUM_REGS_DATA); - - r->status |= R_STATUS_OUTPUT_VALID_MASK; + r->data_out_rdy = true; } static void ot_aes_process(OtAESState *s) @@ -900,6 +899,15 @@ static void ot_aes_process(OtAESState *s) } } +static void ot_aes_commit_data_out(OtAESRegisters *r) +{ + if (r->data_out_rdy) { + bitmap_fill(r->data_out_bm, PARAM_NUM_REGS_DATA); + r->status |= R_STATUS_OUTPUT_VALID_MASK; + r->data_out_rdy = false; + } +} + static inline void ot_aes_do_process(OtAESState *s) { ot_aes_process(s); @@ -908,10 +916,18 @@ static inline void ot_aes_do_process(OtAESState *s) s->reseed_count -= 1u; } if (!s->reseed_count) { + /* + * delay availability of pushed data till completion of reseed + * otherwise, IDLE status may be false once the vCPU has read the data, + * which would not match the HW behavior + */ trace_ot_aes_reseed("reseed_count reached"); s->regs->trigger |= R_TRIGGER_PRNG_RESEED_MASK; ot_aes_trigger_reseed(s); ot_aes_load_reseed_rate(s); + } else { + /* flag pushed data as immediately available */ + ot_aes_commit_data_out(s->regs); } OtAESRegisters *r = s->regs; @@ -970,6 +986,13 @@ static void ot_aes_fill_entropy(void *opaque, uint32_t bits, bool fips) edn->scheduled = false; r->trigger &= ~R_TRIGGER_PRNG_RESEED_MASK; + /* + * if a previous AES data output generation had completed, flag the output + * as valid, as this state was delayed till entropy collection was + * completed to maintain a coherent IDLE state. + */ + ot_aes_commit_data_out(r); + ot_prng_reseed(s->prng, bits); ot_aes_handle_trigger(s); @@ -1272,6 +1295,7 @@ static void ot_aes_reset(DeviceState *dev) r->ctrl_aux_regwen = 1u; r->trigger = 0xeu; r->status = 0u; + r->data_out_rdy = false; e->scheduled = false; ot_aes_load_reseed_rate(s); From 5db08f151869db3160cb7d094281aef8e1266c12 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 26 Mar 2025 16:26:26 +0100 Subject: [PATCH 12/37] [ot] hw/opentitan: ot_csrng: rework FIPS compliance tracking Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index 926a3717c7b6e..79d1ed80307b3 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -303,7 +303,7 @@ typedef struct { unsigned rem_packet_count; /* remaining packets to generate */ bool instantiated; bool seeded; /* ready to generate randomness */ - bool fips; + bool no_fips; bool force_fips; } OtCSRNGDrng; @@ -669,7 +669,7 @@ static OtCSRNDCmdResult ot_csrng_drng_instantiate( } memset(drng->v_counter, 0, sizeof(drng->v_counter)); - drng->fips = false; + drng->no_fips = false; uint8_t key[OT_CSRNG_AES_KEY_SIZE]; memset(key, 0, sizeof(key)); @@ -711,7 +711,7 @@ static void ot_csrng_drng_uninstantiate(OtCSRNGInstance *inst) drng->instantiated = false; drng->seeded = false; - drng->fips = false; + drng->no_fips = false; drng->rem_packet_count = 0; /* only to help debugging */ @@ -821,10 +821,10 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) memcpy(drng->material, buffer, sizeof(entropy)); drng->material_len = sizeof(entropy) / (sizeof(uint32_t)); ot_csrng_drng_update(inst); - drng->fips = fips; + drng->no_fips |= !fips; } else { ot_csrng_drng_update(inst); - drng->fips = false; + drng->no_fips = true; } drng->reseed_counter = 0u; @@ -851,7 +851,7 @@ static void ot_csrng_drng_generate(OtCSRNGInstance *inst, uint32_t *out, xtrace_ot_csrng_show_buffer(ot_csrng_get_slot(inst), "out", out, OT_CSRNG_AES_BLOCK_SIZE); - *fips = drng->fips || drng->force_fips; + *fips = (!drng->no_fips) || drng->force_fips; if (!ot_csrng_drng_remaining_count(inst)) { ot_csrng_drng_update(inst); @@ -1698,7 +1698,7 @@ static uint32_t ot_csrng_read_state_db(OtCSRNGState *s) break; case 13u: /* Status + Compliance, only 8 LSBs matter */ val32 = (uint32_t)((((uint8_t)drng->instantiated) << 0u) | - (((uint8_t)drng->fips) << 1u)); + (((uint8_t)!drng->no_fips) << 1u)); break; default: val32 = 0; From ea472b7f77660e5bda7bcfeda56c9d6e3b3ee709 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:19:18 +0100 Subject: [PATCH 13/37] [ot] hw/opentitan: ot_edn: rework FIPS compliance tracking Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 27 +++++++++++++++++++-------- hw/opentitan/trace-events | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index 2d26dae939f43..d79374e70515b 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -605,7 +605,7 @@ static bool ot_edn_update_genbits_ready(OtEDNState *s) } static OtCSRNGCmdStatus -ot_edn_push_csrng_request(OtEDNState *s, uint32_t length) +ot_edn_push_csrng_request(OtEDNState *s, bool auto_mode, uint32_t length) { ot_edn_connect_csrng(s); @@ -614,7 +614,8 @@ ot_edn_push_csrng_request(OtEDNState *s, uint32_t length) OtCSRNGCmdStatus res = CSRNG_STATUS_INVALID_ACMD; for (unsigned cix = 0; cix < length; cix++) { - trace_ot_edn_push_csrng_command(c->appid, c->buffer[cix]); + trace_ot_edn_push_csrng_command(c->appid, auto_mode ? "auto" : "boot", + c->buffer[cix]); res = ot_csrng_push_command(c->device, c->appid, c->buffer[cix]); if (res != CSRNG_STATUS_SUCCESS) { trace_ot_edn_push_csrng_error(c->appid, (int)res); @@ -667,7 +668,7 @@ static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) } c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); - c->hw_cmd_status = ot_edn_push_csrng_request(s, 1u); + c->hw_cmd_status = ot_edn_push_csrng_request(s, false, 1u); if (c->hw_cmd_status) { return; } @@ -678,6 +679,7 @@ static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) */ switch (command) { case OT_CSRNG_CMD_INSTANTIATE: + c->no_fips = false; ot_edn_change_state(s, EDN_BOOT_INS_ACK_WAIT); break; case OT_CSRNG_CMD_GENERATE: @@ -729,10 +731,11 @@ static void ot_edn_send_auto_reseed_cmd(OtEDNState *s) ot_edn_change_state(s, EDN_AUTO_SEND_RESEED_CMD); c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); - c->hw_cmd_status = ot_edn_push_csrng_request(s, length); + c->hw_cmd_status = ot_edn_push_csrng_request(s, true, length); if (c->hw_cmd_status) { return; } + c->no_fips = false; ot_edn_change_state(s, EDN_AUTO_ACK_WAIT); } @@ -787,7 +790,7 @@ static void ot_edn_send_auto_generate_cmd(OtEDNState *s) } c->hw_cmd_type = (uint8_t)FIELD_EX32(command, OT_CSNRG_CMD, ACMD); - c->hw_cmd_status = ot_edn_push_csrng_request(s, length); + c->hw_cmd_status = ot_edn_push_csrng_request(s, true, length); if (c->hw_cmd_status) { return; } @@ -810,7 +813,7 @@ static void ot_edn_send_boot_uninstanciate_cmd(OtEDNState *s) FIELD_DP32(0, OT_CSNRG_CMD, ACMD, OT_CSRNG_CMD_UNINSTANTIATE); c->hw_cmd_type = (uint8_t)OT_CSRNG_CMD_UNINSTANTIATE; - c->hw_cmd_status = ot_edn_push_csrng_request(s, 1u); + c->hw_cmd_status = ot_edn_push_csrng_request(s, false, 1u); if (c->hw_cmd_status) { return; } @@ -998,7 +1001,7 @@ static void ot_edn_handle_sw_cmd_req(OtEDNState *s, uint32_t value) c->sw_ack = false; - trace_ot_edn_push_csrng_command(c->appid, value); + trace_ot_edn_push_csrng_command(c->appid, "sw", value); OtCSRNGCmdStatus res = ot_csrng_push_command(c->device, c->appid, value); if (res != CSRNG_STATUS_SUCCESS) { xtrace_ot_edn_error(c->appid, "CSRNG rejected command"); @@ -1019,7 +1022,12 @@ static void ot_edn_handle_sw_cmd_req(OtEDNState *s, uint32_t value) */ s->sw_cmd_ready = false; uint32_t command = FIELD_EX32(value, OT_CSNRG_CMD, ACMD); - if (command == OT_CSRNG_CMD_GENERATE) { + switch (command) { + case OT_CSRNG_CMD_INSTANTIATE: + case OT_CSRNG_CMD_RESEED: + c->no_fips = false; + break; + case OT_CSRNG_CMD_GENERATE: c->rem_packet_count = FIELD_EX32(value, OT_CSNRG_CMD, GLEN); xtrace_ot_edn_dinfo(c->appid, "SW generation w/ packets", c->rem_packet_count); @@ -1028,6 +1036,9 @@ static void ot_edn_handle_sw_cmd_req(OtEDNState *s, uint32_t value) epix++) { s->endpoints[epix].gen_count = 0; } + break; + default: + break; } } } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 4f33478f5dfd3..490673945d4c1 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -152,7 +152,7 @@ ot_edn_invalid_state(unsigned appid, const char *func, const char *state, int st ot_edn_io_read_out(unsigned appid, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "a#%u addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_edn_io_write(unsigned appid, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "a#%u addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_edn_irqs(unsigned appid, uint32_t active, uint32_t mask, uint32_t eff) "a#%u act:0x%08x msk:0x%08x eff:0x%08x" -ot_edn_push_csrng_command(unsigned appid, uint32_t value) "a#%u cmd:0x%08x" +ot_edn_push_csrng_command(unsigned appid, const char *mode, uint32_t value) "a#%u (%s) cmd:0x%08x" ot_edn_push_csrng_error(unsigned appid, int res) "a#%u res:%d" ot_edn_request_entropy(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_reset(unsigned appid) "a#%u" From 607104c3913579b3f68637824931c698d1a61f56 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:23:03 +0100 Subject: [PATCH 14/37] [ot] hw/opentitan: ot_otbn: reformat code Signed-off-by: Emmanuel Blot --- hw/opentitan/otbn/otbn/src/csrs.rs | 3 +-- hw/opentitan/otbn/otbn/src/proxy.rs | 22 +++++++++++++++------- hw/opentitan/otbn/otbn/src/random.rs | 5 +---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/hw/opentitan/otbn/otbn/src/csrs.rs b/hw/opentitan/otbn/otbn/src/csrs.rs index 3c932f06a915a..8593e66d21ee4 100644 --- a/hw/opentitan/otbn/otbn/src/csrs.rs +++ b/hw/opentitan/otbn/otbn/src/csrs.rs @@ -16,7 +16,6 @@ use super::random; use super::{CSR, WSR}; use crate::{ExceptionCause, CSRNG, PRNG}; - #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[repr(u32)] #[allow(non_camel_case_types)] @@ -406,7 +405,7 @@ impl CSRSet { key_s0_h: CSRWideGeneric::default(), key_s1_l: CSRWideGeneric::default(), key_s1_h: CSRWideGeneric::default(), - shared_flags: SharedFlags::default() + shared_flags: SharedFlags::default(), }; csrs.fg0.plug(&csrs.shared_flags, FlagMode::Fg0); csrs.fg1.plug(&csrs.shared_flags, FlagMode::Fg1); diff --git a/hw/opentitan/otbn/otbn/src/proxy.rs b/hw/opentitan/otbn/otbn/src/proxy.rs index 3e27359e0d7c4..14c8e5734dee8 100644 --- a/hw/opentitan/otbn/otbn/src/proxy.rs +++ b/hw/opentitan/otbn/otbn/src/proxy.rs @@ -97,8 +97,11 @@ impl Proxy { } /// Register a callback for requesting entropy from EDN - pub fn register_entropy_req_cb(&mut self, urnd_entropy_req: Box, - rnd_entropy_req: Box) { + pub fn register_entropy_req_cb( + &mut self, + urnd_entropy_req: Box, + rnd_entropy_req: Box, + ) { self.syncurnd.register_entropy_req_cb(urnd_entropy_req); self.rnd.register_entropy_req_cb(rnd_entropy_req); } @@ -382,13 +385,18 @@ impl comm::Callback for CCallback { #[no_mangle] pub extern "C" fn ot_otbn_proxy_new( - urnd_entropy_req: CCallbackFunc, urnd_opaque: CCallbackArg, - rnd_entropy_req: CCallbackFunc, rnd_opaque: CCallbackArg, - on_complete: CCallbackFunc, on_comp_opaque: CCallbackArg, + urnd_entropy_req: CCallbackFunc, + urnd_opaque: CCallbackArg, + rnd_entropy_req: CCallbackFunc, + rnd_opaque: CCallbackArg, + on_complete: CCallbackFunc, + on_comp_opaque: CCallbackArg, ) -> Box { let mut proxy = Box::new(Proxy::new()); - proxy.register_entropy_req_cb(Box::new(CCallback::new(urnd_entropy_req, urnd_opaque)), - Box::new(CCallback::new(rnd_entropy_req, rnd_opaque))); + proxy.register_entropy_req_cb( + Box::new(CCallback::new(urnd_entropy_req, urnd_opaque)), + Box::new(CCallback::new(rnd_entropy_req, rnd_opaque)), + ); proxy.register_signal_cb(Box::new(CCallback::new(on_complete, on_comp_opaque))); proxy } diff --git a/hw/opentitan/otbn/otbn/src/random.rs b/hw/opentitan/otbn/otbn/src/random.rs index e506e7d519be3..71c290ae842c9 100644 --- a/hw/opentitan/otbn/otbn/src/random.rs +++ b/hw/opentitan/otbn/otbn/src/random.rs @@ -16,7 +16,7 @@ pub struct RndCache { value: u256, available: bool, fips: bool, - repeat: bool + repeat: bool, } pub struct Rnd { @@ -117,7 +117,6 @@ impl CSRNG for Rnd { } } - #[derive(Default)] pub struct Urnd { xoshiro: Xoshiro256PlusPlus, @@ -147,7 +146,6 @@ impl Urnd { } } - pub struct SyncUrnd { urnd: Arc>, sync: Mutex, @@ -213,4 +211,3 @@ impl SyncUrnd { self.wait.notify_one(); } } - From 8ded7d96d637015d476ae076099a89625dde89d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 26 Mar 2025 16:27:15 +0100 Subject: [PATCH 15/37] [ot] hw/opentitan: ot_otbn: enable reporting errors without tracing execution. Tracing execution massively slows down OTBN execution. Add a new option to trace execution, while leaving logfile option to only report execution errors when trace logging is not enabled. Signed-off-by: Emmanuel Blot --- docs/opentitan/earlgrey.md | 8 +++- hw/opentitan/ot_otbn.c | 12 ++++-- hw/opentitan/otbn/otbn/src/otbn.rs | 61 +++++++++++++++------------ hw/opentitan/otbn/otbn/src/proxy.rs | 12 +++--- include/hw/opentitan/otbn/otbnproxy.h | 21 ++++----- 5 files changed, 65 insertions(+), 49 deletions(-) diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index efce8c868efbd..3dd1d3177d8a7 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -158,8 +158,12 @@ See [`tools.md`](tools.md) ### OTBN -* `-global ot-otbn.logfile=` dumps executed instructions on OTBN core into the specified - filename. Beware that is even further slows down execution speed, which could likely result into +* `-global ot-otbn.logfile=` output OTBN execution message to the specified logfile. When + _logasm_ option (see below) is not enabled, only execution termination and error messages are + logged. `stderr` can be used to log the messages to the standard error stream instead of a file. + +* `-global ot-otbn.logasm=` dumps executed instructions on OTBN core into the _logfile_ + filename. Beware that this further slows down execution speed, which could likely result in the guest application on the Ibex core to time out. ### OTP diff --git a/hw/opentitan/ot_otbn.c b/hw/opentitan/ot_otbn.c index cf699cfd2c624..da5f72fcc4d12 100644 --- a/hw/opentitan/ot_otbn.c +++ b/hw/opentitan/ot_otbn.c @@ -165,7 +165,8 @@ struct OtOTBNState { enum OtOTBNCommand last_cmd; OtOTBNRandom rnds[OT_OTBN_RND_COUNT]; - char *logfile; + char *log_file; + bool log_asm; }; static void ot_otbn_request_entropy(OtOTBNRandom *rnd); @@ -590,7 +591,8 @@ static Property ot_otbn_properties[] = { DEFINE_PROP_UINT8("edn-u-ep", OtOTBNState, rnds[OT_OTBN_URND].ep, UINT8_MAX), DEFINE_PROP_UINT8("edn-r-ep", OtOTBNState, rnds[OT_OTBN_RND].ep, UINT8_MAX), - DEFINE_PROP_STRING("logfile", OtOTBNState, logfile), + DEFINE_PROP_STRING("logfile", OtOTBNState, log_file), + DEFINE_PROP_BOOL("logasm", OtOTBNState, log_asm, false), DEFINE_PROP_END_OF_LIST(), }; @@ -645,7 +647,11 @@ static void ot_otbn_reset(DeviceState *dev) ot_fifo32_reset(&rnd->packer); } - ot_otbn_proxy_start(s->proxy, false, s->logfile); + if (!s->log_file) { + s->log_asm = false; + } + + ot_otbn_proxy_start(s->proxy, false, s->log_file, s->log_asm); } diff --git a/hw/opentitan/otbn/otbn/src/otbn.rs b/hw/opentitan/otbn/otbn/src/otbn.rs index 914afd25cd65a..5eb383450d819 100644 --- a/hw/opentitan/otbn/otbn/src/otbn.rs +++ b/hw/opentitan/otbn/otbn/src/otbn.rs @@ -110,6 +110,7 @@ pub struct Executer { syncurnd: Arc, on_complete: Option>, log_file: Option>, + log_asm: bool, } impl Executer { @@ -123,6 +124,7 @@ impl Executer { rnd: Arc, on_complete: Option>, log_name: Option, + log_asm: bool, ) -> Self { let log_file: Option>; if let Some(logname) = log_name { @@ -143,6 +145,7 @@ impl Executer { syncurnd, on_complete, log_file, + log_asm, } } @@ -156,6 +159,7 @@ impl Executer { rnd: Arc, on_complete: Option>, log_name: Option, + log_asm: bool, ) { Self::new( channel, @@ -166,6 +170,7 @@ impl Executer { rnd, on_complete, log_name, + log_asm, ) .enter(); } @@ -316,32 +321,34 @@ impl Executer { loop { // Debug/traces - if let Some(log_file) = &mut self.log_file { - // Output current instruction disassembly to log - if let Some(insn_bits) = executor.imem.read_mem(executor.hart_state.pc) { - let mut outputter = insn_disasm::InstructionStringOutputter { - insn_pc: executor.hart_state.pc, - }; - if let Some(inst) = insn_decode::decoder(&mut outputter, insn_bits) { - writeln!( - log_file, - "{:04x}: {:08x} {}", - executor.hart_state.pc, insn_bits, inst - ) - .expect("Log file write failed"); + if self.log_asm { + if let Some(log_file) = &mut self.log_file { + // Output current instruction disassembly to log + if let Some(insn_bits) = executor.imem.read_mem(executor.hart_state.pc) { + let mut outputter = insn_disasm::InstructionStringOutputter { + insn_pc: executor.hart_state.pc, + }; + if let Some(inst) = insn_decode::decoder(&mut outputter, insn_bits) { + writeln!( + log_file, + "{:04x}: {:08x} {}", + executor.hart_state.pc, insn_bits, inst + ) + .expect("Log file write failed"); + } else { + let base = (insn_bits >> 2) & 0b11111; + let funct3 = (insn_bits >> 12) & 0b111; + writeln!( + log_file, + "Unable to decode instruction @ {:x}: {:x} [{:03b}..{:05b}]", + executor.hart_state.pc, insn_bits, funct3, base + ) + .expect("Log file write failed"); + } } else { - let base = (insn_bits >> 2) & 0b11111; - let funct3 = (insn_bits >> 12) & 0b111; - writeln!( - log_file, - "Unable to decode instruction @ {:x}: {:x} [{:03b}..{:05b}]", - executor.hart_state.pc, insn_bits, funct3, base - ) - .expect("Log file write failed"); + writeln!(log_file, "Could not read PC {:08x}", executor.hart_state.pc) + .expect("Log file write failed"); } - } else { - writeln!(log_file, "Could not read PC {:08x}", executor.hart_state.pc) - .expect("Log file write failed"); } } @@ -405,8 +412,10 @@ impl Executer { self.registers.insn_count.fetch_add(1, Ordering::Relaxed); - if let Some(log_file) = &mut self.log_file { - Executer::log_changes(executor.hart_state, log_file); + if self.log_asm { + if let Some(log_file) = &mut self.log_file { + Executer::log_changes(executor.hart_state, log_file); + } } } } diff --git a/hw/opentitan/otbn/otbn/src/proxy.rs b/hw/opentitan/otbn/otbn/src/proxy.rs index 14c8e5734dee8..5789f39661f6c 100644 --- a/hw/opentitan/otbn/otbn/src/proxy.rs +++ b/hw/opentitan/otbn/otbn/src/proxy.rs @@ -112,7 +112,7 @@ impl Proxy { } /// Kick off the core executer, and create the communication channels - pub fn start(&mut self, test_mode: bool, log_name: Option<&str>) { + pub fn start(&mut self, test_mode: bool, log_name: Option<&str>, log_asm: bool) { if self.core_id.is_some() { // already started, only reset // hartstate is not reset, as it is done after each execution @@ -155,6 +155,7 @@ impl Proxy { rnd, on_complete, log_name, + log_asm, ) }) .unwrap(), @@ -406,14 +407,15 @@ pub extern "C" fn ot_otbn_proxy_new( pub unsafe extern "C" fn ot_otbn_proxy_start( proxy: Option<&mut Proxy>, test_mode: bool, - logname: *const c_char, + log_name: *const c_char, + log_asm: bool, ) { - let log_name: Option<&str> = if !logname.is_null() { - Some(CStr::from_ptr(logname).to_str().unwrap()) + let log_name: Option<&str> = if !log_name.is_null() { + Some(CStr::from_ptr(log_name).to_str().unwrap()) } else { None }; - proxy.unwrap().start(test_mode, log_name); + proxy.unwrap().start(test_mode, log_name, log_asm); } #[no_mangle] diff --git a/include/hw/opentitan/otbn/otbnproxy.h b/include/hw/opentitan/otbn/otbnproxy.h index 1d19513f944b3..b54e79bd3f1b6 100644 --- a/include/hw/opentitan/otbn/otbnproxy.h +++ b/include/hw/opentitan/otbn/otbnproxy.h @@ -51,11 +51,7 @@ enum OtOTBNStatus { OT_OTBN_STATUS_LOCKED = 0xFF, }; -enum OtOTBNRandomSource { - OT_OTBN_URND, - OT_OTBN_RND, - OT_OTBN_RND_COUNT -}; +enum OtOTBNRandomSource { OT_OTBN_URND, OT_OTBN_RND, OT_OTBN_RND_COUNT }; #define OT_OTBN_IMEM_SIZE (8U << 10U) #define OT_OTBN_DMEM_SIZE (3U << 10U) @@ -64,17 +60,16 @@ enum OtOTBNRandomSource { #define OT_OTBN_RANDOM_WORD_COUNT \ ((OT_OTBN_RANDOM_BIT_WIDTH) / (8u * sizeof(uint32_t))) -extern OTBNProxy ot_otbn_proxy_new(ot_otbn_fetch_entropy_fn urnd_req_entropy, - void *urnd_opaque, - ot_otbn_fetch_entropy_fn rnd_req_entropy, - void *rnd_opaque, - ot_otbn_signal_completion_fn signal, - void *on_comp_opaque); +extern OTBNProxy +ot_otbn_proxy_new(ot_otbn_fetch_entropy_fn urnd_req_entropy, void *urnd_opaque, + ot_otbn_fetch_entropy_fn rnd_req_entropy, void *rnd_opaque, + ot_otbn_signal_completion_fn signal, void *on_comp_opaque); extern void ot_otbn_proxy_start(OTBNProxy proxy, bool test_mode, - const char *logname); + const char *logname, bool log_asm); extern void ot_otbn_proxy_terminate(OTBNProxy proxy); extern int ot_otbn_proxy_push_entropy(OTBNProxy proxy, uint32_t rndix, - const uint8_t *seed, uint32_t len, bool fips); + const uint8_t *seed, uint32_t len, + bool fips); extern int ot_otbn_proxy_execute(OTBNProxy proxy, bool dumpstate); extern int ot_otbn_proxy_wipe_memory(OTBNProxy proxy, bool doi); extern bool ot_otbn_proxy_acknowledge_execution(OTBNProxy proxy); From 14da388423a5b001c18e09ecb95f8d0e32e3b61f Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:16:38 +0100 Subject: [PATCH 16/37] [ot] python/qemu: ot.pyot.executer: fix a bug when a single ROM is used. Signed-off-by: Emmanuel Blot --- python/qemu/ot/pyot/executer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/qemu/ot/pyot/executer.py b/python/qemu/ot/pyot/executer.py index 5a96c9c03eb30..dec35553e62d3 100644 --- a/python/qemu/ot/pyot/executer.py +++ b/python/qemu/ot/pyot/executer.py @@ -273,6 +273,8 @@ def _build_qemu_fw_args(self, args: Namespace) \ -> tuple[str, Optional[str], list[str], Optional[str]]: rom_exec = bool(args.rom_exec) roms = args.rom or [] + if isinstance(roms, str): + roms = [roms] multi_rom = (len(roms) + int(rom_exec)) > 1 # generate pre-application ROM option fw_args: list[str] = [] From 1252a03bdf87f9a6b1f1eb9f9e30e067074c3131 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 11:24:56 +0100 Subject: [PATCH 17/37] [ot] hw/riscv: ot_darjeeling: remove OT_SENSOR OT_SENSOR IP is no longer present on Darjeeling Signed-off-by: Emmanuel Blot --- hw/riscv/Kconfig | 1 - hw/riscv/ot_darjeeling.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index dc815d82f6648..ff715bc8b08f9 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -46,7 +46,6 @@ config OT_DARJEELING select OT_PWRMGR select OT_RSTMGR select OT_ROM_CTRL - select OT_SENSOR select OT_SOC_PROXY select OT_SPI_DEVICE select OT_SPI_HOST diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index db373bc7b0107..d8f99216c21e9 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -62,7 +62,6 @@ #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/opentitan/ot_rstmgr.h" -#include "hw/opentitan/ot_sensor.h" #include "hw/opentitan/ot_soc_proxy.h" #include "hw/opentitan/ot_spi_device.h" #include "hw/opentitan/ot_spi_host.h" @@ -1032,16 +1031,6 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ) ), }, - [OT_DJ_SOC_DEV_SENSOR_CTRL] = { - .type = TYPE_OT_SENSOR, - .memmap = MEMMAPENTRIES( - { .base = 0x30020000u } - ), - .gpio = IBEXGPIOCONNDEFS( - OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 81), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 82) - ) - }, [OT_DJ_SOC_DEV_I2C0] = { .type = TYPE_OT_I2C_DJ, .memmap = MEMMAPENTRIES( From 2bb93036da8680c9593aa963d7c82a3c1d663eda Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 11:23:34 +0100 Subject: [PATCH 18/37] [ot] hw/opentitan: ot_sensor: renamed as ot_sensor_eg OT Sensor IP is top-specific Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 2 +- hw/opentitan/meson.build | 2 +- hw/opentitan/{ot_sensor.c => ot_sensor_eg.c} | 76 +++++++++---------- hw/riscv/Kconfig | 2 +- hw/riscv/ot_earlgrey.c | 4 +- .../opentitan/{ot_sensor.h => ot_sensor_eg.h} | 14 ++-- 6 files changed, 50 insertions(+), 50 deletions(-) rename hw/opentitan/{ot_sensor.c => ot_sensor_eg.c} (82%) rename include/hw/opentitan/{ot_sensor.h => ot_sensor_eg.h} (79%) diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 68eee35232204..9133ba2a4d86d 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -138,7 +138,7 @@ config OT_ROM_CTRL config OT_RSTMGR bool -config OT_SENSOR +config OT_SENSOR_EG bool config OT_SOC_PROXY diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 7fffd4db4144f..6cebf39e835f5 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -45,7 +45,7 @@ system_ss.add(when: 'CONFIG_OT_PWRMGR', if_true: files('ot_pwrmgr.c')) system_ss.add(when: 'CONFIG_OT_RANDOM_SRC', if_true: files('ot_random_src.c')) system_ss.add(when: 'CONFIG_OT_ROM_CTRL', if_true: files('ot_rom_ctrl.c', 'ot_rom_ctrl_img.c')) system_ss.add(when: 'CONFIG_OT_RSTMGR', if_true: files('ot_rstmgr.c')) -system_ss.add(when: 'CONFIG_OT_SENSOR', if_true: files('ot_sensor.c')) +system_ss.add(when: 'CONFIG_OT_SENSOR_EG', if_true: files('ot_sensor_eg.c')) system_ss.add(when: 'CONFIG_OT_SOC_PROXY', if_true: files('ot_soc_proxy.c')) system_ss.add(when: 'CONFIG_OT_SOCDBG_CTRL', if_true: files('ot_socdbg_ctrl.c')) system_ss.add(when: 'CONFIG_OT_SPI_DEVICE', if_true: files('ot_spi_device.c')) diff --git a/hw/opentitan/ot_sensor.c b/hw/opentitan/ot_sensor_eg.c similarity index 82% rename from hw/opentitan/ot_sensor.c rename to hw/opentitan/ot_sensor_eg.c index e56e973f4661e..0ba8e8a204d1c 100644 --- a/hw/opentitan/ot_sensor.c +++ b/hw/opentitan/ot_sensor_eg.c @@ -1,7 +1,7 @@ /* - * QEMU OpenTitan Sensor controller device + * QEMU OpenTitan Sensor controller device for EarlGrey * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -32,7 +32,7 @@ #include "qemu/log.h" #include "qemu/typedefs.h" #include "hw/opentitan/ot_alert.h" -#include "hw/opentitan/ot_sensor.h" +#include "hw/opentitan/ot_sensor_eg.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -131,7 +131,7 @@ static const char *REG_NAMES[REGS_COUNT] = { }; #undef REG_NAME_ENTRY -struct OtSensorState { +struct OtSensorEgState { SysBusDevice parent_obj; MemoryRegion mmio; @@ -141,7 +141,7 @@ struct OtSensorState { uint32_t *regs; }; -static void ot_sensor_update_irqs(OtSensorState *s) +static void ot_sensor_eg_update_irqs(OtSensorEgState *s) { uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -151,7 +151,7 @@ static void ot_sensor_update_irqs(OtSensorState *s) } } -static void ot_sensor_update_alerts(OtSensorState *s) +static void ot_sensor_eg_update_alerts(OtSensorEgState *s) { uint32_t level = s->regs[R_ALERT_TEST]; @@ -160,9 +160,9 @@ static void ot_sensor_update_alerts(OtSensorState *s) } } -static uint64_t ot_sensor_regs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ot_sensor_eg_regs_read(void *opaque, hwaddr addr, unsigned size) { - OtSensorState *s = opaque; + OtSensorEgState *s = opaque; (void)size; uint32_t val32; @@ -205,10 +205,10 @@ static uint64_t ot_sensor_regs_read(void *opaque, hwaddr addr, unsigned size) return (uint64_t)val32; }; -static void ot_sensor_regs_write(void *opaque, hwaddr addr, uint64_t val64, - unsigned size) +static void ot_sensor_eg_regs_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) { - OtSensorState *s = opaque; + OtSensorEgState *s = opaque; (void)size; uint32_t val32 = (uint32_t)val64; @@ -221,22 +221,22 @@ static void ot_sensor_regs_write(void *opaque, hwaddr addr, uint64_t val64, case R_INTR_STATE: val32 &= INTR_MASK; s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ - ot_sensor_update_irqs(s); + ot_sensor_eg_update_irqs(s); break; case R_INTR_ENABLE: val32 &= INTR_MASK; s->regs[R_INTR_ENABLE] = val32; - ot_sensor_update_irqs(s); + ot_sensor_eg_update_irqs(s); break; case R_INTR_TEST: val32 &= INTR_MASK; s->regs[R_INTR_STATE] |= val32; - ot_sensor_update_irqs(s); + ot_sensor_eg_update_irqs(s); break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; s->regs[reg] = val32; - ot_sensor_update_alerts(s); + ot_sensor_eg_update_alerts(s); break; case R_CFG_REGWEN: case R_ALERT_TRIG: @@ -259,36 +259,36 @@ static void ot_sensor_regs_write(void *opaque, hwaddr addr, uint64_t val64, } }; -static Property ot_sensor_properties[] = { +static Property ot_sensor_eg_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static const MemoryRegionOps ot_sensor_regs_ops = { - .read = &ot_sensor_regs_read, - .write = &ot_sensor_regs_write, +static const MemoryRegionOps ot_sensor_eg_regs_ops = { + .read = &ot_sensor_eg_regs_read, + .write = &ot_sensor_eg_regs_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4u, .impl.max_access_size = 4u, }; -static void ot_sensor_reset(DeviceState *dev) +static void ot_sensor_eg_reset(DeviceState *dev) { - OtSensorState *s = OT_SENSOR(dev); + OtSensorEgState *s = OT_SENSOR_EG(dev); memset(s->regs, 0, REGS_SIZE); s->regs[R_CFG_REGWEN] = 0x1u; - ot_sensor_update_irqs(s); - ot_sensor_update_alerts(s); + ot_sensor_eg_update_irqs(s); + ot_sensor_eg_update_alerts(s); } -static void ot_sensor_init(Object *obj) +static void ot_sensor_eg_init(Object *obj) { - OtSensorState *s = OT_SENSOR(obj); + OtSensorEgState *s = OT_SENSOR_EG(obj); - memory_region_init_io(&s->mmio, obj, &ot_sensor_regs_ops, s, TYPE_OT_SENSOR, - REGS_SIZE); + memory_region_init_io(&s->mmio, obj, &ot_sensor_eg_regs_ops, s, + TYPE_OT_SENSOR_EG, REGS_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); s->regs = g_new0(uint32_t, REGS_COUNT); @@ -300,27 +300,27 @@ static void ot_sensor_init(Object *obj) } } -static void ot_sensor_class_init(ObjectClass *klass, void *data) +static void ot_sensor_eg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_sensor_reset); - device_class_set_props(dc, ot_sensor_properties); + device_class_set_legacy_reset(dc, &ot_sensor_eg_reset); + device_class_set_props(dc, ot_sensor_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } -static const TypeInfo ot_sensor_info = { - .name = TYPE_OT_SENSOR, +static const TypeInfo ot_sensor_eg_info = { + .name = TYPE_OT_SENSOR_EG, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OtSensorState), - .instance_init = &ot_sensor_init, - .class_init = &ot_sensor_class_init, + .instance_size = sizeof(OtSensorEgState), + .instance_init = &ot_sensor_eg_init, + .class_init = &ot_sensor_eg_class_init, }; -static void ot_sensor_register_types(void) +static void ot_sensor_eg_register_types(void) { - type_register_static(&ot_sensor_info); + type_register_static(&ot_sensor_eg_info); } -type_init(ot_sensor_register_types); +type_init(ot_sensor_eg_register_types); diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index ff715bc8b08f9..8d369e6298305 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -86,7 +86,7 @@ config OT_EARLGREY select OT_PWRMGR select OT_ROM_CTRL select OT_RSTMGR - select OT_SENSOR + select OT_SENSOR_EG select OT_SPI_DEVICE select OT_SPI_HOST select OT_SRAM_CTRL diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 6442b97073d21..8faba62f75422 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -60,7 +60,7 @@ #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/opentitan/ot_rstmgr.h" -#include "hw/opentitan/ot_sensor.h" +#include "hw/opentitan/ot_sensor_eg.h" #include "hw/opentitan/ot_spi_device.h" #include "hw/opentitan/ot_spi_host.h" #include "hw/opentitan/ot_sram_ctrl.h" @@ -876,7 +876,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { ), }, [OT_EG_SOC_DEV_SENSOR_CTRL] = { - .type = TYPE_OT_SENSOR, + .type = TYPE_OT_SENSOR_EG, .memmap = MEMMAPENTRIES( { .base = 0x40490000u } ), diff --git a/include/hw/opentitan/ot_sensor.h b/include/hw/opentitan/ot_sensor_eg.h similarity index 79% rename from include/hw/opentitan/ot_sensor.h rename to include/hw/opentitan/ot_sensor_eg.h index b8bd230868f95..b57895c0ecb90 100644 --- a/include/hw/opentitan/ot_sensor.h +++ b/include/hw/opentitan/ot_sensor_eg.h @@ -1,7 +1,7 @@ /* - * QEMU OpenTitan Sensor controller device + * QEMU OpenTitan Sensor controller device for EarlGrey * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -25,12 +25,12 @@ * THE SOFTWARE. */ -#ifndef HW_OPENTITAN_OT_SENSOR_H -#define HW_OPENTITAN_OT_SENSOR_H +#ifndef HW_OPENTITAN_OT_SENSOR_EG_H +#define HW_OPENTITAN_OT_SENSOR_EG_H #include "qom/object.h" -#define TYPE_OT_SENSOR "ot-sensor" -OBJECT_DECLARE_SIMPLE_TYPE(OtSensorState, OT_SENSOR) +#define TYPE_OT_SENSOR_EG "ot-sensor-eg" +OBJECT_DECLARE_SIMPLE_TYPE(OtSensorEgState, OT_SENSOR_EG) -#endif /* HW_OPENTITAN_OT_SENSOR_H */ +#endif /* HW_OPENTITAN_OT_SENSOR_EG_H */ From c18ae563b2acc358e860fe28e7afe7287c6216ee Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:07:44 +0100 Subject: [PATCH 19/37] [ot] hw/opentitan: ot_sensor_eg: update register definitions Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_sensor_eg.c | 152 +++++++++++++++++++++++++++++++++--- 1 file changed, 139 insertions(+), 13 deletions(-) diff --git a/hw/opentitan/ot_sensor_eg.c b/hw/opentitan/ot_sensor_eg.c index 0ba8e8a204d1c..5332c3859b5e6 100644 --- a/hw/opentitan/ot_sensor_eg.c +++ b/hw/opentitan/ot_sensor_eg.c @@ -40,7 +40,10 @@ #include "hw/sysbus.h" #include "trace.h" -#define NUM_ALERTS 2u +#define NUM_ALERTS 2u +#define NUM_IO_RAILS 2u + +#define NUM_ALERT_SENSOR_COUNT 11u /* clang-format off */ REG32(INTR_STATE, 0x0u) @@ -65,7 +68,19 @@ REG32(ALERT_TRIG, 0x14u) FIELD(ALERT_TRIG, VAL_8, 8u, 1u) FIELD(ALERT_TRIG, VAL_9, 9u, 1u) FIELD(ALERT_TRIG, VAL_10, 10u, 1u) -REG32(FATAL_ALERT_EN, 0x18u) +REG32(ALERT_EN_0, 0x18u) + SHARED_FIELD(ALERT_EN_VAL, 0u, 4u) +REG32(ALERT_EN_1, 0x1cu) +REG32(ALERT_EN_2, 0x20u) +REG32(ALERT_EN_3, 0x24u) +REG32(ALERT_EN_4, 0x28u) +REG32(ALERT_EN_5, 0x2cu) +REG32(ALERT_EN_6, 0x30u) +REG32(ALERT_EN_7, 0x34u) +REG32(ALERT_EN_8, 0x38u) +REG32(ALERT_EN_9, 0x3cu) +REG32(ALERT_EN_10, 0x40u) +REG32(FATAL_ALERT_EN, 0x44u) FIELD(FATAL_ALERT_EN, VAL_0, 0u, 1u) FIELD(FATAL_ALERT_EN, VAL_1, 1u, 1u) FIELD(FATAL_ALERT_EN, VAL_2, 2u, 1u) @@ -77,7 +92,7 @@ REG32(FATAL_ALERT_EN, 0x18u) FIELD(FATAL_ALERT_EN, VAL_8, 8u, 1u) FIELD(FATAL_ALERT_EN, VAL_9, 9u, 1u) FIELD(FATAL_ALERT_EN, VAL_10, 10u, 1u) -REG32(RECOV_ALERT, 0x1cu) +REG32(RECOV_ALERT, 0x48u) FIELD(RECOV_ALERT, VAL_0, 0u, 1u) FIELD(RECOV_ALERT, VAL_1, 1u, 1u) FIELD(RECOV_ALERT, VAL_2, 2u, 1u) @@ -89,7 +104,7 @@ REG32(RECOV_ALERT, 0x1cu) FIELD(RECOV_ALERT, VAL_8, 8u, 1u) FIELD(RECOV_ALERT, VAL_9, 9u, 1u) FIELD(RECOV_ALERT, VAL_10, 10u, 1u) -REG32(FATAL_ALERT, 0x20u) +REG32(FATAL_ALERT, 0x4cu) FIELD(FATAL_ALERT, VAL_0, 0u, 1u) FIELD(FATAL_ALERT, VAL_1, 1u, 1u) FIELD(FATAL_ALERT, VAL_2, 2u, 1u) @@ -102,33 +117,73 @@ REG32(FATAL_ALERT, 0x20u) FIELD(FATAL_ALERT, VAL_9, 9u, 1u) FIELD(FATAL_ALERT, VAL_10, 10u, 1u) FIELD(FATAL_ALERT, VAL_11, 11u, 1u) -REG32(STATUS, 0x24u) +REG32(STATUS, 0x50u) FIELD(STATUS, AST_INIT_DONE, 0u, 1u) FIELD(STATUS, IO_POK, 1u, 2u) +REG32(MANUAL_PAD_ATTR_REGWEN_0, 0x54u) + SHARED_FIELD(MANUAL_PAD_ATTR_REGWEN_EN, 0u, 1u) +REG32(MANUAL_PAD_ATTR_REGWEN_1, 0x58u) +REG32(MANUAL_PAD_ATTR_REGWEN_2, 0x5cu) +REG32(MANUAL_PAD_ATTR_REGWEN_3, 0x60u) +REG32(MANUAL_PAD_ATTR_0, 0x64u) + SHARED_FIELD(MANUAL_PAD_ATTR_PULL_EN, 2u, 1u) + SHARED_FIELD(MANUAL_PAD_ATTR_PULL_SELECT, 3u, 1u) + SHARED_FIELD(MANUAL_PAD_ATTR_INPUT_DISABLE, 7u, 1u) +REG32(MANUAL_PAD_ATTR_1, 0x68u) +REG32(MANUAL_PAD_ATTR_2, 0x6cu) +REG32(MANUAL_PAD_ATTR_3, 0x70u) /* clang-format on */ -#define PARAM_NUM_IO_RAILS 2 - #define INTR_MASK (INTR_IO_STATUS_CHANGE_MASK | INTR_INIT_STATUS_CHANGE_MASK) #define ALERT_TEST_MASK \ (R_ALERT_TEST_RECOV_ALERT_MASK | R_ALERT_TEST_FATAL_ALERT_MASK) +#define ALERT_SENSOR_MASK ((1u << NUM_ALERT_SENSOR_COUNT) - 1u) +#define MANUAL_PAD_ATTR_MASK \ + (MANUAL_PAD_ATTR_PULL_EN_MASK | MANUAL_PAD_ATTR_PULL_SELECT_MASK | \ + MANUAL_PAD_ATTR_INPUT_DISABLE_MASK) #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) -#define R_LAST_REG (R_STATUS) +#define R_LAST_REG (R_MANUAL_PAD_ATTR_3) #define REGS_COUNT (R_LAST_REG + 1u) #define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) +/* clang-format off */ static const char *REG_NAMES[REGS_COUNT] = { - REG_NAME_ENTRY(INTR_STATE), REG_NAME_ENTRY(INTR_ENABLE), - REG_NAME_ENTRY(INTR_TEST), REG_NAME_ENTRY(ALERT_TEST), - REG_NAME_ENTRY(CFG_REGWEN), REG_NAME_ENTRY(ALERT_TRIG), - REG_NAME_ENTRY(FATAL_ALERT_EN), REG_NAME_ENTRY(RECOV_ALERT), - REG_NAME_ENTRY(FATAL_ALERT), REG_NAME_ENTRY(STATUS), + REG_NAME_ENTRY(INTR_STATE), + REG_NAME_ENTRY(INTR_ENABLE), + REG_NAME_ENTRY(INTR_TEST), + REG_NAME_ENTRY(ALERT_TEST), + REG_NAME_ENTRY(CFG_REGWEN), + REG_NAME_ENTRY(ALERT_TRIG), + REG_NAME_ENTRY(ALERT_EN_0), + REG_NAME_ENTRY(ALERT_EN_1), + REG_NAME_ENTRY(ALERT_EN_2), + REG_NAME_ENTRY(ALERT_EN_3), + REG_NAME_ENTRY(ALERT_EN_4), + REG_NAME_ENTRY(ALERT_EN_5), + REG_NAME_ENTRY(ALERT_EN_6), + REG_NAME_ENTRY(ALERT_EN_7), + REG_NAME_ENTRY(ALERT_EN_8), + REG_NAME_ENTRY(ALERT_EN_9), + REG_NAME_ENTRY(ALERT_EN_10), + REG_NAME_ENTRY(FATAL_ALERT_EN), + REG_NAME_ENTRY(RECOV_ALERT), + REG_NAME_ENTRY(FATAL_ALERT), + REG_NAME_ENTRY(STATUS), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_REGWEN_0), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_REGWEN_1), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_REGWEN_2), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_REGWEN_3), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_0), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_1), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_2), + REG_NAME_ENTRY(MANUAL_PAD_ATTR_3), }; +/* clang-format on */ #undef REG_NAME_ENTRY struct OtSensorEgState { @@ -173,9 +228,12 @@ static uint64_t ot_sensor_eg_regs_read(void *opaque, hwaddr addr, unsigned size) case R_INTR_ENABLE: case R_CFG_REGWEN: case R_ALERT_TRIG: + case R_ALERT_EN_0 ... R_ALERT_EN_10: case R_FATAL_ALERT_EN: case R_RECOV_ALERT: case R_FATAL_ALERT: + case R_MANUAL_PAD_ATTR_REGWEN_0 ... R_MANUAL_PAD_ATTR_REGWEN_3: + case R_MANUAL_PAD_ATTR_0 ... R_MANUAL_PAD_ATTR_3: val32 = s->regs[reg]; break; case R_STATUS: @@ -237,12 +295,56 @@ static void ot_sensor_eg_regs_write(void *opaque, hwaddr addr, uint64_t val64, val32 &= ALERT_TEST_MASK; s->regs[reg] = val32; ot_sensor_eg_update_alerts(s); + s->regs[reg] = 0u; + ot_sensor_eg_update_alerts(s); break; case R_CFG_REGWEN: + val32 &= R_CFG_REGWEN_EN_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; case R_ALERT_TRIG: + val32 &= ALERT_SENSOR_MASK; + s->regs[reg] = val32; + qemu_log_mask(LOG_UNIMP, + "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", + addr, REG_NAME(reg)); + break; + case R_ALERT_EN_0 ... R_ALERT_EN_10: + if (!s->regs[R_CFG_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot change %s, CFG_REGWEN disabled", + REG_NAME(reg)); + break; + } + val32 &= ALERT_EN_VAL_MASK; + s->regs[reg] = val32; + qemu_log_mask(LOG_UNIMP, + "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", + addr, REG_NAME(reg)); + break; case R_FATAL_ALERT_EN: + if (!s->regs[R_CFG_REGWEN]) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot change %s, CFG_REGWEN disabled", + REG_NAME(reg)); + break; + } + val32 &= ALERT_SENSOR_MASK; + s->regs[reg] = val32; + qemu_log_mask(LOG_UNIMP, + "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", + addr, REG_NAME(reg)); + break; case R_RECOV_ALERT: + val32 &= ALERT_SENSOR_MASK; + s->regs[reg] = val32; + qemu_log_mask(LOG_UNIMP, + "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", + addr, REG_NAME(reg)); + break; case R_FATAL_ALERT: + val32 &= ALERT_SENSOR_MASK; + s->regs[reg] = val32; qemu_log_mask(LOG_UNIMP, "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", addr, REG_NAME(reg)); @@ -252,6 +354,23 @@ static void ot_sensor_eg_regs_write(void *opaque, hwaddr addr, uint64_t val64, "%s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, addr, REG_NAME(reg)); break; + case R_MANUAL_PAD_ATTR_REGWEN_0 ... R_MANUAL_PAD_ATTR_REGWEN_3: + val32 &= MANUAL_PAD_ATTR_REGWEN_EN_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_MANUAL_PAD_ATTR_0 ... R_MANUAL_PAD_ATTR_3: + if (!s->regs[reg - R_MANUAL_PAD_ATTR_0 + R_MANUAL_PAD_ATTR_REGWEN_0]) { + qemu_log_mask(LOG_GUEST_ERROR, "Cannot change %s, %s disabled", + REG_NAME(reg), + REG_NAME(reg - R_MANUAL_PAD_ATTR_0 + + R_MANUAL_PAD_ATTR_REGWEN_0)); + } + break; + val32 &= MANUAL_PAD_ATTR_MASK; + qemu_log_mask(LOG_UNIMP, + "Unimplemented register 0x%02" HWADDR_PRIx " (%s)\n", + addr, REG_NAME(reg)); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -278,6 +397,13 @@ static void ot_sensor_eg_reset(DeviceState *dev) memset(s->regs, 0, REGS_SIZE); s->regs[R_CFG_REGWEN] = 0x1u; + for (unsigned rix = R_ALERT_EN_0; rix <= R_ALERT_EN_10; rix++) { + s->regs[rix] = 0x6u; + } + for (unsigned rix = R_MANUAL_PAD_ATTR_REGWEN_0; + rix <= R_MANUAL_PAD_ATTR_REGWEN_3; rix++) { + s->regs[rix] = 0x1u; + } ot_sensor_eg_update_irqs(s); ot_sensor_eg_update_alerts(s); From e0f018b7e0c94034de3b3d2894070805fc01c7bd Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 26 Mar 2025 16:26:13 +0100 Subject: [PATCH 20/37] [ot] hw/opentitan: ot_entropy_src: improve trace messages Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 115 ++++++++++++++++++---------------- hw/opentitan/trace-events | 2 +- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index a3c5f445f765e..f738171bd05dd 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -430,29 +430,29 @@ static const uint16_t OtEDNFsmStateCode[] = { [ENTROPY_SRC_ERROR] = 0b100111101, }; -#define STATE_NAME_ENTRY(_st_) [_st_] = stringify(_st_) +#define STATE_NAME_ENTRY(_st_) [ENTROPY_SRC_##_st_] = stringify(_st_) static const char *STATE_NAMES[] = { - STATE_NAME_ENTRY(ENTROPY_SRC_IDLE), - STATE_NAME_ENTRY(ENTROPY_SRC_BOOT_HT_RUNNING), - STATE_NAME_ENTRY(ENTROPY_SRC_BOOT_POST_HT_CHK), - STATE_NAME_ENTRY(ENTROPY_SRC_BOOT_PHASE_DONE), - STATE_NAME_ENTRY(ENTROPY_SRC_STARTUP_HT_START), - STATE_NAME_ENTRY(ENTROPY_SRC_STARTUP_PHASE1), - STATE_NAME_ENTRY(ENTROPY_SRC_STARTUP_PASS1), - STATE_NAME_ENTRY(ENTROPY_SRC_STARTUP_FAIL1), - STATE_NAME_ENTRY(ENTROPY_SRC_CONT_HT_START), - STATE_NAME_ENTRY(ENTROPY_SRC_CONT_HT_RUNNING), - STATE_NAME_ENTRY(ENTROPY_SRC_FW_INSERT_START), - STATE_NAME_ENTRY(ENTROPY_SRC_FW_INSERT_MSG), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_MSGDONE), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_PREP), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_PROCESS), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_VALID), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_DONE), - STATE_NAME_ENTRY(ENTROPY_SRC_SHA3_QUIESCE), - STATE_NAME_ENTRY(ENTROPY_SRC_ALERT_STATE), - STATE_NAME_ENTRY(ENTROPY_SRC_ALERT_HANG), - STATE_NAME_ENTRY(ENTROPY_SRC_ERROR), + STATE_NAME_ENTRY(IDLE), + STATE_NAME_ENTRY(BOOT_HT_RUNNING), + STATE_NAME_ENTRY(BOOT_POST_HT_CHK), + STATE_NAME_ENTRY(BOOT_PHASE_DONE), + STATE_NAME_ENTRY(STARTUP_HT_START), + STATE_NAME_ENTRY(STARTUP_PHASE1), + STATE_NAME_ENTRY(STARTUP_PASS1), + STATE_NAME_ENTRY(STARTUP_FAIL1), + STATE_NAME_ENTRY(CONT_HT_START), + STATE_NAME_ENTRY(CONT_HT_RUNNING), + STATE_NAME_ENTRY(FW_INSERT_START), + STATE_NAME_ENTRY(FW_INSERT_MSG), + STATE_NAME_ENTRY(SHA3_MSGDONE), + STATE_NAME_ENTRY(SHA3_PREP), + STATE_NAME_ENTRY(SHA3_PROCESS), + STATE_NAME_ENTRY(SHA3_VALID), + STATE_NAME_ENTRY(SHA3_DONE), + STATE_NAME_ENTRY(SHA3_QUIESCE), + STATE_NAME_ENTRY(ALERT_STATE), + STATE_NAME_ENTRY(ALERT_HANG), + STATE_NAME_ENTRY(ERROR), }; #undef STATE_NAME_ENTRY #define STATE_NAME(_st_) \ @@ -466,9 +466,10 @@ static const char *STATE_NAMES[] = { #define xtrace_ot_entropy_src_show_buffer(_msg_, _buf_, _len_) \ ot_entropy_src_show_buffer(__func__, __LINE__, _msg_, _buf_, _len_) -static bool ot_entropy_src_is_module_enabled(OtEntropySrcState *s); -static bool ot_entropy_src_is_hw_route(OtEntropySrcState *s); -static bool ot_entropy_src_is_fips_capable(OtEntropySrcState *s); +static bool ot_entropy_src_is_module_enabled(const OtEntropySrcState *s); +static bool ot_entropy_src_is_fips_enabled(const OtEntropySrcState *s); +static bool ot_entropy_src_is_hw_route(const OtEntropySrcState *s); +static bool ot_entropy_src_is_fips_capable(const OtEntropySrcState *s); static void ot_entropy_src_reset(DeviceState *dev); static void ot_entropy_src_update_alerts(OtEntropySrcState *s); static void ot_entropy_src_update_filler(OtEntropySrcState *s); @@ -571,8 +572,17 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, randu32[pos++] = ot_fifo32_pop(&s->final_fifo); } + bool fips_capable = ot_entropy_src_is_fips_capable(s); + /* note: fips compliancy is only simulated here for now */ - *fips = fips_compliant && ot_entropy_src_is_fips_capable(s); + *fips = fips_compliant && fips_capable; + + trace_ot_entropy_src_get_random_fips( + STATE_NAME(s->state), ot_entropy_src_is_fips_enabled(s), + REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_ROUTE), + REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_TYPE), + REG_MB4_IS_FALSE(s, CONF, RNG_BIT_ENABLE), fips_capable, fips_compliant, + *fips); if (ot_fifo32_num_used(&s->final_fifo) < ES_WORD_COUNT) { ot_entropy_src_update_filler(s); @@ -613,17 +623,17 @@ static void ot_entropy_src_show_buffer( } } -static bool ot_entropy_src_is_module_enabled(OtEntropySrcState *s) +static bool ot_entropy_src_is_module_enabled(const OtEntropySrcState *s) { return REG_MB4_IS_TRUE(s, MODULE_ENABLE, MODULE_ENABLE); } -static bool ot_entropy_src_is_module_disabled(OtEntropySrcState *s) +static bool ot_entropy_src_is_module_disabled(const OtEntropySrcState *s) { return REG_MB4_IS_FALSE(s, MODULE_ENABLE, MODULE_ENABLE); } -static bool ot_entropy_src_is_fips_enabled(OtEntropySrcState *s) +static bool ot_entropy_src_is_fips_enabled(const OtEntropySrcState *s) { return REG_MB4_IS_TRUE(s, CONF, FIPS_ENABLE); } @@ -636,54 +646,48 @@ static void ot_entropy_src_update_irqs(OtEntropySrcState *s) } } -static bool ot_entropy_src_is_final_fifo_slot_available(OtEntropySrcState *s) +static bool +ot_entropy_src_is_final_fifo_slot_available(const OtEntropySrcState *s) { return ot_fifo32_num_free(&s->final_fifo) >= ES_WORD_COUNT; } -static bool ot_entropy_src_is_hw_route(OtEntropySrcState *s) +static bool ot_entropy_src_is_hw_route(const OtEntropySrcState *s) { return REG_MB4_IS_FALSE(s, ENTROPY_CONTROL, ES_ROUTE); } -static bool ot_entropy_src_is_fw_route(OtEntropySrcState *s) +static bool ot_entropy_src_is_fw_route(const OtEntropySrcState *s) { return REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_ROUTE); } -static bool ot_entropy_src_is_bypass_mode(OtEntropySrcState *s) +static bool ot_entropy_src_is_bypass_mode(const OtEntropySrcState *s) { return !ot_entropy_src_is_fips_enabled(s) || (ot_entropy_src_is_fw_route(s) && REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_TYPE)); } -static bool ot_entropy_src_is_fw_ov_mode(OtEntropySrcState *s) +static bool ot_entropy_src_is_fw_ov_mode(const OtEntropySrcState *s) { return REG_MB4_IS_TRUE(s, FW_OV_CONTROL, FW_OV_MODE); } -static bool ot_entropy_src_is_fw_ov_entropy_insert(OtEntropySrcState *s) +static bool ot_entropy_src_is_fw_ov_entropy_insert(const OtEntropySrcState *s) { return REG_MB4_IS_TRUE(s, FW_OV_CONTROL, FW_OV_ENTROPY_INSERT); } -static bool ot_entropy_src_is_fips_capable(OtEntropySrcState *s) +static bool ot_entropy_src_is_fips_capable(const OtEntropySrcState *s) { - bool fips_capable = - ot_entropy_src_is_fips_enabled(s) && - !(REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_ROUTE) && - REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_TYPE)) && - REG_MB4_IS_FALSE(s, CONF, RNG_BIT_ENABLE); - trace_ot_entropy_src_is_fips_capable( - ot_entropy_src_is_fips_enabled(s), - REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_ROUTE), - REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_TYPE), - REG_MB4_IS_FALSE(s, CONF, RNG_BIT_ENABLE), fips_capable); - return fips_capable; + return ot_entropy_src_is_fips_enabled(s) && + !(REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_ROUTE) && + REG_MB4_IS_TRUE(s, ENTROPY_CONTROL, ES_TYPE)) && + REG_MB4_IS_FALSE(s, CONF, RNG_BIT_ENABLE); } -static unsigned ot_alert_get_alert_fail_count(OtEntropySrcState *s) +static unsigned ot_alert_get_alert_fail_count(const OtEntropySrcState *s) { unsigned count; @@ -725,8 +729,11 @@ static void ot_entropy_src_change_state_line( break; } - trace_ot_entropy_src_change_state(line, STATE_NAME(old_state), old_state, - STATE_NAME(s->state), s->state); + if (old_state != s->state) { + trace_ot_entropy_src_change_state(line, STATE_NAME(old_state), + old_state, STATE_NAME(s->state), + s->state); + } if (s->state == ENTROPY_SRC_ERROR) { s->regs[R_ERR_CODE] |= R_ERR_CODE_ES_MAIN_SM_ERR_MASK; @@ -774,7 +781,7 @@ static bool ot_entropy_src_check_multibitboot( return false; } -static bool ot_entropy_src_can_consume_entropy(OtEntropySrcState *s) +static bool ot_entropy_src_can_consume_entropy(const OtEntropySrcState *s) { return ot_entropy_src_is_module_enabled(s) && !(ot_entropy_src_is_fw_ov_entropy_insert(s) && @@ -811,7 +818,7 @@ static void ot_entropy_src_update_filler(OtEntropySrcState *s) } } -static bool ot_entropy_src_can_condition_entropy(OtEntropySrcState *s) +static bool ot_entropy_src_can_condition_entropy(const OtEntropySrcState *s) { if (!ot_fifo32_is_full(&s->precon_fifo)) { /* room in preconditioner packer */ @@ -825,7 +832,7 @@ static bool ot_entropy_src_can_condition_entropy(OtEntropySrcState *s) return false; } -static bool ot_entropy_src_can_bypass_entropy(OtEntropySrcState *s) +static bool ot_entropy_src_can_bypass_entropy(const OtEntropySrcState *s) { if (!ot_fifo32_is_full(&s->bypass_fifo)) { /* room in bypass packer */ @@ -873,7 +880,7 @@ ot_entropy_src_push_entropy_to_conditioner(OtEntropySrcState *s, uint32_t word) return true; } -static bool ot_entropy_src_can_hash(OtEntropySrcState *s) +static bool ot_entropy_src_can_hash(const OtEntropySrcState *s) { return ot_fifo32_is_empty(&s->precon_fifo) && (s->cond_word >= (2048 / (8u * sizeof(uint32_t)))); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 490673945d4c1..7d80702e0468b 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -168,11 +168,11 @@ ot_entropy_src_change_state(int line, const char *old, int nold, const char *new ot_entropy_src_consume_entropy(bool obs_fifo, bool bypass, bool hw_path, unsigned ncount) "obs_fifo %u, bypass %u, hw_path %u ncount %u" ot_entropy_src_error(const char *msg, const char *state, int st) "%s [%s:%u]" ot_entropy_src_fill_noise(unsigned count, unsigned infifo) "up to %u, input fifo %u" +ot_entropy_src_get_random_fips(const char *state, bool en, bool es_route, bool es_type, bool rng_bit_en, bool cap, bool comp, bool fips) "st:%s en:%u rt:%u tp:%u !rb:%u cap:%u comp:%u => %u" ot_entropy_src_info(const char *msg) "%s" ot_entropy_src_init_ongoing(const char *state, int st, int ns) "ES still initializing in [%s:%u] %d ns to go" ot_entropy_src_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_entropy_src_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_entropy_src_is_fips_capable(bool en, bool es_route, bool es_type, bool rng_bit_en, bool res) "en:%u rt:%u tp:%u !rb:%u => %u" ot_entropy_src_no_entropy(unsigned count) "only %u words available" ot_entropy_src_obs_fifo(unsigned level, unsigned thold) "level %u, threshold %u" ot_entropy_src_push_bypass_entropy(unsigned slot) "final FIFO depth: %u" From 6f135007442784ebfcc586ec7e2e524bdd227904 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:57:54 +0100 Subject: [PATCH 21/37] [ot] hw/opentitan: ot_edn: add a sanity check on command sequence. A new command should never be emitted if there is a previous one on going. Remove useless command buffer wipes. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index d79374e70515b..6efbc95bbe790 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -631,20 +631,30 @@ ot_edn_push_csrng_request(OtEDNState *s, bool auto_mode, uint32_t length) return res; } -static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) +static bool ot_edn_check_command_ready(OtEDNState *s) { - OtEDNCSRNG *c = &s->rng; - + const OtEDNCSRNG *c = &s->rng; uint32_t command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); if (command != OT_CSRNG_CMD_NONE) { xtrace_ot_edn_error(c->appid, "Another command is already scheduled"); s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_MAIN_SM_ERR_MASK; ot_edn_change_state(s, EDN_ERROR); + return false; + } + + return true; +} + +static void ot_edn_send_boot_req(OtEDNState *s, unsigned reg) +{ + OtEDNCSRNG *c = &s->rng; + + if (!ot_edn_check_command_ready(s)) { return; } c->buffer[0u] = s->regs[reg]; - command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); + uint32_t command = FIELD_EX32(c->buffer[0], OT_CSNRG_CMD, ACMD); uint32_t clen = FIELD_EX32(command, OT_CSNRG_CMD, CLEN); if (clen) { qemu_log_mask(LOG_GUEST_ERROR, @@ -694,11 +704,14 @@ static void ot_edn_send_auto_reseed_cmd(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; + if (!ot_edn_check_command_ready(s)) { + return; + } + bool fatal_error = false; uint32_t command; uint32_t length; - memset(c->buffer, 0, sizeof(c->buffer)); if (ot_fifo32_is_empty(&c->cmd_reseed_fifo)) { s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; @@ -744,11 +757,14 @@ static void ot_edn_send_auto_generate_cmd(OtEDNState *s) { OtEDNCSRNG *c = &s->rng; + if (!ot_edn_check_command_ready(s)) { + return; + } + bool fatal_error = false; uint32_t command; uint32_t length; - memset(c->buffer, 0, sizeof(c->buffer)); if (ot_fifo32_is_empty(&c->cmd_gen_fifo)) { s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; @@ -808,7 +824,10 @@ static void ot_edn_send_boot_uninstanciate_cmd(OtEDNState *s) g_assert(s->state == EDN_BOOT_LOAD_UNI); - memset(c->buffer, 0, sizeof(c->buffer)); + if (!ot_edn_check_command_ready(s)) { + return; + } + c->buffer[0u] = FIELD_DP32(0, OT_CSNRG_CMD, ACMD, OT_CSRNG_CMD_UNINSTANTIATE); From 72539c087fcd376932c996d10d762a81f27b34d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 12:58:46 +0100 Subject: [PATCH 22/37] [ot] hw/opentitan: ot_edn: fix issues with disablement handling. EDN_ENABLE is not a boolean, but a multibit boolean. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 38 ++++++++++++++++++++++---------------- hw/opentitan/trace-events | 2 +- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index 6efbc95bbe790..f00f821876b7e 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -911,16 +911,22 @@ static void ot_edn_clean_up(OtEDNState *s, bool discard_requests) bool accept_entropy = ot_edn_update_genbits_ready(s); g_assert(!accept_entropy); c->genbits_ready = NULL; - - ot_edn_change_state(s, EDN_IDLE); } static bool ot_edn_update_mode(OtEDNState *s) { + /* + * CTRL may have disabled EDN, while the EDN was in an active state. + * If disablement has been requested, now is time to handle it, if not + * already done or in a fatal error state. + */ if (!ot_edn_is_enabled(s)) { - if (s->state != EDN_IDLE && s->state != EDN_ERROR) { - ot_edn_change_state(s, EDN_IDLE); + if (s->state != EDN_IDLE) { ot_edn_handle_disable(s); + ot_edn_clean_up(s, true); + if (s->state != EDN_ERROR) { + ot_edn_change_state(s, EDN_IDLE); + } } return true; } @@ -959,6 +965,13 @@ static bool ot_edn_update_mode(OtEDNState *s) static void ot_edn_handle_ctrl(OtEDNState *s, uint32_t val32) { + OtEDNCSRNG *c = &s->rng; + + bool enabled = + FIELD_EX32(s->regs[R_CTRL], CTRL, EDN_ENABLE) == OT_MULTIBITBOOL4_TRUE; + + s->regs[R_CTRL] = val32; + #define CHECK_MULTIBOOT(_s_, _r_, _b_) \ ot_edn_check_multibitboot((_s_), FIELD_EX32(val32, _r_, _b_), \ ALERT_STATUS_BIT(_b_)); @@ -966,16 +979,11 @@ static void ot_edn_handle_ctrl(OtEDNState *s, uint32_t val32) bool boot_req_mode = CHECK_MULTIBOOT(s, CTRL, BOOT_REQ_MODE); bool auto_req_mode = CHECK_MULTIBOOT(s, CTRL, AUTO_REQ_MODE); bool cmd_fifo_rst = CHECK_MULTIBOOT(s, CTRL, CMD_FIFO_RST); - - bool disabling = (s->regs[R_CTRL] & R_CTRL_EDN_ENABLE_MASK) && - !(val32 & R_CTRL_EDN_ENABLE_MASK); - - s->regs[R_CTRL] = val32; - - OtEDNCSRNG *c = &s->rng; + bool disabling = !enable && enabled; trace_ot_edn_ctrl_in_state(c->appid, STATE_NAME(s->state), s->state, enable, - boot_req_mode, auto_req_mode, cmd_fifo_rst); + boot_req_mode, auto_req_mode, cmd_fifo_rst, + disabling); if ((FIELD_EX32(s->regs[R_CTRL], CTRL, CMD_FIFO_RST) == OT_MULTIBITBOOL4_TRUE) || @@ -983,10 +991,6 @@ static void ot_edn_handle_ctrl(OtEDNState *s, uint32_t val32) ot_edn_reset_replay_fifos(s); } - if (disabling) { - ot_edn_clean_up(s, true); - } - if (!ot_edn_update_mode(s)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %u: EDN in %s, write 0x%08x to CTRL is delayed\n", @@ -1497,6 +1501,8 @@ static void ot_edn_reset(DeviceState *dev) s->regs[R_BOOT_GEN_CMD] = 0xfff003u; ot_edn_clean_up(s, true); + ot_edn_change_state(s, EDN_IDLE); + ot_fifo32_reset(&c->cmd_gen_fifo); ot_fifo32_reset(&c->cmd_reseed_fifo); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 7d80702e0468b..f81c86c6a6124 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -139,7 +139,7 @@ ot_edn_clean_up(unsigned appid, bool discard) "a#%u discard:%u" ot_edn_change_state(unsigned appid, int line, const char *old, int nold, const char *new, int nnew) "a#%u @ %d [%s:%d] -> [%s:%d]" ot_edn_connect_endpoint(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_csrng_ack(unsigned appid, const char *state, int level) "a#%u %s %d" -ot_edn_ctrl_in_state(unsigned appid, const char *state, int nstate, bool en, bool boot, bool auto_, bool clr) "a#%u [%s:%d] en:%u boot:%u auto:%u clr:%u" +ot_edn_ctrl_in_state(unsigned appid, const char *state, int nstate, bool en, bool boot, bool auto_, bool clr, bool dis) "a#%u [%s:%d] en:%u boot:%u auto:%u clr:%u dis:%u" ot_edn_dinfo(unsigned appid, const char *func, int line, const char *msg, uint32_t value) "a#%u %s:%d %s %u" ot_edn_enable(unsigned appid, const char *msg) "a#%u: %s" ot_edn_ep_fifo(unsigned appid, const char *msg, unsigned remwslot) "a#%u %s rem:%u" From cc9852e0fe829048d49366d5c5b3331b99ad4214 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 14:23:12 +0100 Subject: [PATCH 23/37] [ot] hw/opentitan: ot_edn: replace legacy reset with Resettable implementation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 23 +++++++++++++++++++---- hw/opentitan/trace-events | 2 +- include/hw/opentitan/ot_edn.h | 3 ++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index f00f821876b7e..d69630f551dca 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -231,6 +231,11 @@ typedef struct OtEDNEndPoint { typedef QSIMPLEQ_HEAD(OtEndpointQueue, OtEDNEndPoint) OtEndpointQueue; +struct OtEDNClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + struct OtEDNState { SysBusDevice parent_obj; @@ -1487,12 +1492,17 @@ static const MemoryRegionOps ot_edn_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_edn_reset(DeviceState *dev) +static void ot_edn_reset_enter(Object *obj, ResetType type) { - OtEDNState *s = OT_EDN(dev); + OtEDNClass *k = OT_EDN_GET_CLASS(obj); + OtEDNState *s = OT_EDN(obj); OtEDNCSRNG *c = &s->rng; - trace_ot_edn_reset(s->rng.appid); + trace_ot_edn_reset(s->rng.appid, "enter"); + + if (k->parent_phases.enter) { + k->parent_phases.enter(obj, type); + } memset(s->regs, 0, REGS_SIZE); s->regs[R_REGWEN] = 0x1u; @@ -1550,9 +1560,13 @@ static void ot_edn_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_edn_reset); device_class_set_props(dc, ot_edn_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtEDNClass *uc = OT_EDN_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_edn_reset_enter, NULL, NULL, + &uc->parent_phases); } static const TypeInfo ot_edn_info = { @@ -1560,6 +1574,7 @@ static const TypeInfo ot_edn_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtEDNState), .instance_init = &ot_edn_init, + .class_size = sizeof(OtEDNClass), .class_init = &ot_edn_class_init, }; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index f81c86c6a6124..a05f244c05a7d 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -155,7 +155,7 @@ ot_edn_irqs(unsigned appid, uint32_t active, uint32_t mask, uint32_t eff) "a#%u ot_edn_push_csrng_command(unsigned appid, const char *mode, uint32_t value) "a#%u (%s) cmd:0x%08x" ot_edn_push_csrng_error(unsigned appid, int res) "a#%u res:%d" ot_edn_request_entropy(unsigned appid, unsigned epid) "a#%u:e#%u" -ot_edn_reset(unsigned appid) "a#%u" +ot_edn_reset(unsigned appid, const char *phase) "a#%u %s" ot_edn_reset_replay_fifos(unsigned appid) "a#%u" ot_edn_schedule(unsigned appid, const char *cause) "a#%u %s" ot_edn_update_genbits_ready(unsigned appid, unsigned rem, unsigned fslot, bool accept) "a#%u rem packet %u, free slot %u, accept? %u" diff --git a/include/hw/opentitan/ot_edn.h b/include/hw/opentitan/ot_edn.h index 7027efd4ff2a0..679ca01ca993c 100644 --- a/include/hw/opentitan/ot_edn.h +++ b/include/hw/opentitan/ot_edn.h @@ -2,6 +2,7 @@ * QEMU OpenTitan Entropy Distribution Network device * * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -31,7 +32,7 @@ #include "qom/object.h" #define TYPE_OT_EDN "ot-edn" -OBJECT_DECLARE_SIMPLE_TYPE(OtEDNState, OT_EDN) +OBJECT_DECLARE_TYPE(OtEDNState, OtEDNClass, OT_EDN) /* * Function called by the EDN instance whenever entropy has been requested From 051db7140c42339edb8380b37388343f81b1675a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 14:55:15 +0100 Subject: [PATCH 24/37] [ot] hw/opentitan: ot_common: redefine some OBJECT macro with an explicit class name QEMU assumes that the class name should be built from the instance name, however this is not even used constantly across QEMU files. Only when abstract classes are used this auto naming scheme is applied. Redefine two similar macros (from the original ones) to provide the actual object class name. Signed-off-by: Emmanuel Blot --- include/hw/opentitan/ot_common.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/hw/opentitan/ot_common.h b/include/hw/opentitan/ot_common.h index 7c0b63c7500f8..0961088bafdc7 100644 --- a/include/hw/opentitan/ot_common.h +++ b/include/hw/opentitan/ot_common.h @@ -325,4 +325,29 @@ void ot_common_configure_devices_with_id( void ot_common_configure_device_opts(DeviceState **devices, unsigned count); +/* ------------------------------------------------------------------------ */ +/* OBJECT macros */ +/* ------------------------------------------------------------------------ */ + +/* + * OBJECT_DEFINE_TYPE_EXTENDED with explicit class name + */ +#define OT_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, ModuleClassName, \ + module_obj_name, MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, ABSTRACT, ...) \ + DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, sizeof(ModuleClassName), \ + __VA_ARGS__) + +/* + * OBJECT_DEFINE_ABSTRACT_TYPE with explicit class name + */ +#define OT_OBJECT_DEFINE_ABSTRACT_TYPE(ModuleObjName, ModuleClassName, \ + module_obj_name, MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME) \ + OT_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, ModuleClassName, \ + module_obj_name, MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, true, { NULL }) + #endif /* HW_OPENTITAN_OT_COMMON_H */ From b66b17a26391aaa2ae40c5455e9d4e1f99e7bbb0 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 14:29:19 +0100 Subject: [PATCH 25/37] [ot] hw/opentitan: ot_ibex_wrapper: rename OtIbexWrapperStateClass This naming better follows other OT class naming conventions. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper.c | 6 ++++-- hw/opentitan/ot_ibex_wrapper_dj.c | 5 +++-- hw/opentitan/ot_ibex_wrapper_eg.c | 5 +++-- include/hw/opentitan/ot_ibex_wrapper.h | 6 +++--- include/hw/opentitan/ot_ibex_wrapper_dj.h | 3 ++- include/hw/opentitan/ot_ibex_wrapper_eg.h | 3 ++- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_ibex_wrapper.c b/hw/opentitan/ot_ibex_wrapper.c index a2dabfa070cf0..793ff1aa3842b 100644 --- a/hw/opentitan/ot_ibex_wrapper.c +++ b/hw/opentitan/ot_ibex_wrapper.c @@ -2,6 +2,7 @@ * QEMU OpenTitan Ibex wrapper device * * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Loïc Lefort @@ -26,10 +27,11 @@ */ #include "qemu/osdep.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_ibex_wrapper.h" -OBJECT_DEFINE_ABSTRACT_TYPE(OtIbexWrapperState, ot_ibex_wrapper, - OT_IBEX_WRAPPER, SYS_BUS_DEVICE) +OT_OBJECT_DEFINE_ABSTRACT_TYPE(OtIbexWrapperState, OtIbexWrapperClass, + ot_ibex_wrapper, OT_IBEX_WRAPPER, SYS_BUS_DEVICE) static void ot_ibex_wrapper_class_init(ObjectClass *oc, void *data) {} diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c index bff29115d0abd..17abff0cca223 100644 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ b/hw/opentitan/ot_ibex_wrapper_dj.c @@ -2,6 +2,7 @@ * QEMU OpenTitan Darjeeling Ibex wrapper device * * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -1606,11 +1607,11 @@ static void ot_ibex_wrapper_dj_class_init(ObjectClass *klass, void *data) static const TypeInfo ot_ibex_wrapper_dj_info = { .name = TYPE_OT_IBEX_WRAPPER_DJ, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_OT_IBEX_WRAPPER, .instance_size = sizeof(OtIbexWrapperDjState), .instance_init = &ot_ibex_wrapper_dj_init, .class_init = &ot_ibex_wrapper_dj_class_init, - .class_size = sizeof(OtIbexWrapperStateClass), + .class_size = sizeof(OtIbexWrapperClass), }; static void ot_ibex_wrapper_dj_register_types(void) diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c index f1e709fd7ce80..41c8f340ab4ac 100644 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ b/hw/opentitan/ot_ibex_wrapper_eg.c @@ -2,6 +2,7 @@ * QEMU OpenTitan EarlGrey Ibex wrapper device * * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -1039,11 +1040,11 @@ static void ot_ibex_wrapper_eg_class_init(ObjectClass *klass, void *data) static const TypeInfo ot_ibex_wrapper_eg_info = { .name = TYPE_OT_IBEX_WRAPPER_EG, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_OT_IBEX_WRAPPER, .instance_size = sizeof(OtIbexWrapperEgState), .instance_init = &ot_ibex_wrapper_eg_init, .class_init = &ot_ibex_wrapper_eg_class_init, - .class_size = sizeof(OtIbexWrapperStateClass), + .class_size = sizeof(OtIbexWrapperClass), }; static void ot_ibex_wrapper_eg_register_types(void) diff --git a/include/hw/opentitan/ot_ibex_wrapper.h b/include/hw/opentitan/ot_ibex_wrapper.h index baf862448d6d4..b22c7251de7d6 100644 --- a/include/hw/opentitan/ot_ibex_wrapper.h +++ b/include/hw/opentitan/ot_ibex_wrapper.h @@ -2,6 +2,7 @@ * QEMU OpenTitan Ibex Wrapper device * * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -33,14 +34,13 @@ #include "hw/sysbus.h" #define TYPE_OT_IBEX_WRAPPER "ot-ibex_wrapper" -OBJECT_DECLARE_TYPE(OtIbexWrapperState, OtIbexWrapperStateClass, - OT_IBEX_WRAPPER) +OBJECT_DECLARE_TYPE(OtIbexWrapperState, OtIbexWrapperClass, OT_IBEX_WRAPPER) struct OtIbexWrapperState { SysBusDevice parent_obj; }; -struct OtIbexWrapperStateClass { +struct OtIbexWrapperClass { SysBusDeviceClass parent_class; }; diff --git a/include/hw/opentitan/ot_ibex_wrapper_dj.h b/include/hw/opentitan/ot_ibex_wrapper_dj.h index 76069c1468730..8544e52eb6cb3 100644 --- a/include/hw/opentitan/ot_ibex_wrapper_dj.h +++ b/include/hw/opentitan/ot_ibex_wrapper_dj.h @@ -2,6 +2,7 @@ * QEMU OpenTitan Darjeeling Ibex Wrapper device * * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -32,7 +33,7 @@ #include "hw/opentitan/ot_ibex_wrapper.h" #define TYPE_OT_IBEX_WRAPPER_DJ "ot-ibex_wrapper-dj" -OBJECT_DECLARE_TYPE(OtIbexWrapperDjState, OtIbexWrapperStateClass, +OBJECT_DECLARE_TYPE(OtIbexWrapperDjState, OtIbexWrapperClass, OT_IBEX_WRAPPER_DJ) #endif /* HW_OPENTITAN_OT_IBEX_WRAPPER_DJ_H */ diff --git a/include/hw/opentitan/ot_ibex_wrapper_eg.h b/include/hw/opentitan/ot_ibex_wrapper_eg.h index 9bc3c03ee0762..b65179d2badae 100644 --- a/include/hw/opentitan/ot_ibex_wrapper_eg.h +++ b/include/hw/opentitan/ot_ibex_wrapper_eg.h @@ -2,6 +2,7 @@ * QEMU OpenTitan EarlGrey Ibex Wrapper device * * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -33,7 +34,7 @@ #include "hw/opentitan/ot_ibex_wrapper.h" #define TYPE_OT_IBEX_WRAPPER_EG "ot-ibex_wrapper-eg" -OBJECT_DECLARE_TYPE(OtIbexWrapperEgState, OtIbexWrapperStateClass, +OBJECT_DECLARE_TYPE(OtIbexWrapperEgState, OtIbexWrapperClass, OT_IBEX_WRAPPER_EG) #endif /* HW_OPENTITAN_OT_IBEX_WRAPPER_EG_H */ From 87dfad738a456cc587b772ac05a8f4390346cd0a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 14:57:00 +0100 Subject: [PATCH 26/37] [ot] hw/opentitan: ot_ibex_wrapper: replace legacy reset with Resettable implementation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper_dj.c | 19 ++++++++++++++----- hw/opentitan/ot_ibex_wrapper_eg.c | 20 ++++++++++++++------ hw/opentitan/trace-events | 2 +- include/hw/opentitan/ot_ibex_wrapper.h | 2 ++ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c index 17abff0cca223..4279477951a3a 100644 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ b/hw/opentitan/ot_ibex_wrapper_dj.c @@ -1515,11 +1515,16 @@ static const MemoryRegionOps ot_ibex_wrapper_dj_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_ibex_wrapper_dj_reset(DeviceState *dev) +static void ot_ibex_wrapper_dj_reset_enter(Object *obj, ResetType type) { - OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(dev); + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_DJ_GET_CLASS(obj); + OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(obj); - trace_ot_ibex_wrapper_reset(s->ot_id); + trace_ot_ibex_wrapper_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } g_assert(s->ot_id); g_assert(s->sys_mem); @@ -1562,7 +1567,7 @@ static void ot_ibex_wrapper_dj_reset(DeviceState *dev) s->cpu_en_bm = s->lc_ignore ? (1u << OT_IBEX_LC_CTRL_CPU_EN) : 0; memset(s->log_engine, 0, sizeof(*s->log_engine)); - s->log_engine->as = ot_common_get_local_address_space(dev); + s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); } static void ot_ibex_wrapper_dj_realize(DeviceState *dev, Error **errp) @@ -1599,10 +1604,14 @@ static void ot_ibex_wrapper_dj_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_ibex_wrapper_dj_reset); dc->realize = &ot_ibex_wrapper_dj_realize; device_class_set_props(dc, ot_ibex_wrapper_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_dj_reset_enter, + NULL, NULL, &ic->parent_phases); } static const TypeInfo ot_ibex_wrapper_dj_info = { diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c index 41c8f340ab4ac..6dc82f0e61cef 100644 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ b/hw/opentitan/ot_ibex_wrapper_eg.c @@ -967,11 +967,16 @@ static const MemoryRegionOps ot_ibex_wrapper_eg_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_ibex_wrapper_eg_reset(DeviceState *dev) +static void ot_ibex_wrapper_eg_reset_enter(Object *obj, ResetType type) { - OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(dev); + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_EG_GET_CLASS(obj); + OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); + + trace_ot_ibex_wrapper_reset(s->ot_id, "enter"); - trace_ot_ibex_wrapper_reset(s->ot_id); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } g_assert(s->ot_id); g_assert(s->edn); @@ -1004,10 +1009,9 @@ static void ot_ibex_wrapper_eg_reset(DeviceState *dev) s->cpu_en_bm = 1u << OT_IBEX_LC_CTRL_CPU_EN; memset(s->log_engine, 0, sizeof(*s->log_engine)); - s->log_engine->as = ot_common_get_local_address_space(dev); + s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); } - static void ot_ibex_wrapper_eg_init(Object *obj) { OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); @@ -1033,9 +1037,13 @@ static void ot_ibex_wrapper_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_ibex_wrapper_eg_reset); device_class_set_props(dc, ot_ibex_wrapper_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_eg_reset_enter, + NULL, NULL, &ic->parent_phases); } static const TypeInfo ot_ibex_wrapper_eg_info = { diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index a05f244c05a7d..7137e4e6e8bfa 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -254,7 +254,7 @@ ot_ibex_wrapper_io_read_out(const char *id, uint32_t addr, const char * regname, ot_ibex_wrapper_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%08x, pc=0x%x" ot_ibex_wrapper_map(const char *id, unsigned slot, uint32_t src, uint32_t dst, uint32_t size, const char *name, uint32_t offset) "%s: region %u from 0x%08x to 0x%08x on 0x%x bytes (%s), off 0x%x" ot_ibex_wrapper_request_entropy(const char *id, bool again) "%s: %u" -ot_ibex_wrapper_reset(const char *id) "%s" +ot_ibex_wrapper_reset(const char *id, const char *phase) "%s: %s" ot_ibex_wrapper_unmap(const char *id, unsigned slot) "%s: region %u" ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool cpu_en) "%s: 0x%x %u-> CPU enable %u" diff --git a/include/hw/opentitan/ot_ibex_wrapper.h b/include/hw/opentitan/ot_ibex_wrapper.h index b22c7251de7d6..aa4c5a4f187b6 100644 --- a/include/hw/opentitan/ot_ibex_wrapper.h +++ b/include/hw/opentitan/ot_ibex_wrapper.h @@ -31,6 +31,7 @@ #define HW_OPENTITAN_OT_IBEX_WRAPPER_H #include "qom/object.h" +#include "hw/resettable.h" #include "hw/sysbus.h" #define TYPE_OT_IBEX_WRAPPER "ot-ibex_wrapper" @@ -42,6 +43,7 @@ struct OtIbexWrapperState { struct OtIbexWrapperClass { SysBusDeviceClass parent_class; + ResettablePhases parent_phases; }; #define OT_IBEX_WRAPPER_CPU_EN TYPE_OT_IBEX_WRAPPER "-cpu-en" From 846e6f1ce0c4c2ebb9d5c9b60c43bbe79cbb2838 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 15:12:18 +0100 Subject: [PATCH 27/37] [ot] hw/opentitan: ot_ibex_wrapper: add reset_exit implementation Upon reset the data will be invalid with a new EDN request pending. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper_dj.c | 19 ++++++++++++++++++- hw/opentitan/ot_ibex_wrapper_eg.c | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c index 4279477951a3a..9324880b34d7b 100644 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ b/hw/opentitan/ot_ibex_wrapper_dj.c @@ -1567,7 +1567,23 @@ static void ot_ibex_wrapper_dj_reset_enter(Object *obj, ResetType type) s->cpu_en_bm = s->lc_ignore ? (1u << OT_IBEX_LC_CTRL_CPU_EN) : 0; memset(s->log_engine, 0, sizeof(*s->log_engine)); +} + +static void ot_ibex_wrapper_dj_reset_exit(Object *obj, ResetType type) +{ + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_DJ_GET_CLASS(obj); + OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(obj); + + trace_ot_ibex_wrapper_reset(s->ot_id, "exit"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); + + /* "Upon reset the data will be invalid with a new EDN request pending." */ + ot_ibex_wrapper_dj_request_entropy(s); } static void ot_ibex_wrapper_dj_realize(DeviceState *dev, Error **errp) @@ -1611,7 +1627,8 @@ static void ot_ibex_wrapper_dj_class_init(ObjectClass *klass, void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_dj_reset_enter, - NULL, NULL, &ic->parent_phases); + NULL, &ot_ibex_wrapper_dj_reset_exit, + &ic->parent_phases); } static const TypeInfo ot_ibex_wrapper_dj_info = { diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c index 6dc82f0e61cef..0501197d10e58 100644 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ b/hw/opentitan/ot_ibex_wrapper_eg.c @@ -1009,7 +1009,23 @@ static void ot_ibex_wrapper_eg_reset_enter(Object *obj, ResetType type) s->cpu_en_bm = 1u << OT_IBEX_LC_CTRL_CPU_EN; memset(s->log_engine, 0, sizeof(*s->log_engine)); +} + +static void ot_ibex_wrapper_eg_reset_exit(Object *obj, ResetType type) +{ + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_EG_GET_CLASS(obj); + OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); + + trace_ot_ibex_wrapper_reset(s->ot_id, "exit"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); + + /* "Upon reset the data will be invalid with a new EDN request pending." */ + ot_ibex_wrapper_eg_request_entropy(s); } static void ot_ibex_wrapper_eg_init(Object *obj) @@ -1043,7 +1059,8 @@ static void ot_ibex_wrapper_eg_class_init(ObjectClass *klass, void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_eg_reset_enter, - NULL, NULL, &ic->parent_phases); + NULL, &ot_ibex_wrapper_eg_reset_exit, + &ic->parent_phases); } static const TypeInfo ot_ibex_wrapper_eg_info = { From ee3bf125df2bb52734f3237bb76952257bc0ce48 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 16:38:50 +0100 Subject: [PATCH 28/37] [ot] hw/opentitan: ot_csrng: fix connection management 1. inverted detection of a previous EDN connection 2. the filler_fn function now tracks whether an EDN is connected or not, the instantiation status was a left over from the initial implementation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index 79d1ed80307b3..ddec1ac8fd679 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -439,7 +439,7 @@ ot_csnrg_connect_hw_app(OtCSRNGState *s, unsigned app_id, qemu_irq req_sts, OtCSRNGInstance *inst = &s->instances[app_id]; if (!filler_fn) { - if (inst->hw.filler) { + if (!inst->hw.filler) { xtrace_ot_csrng_info("HW app was not connected", app_id); return NULL; } @@ -1027,7 +1027,7 @@ static void ot_csrng_handle_enable(OtCSRNGState *s) /* skip SW instance */ for (unsigned ix = 0u; ix < OT_CSRNG_HW_APP_MAX; ix++) { OtCSRNGInstance *inst = &s->instances[ix]; - if (ot_csrng_drng_is_instantiated(inst)) { + if (inst->hw.filler) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Forcing CSRNG disablement while EDN #%u " "still active\n", From 5c06e482e3ec62d7a0272c17b85115207c9b2cd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 16:48:03 +0100 Subject: [PATCH 29/37] [ot] hw/opentitan: ot_edn: delayed mode change is legit, not an error Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 4 +--- hw/opentitan/trace-events | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index d69630f551dca..1c355501c1dc3 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -997,9 +997,7 @@ static void ot_edn_handle_ctrl(OtEDNState *s, uint32_t val32) } if (!ot_edn_update_mode(s)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %u: EDN in %s, write 0x%08x to CTRL is delayed\n", - __func__, c->appid, STATE_NAME(s->state), val32); + trace_ot_edn_delay_mode_change(c->appid, STATE_NAME(s->state), val32); } } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 7137e4e6e8bfa..e5f141b0422a5 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -140,6 +140,7 @@ ot_edn_change_state(unsigned appid, int line, const char *old, int nold, const c ot_edn_connect_endpoint(unsigned appid, unsigned epid) "a#%u:e#%u" ot_edn_csrng_ack(unsigned appid, const char *state, int level) "a#%u %s %d" ot_edn_ctrl_in_state(unsigned appid, const char *state, int nstate, bool en, bool boot, bool auto_, bool clr, bool dis) "a#%u [%s:%d] en:%u boot:%u auto:%u clr:%u dis:%u" +ot_edn_delay_mode_change(unsigned appid, const char *state, uint32_t ctrl) "a#%u %s ctrl:0x%08x" ot_edn_dinfo(unsigned appid, const char *func, int line, const char *msg, uint32_t value) "a#%u %s:%d %s %u" ot_edn_enable(unsigned appid, const char *msg) "a#%u: %s" ot_edn_ep_fifo(unsigned appid, const char *msg, unsigned remwslot) "a#%u %s rem:%u" From 56f0be58bb0e0fb1b5ba1121518ef108917a02f8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 17:10:15 +0100 Subject: [PATCH 30/37] [ot] hw/opentitan: ot_csrng: add missing write access to 2 new registers Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index ddec1ac8fd679..e286903849027 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -160,12 +160,12 @@ REG32(MAIN_SM_STATE, 0x5cu) (R_RECOV_ALERT_STS_ENABLE_FIELD_ALERT_MASK | \ R_RECOV_ALERT_STS_SW_APP_ENABLE_FIELD_ALERT_MASK | \ R_RECOV_ALERT_STS_READ_INT_STATE_FIELD_ALERT_MASK | \ - R_RECOV_ALERT_STS_FIPS_FORCE_ENABLE_FIELD_ALERT | \ - R_RECOV_ALERT_STS_ACMD_FLAG0_FIELD_ALERT | \ + R_RECOV_ALERT_STS_FIPS_FORCE_ENABLE_FIELD_ALERT_MASK | \ + R_RECOV_ALERT_STS_ACMD_FLAG0_FIELD_ALERT_MASK | \ R_RECOV_ALERT_STS_CS_BUS_CMP_ALERT_MASK | \ - R_RECOV_ALERT_STS_CMD_STAGE_INVALID_ACMD_ALERT | \ - R_RECOV_ALERT_STS_CMD_STAGE_INVALID_CMD_SEQ_ALERT | \ - R_RECOV_ALERT_STS_CMD_STAGE_RESEED_CNT_ALERT) + R_RECOV_ALERT_STS_CMD_STAGE_INVALID_ACMD_ALERT_MASK | \ + R_RECOV_ALERT_STS_CMD_STAGE_INVALID_CMD_SEQ_ALERT_MASK | \ + R_RECOV_ALERT_STS_CMD_STAGE_INVALID_RESEED_CNT_ALERT_MASK) #define ERR_CODE_MASK 0x77e0ffffu #define OT_CSRNG_AES_KEY_SIZE 32u /* 256 bits */ @@ -885,7 +885,7 @@ static void ot_csrng_update_alerts(OtCSRNGState *s) uint32_t level = s->regs[R_ALERT_TEST]; s->regs[R_ALERT_TEST] = 0u; - if (__builtin_popcount(s->regs[R_RECOV_ALERT_STS])) { + if (s->regs[R_RECOV_ALERT_STS]) { level |= 1u << ALERT_RECOVERABLE; } @@ -1945,6 +1945,15 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, val32 &= R_FIPS_FORCE_VAL_MASK; s->regs[reg] = val32; break; + case R_HW_EXC_STS: + val32 &= R_HW_EXC_STS_VAL_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_RECOV_ALERT_STS: + val32 &= RECOV_ALERT_STS_MASK; + s->regs[reg] &= val32; /* RW0C */ + ot_csrng_update_alerts(s); + break; case R_ERR_CODE_TEST: if (!s->regs[R_REGWEN]) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s protected w/ REGWEN\n", From 3fca6e469080e23b28a63de70200c1e47b4efdc7 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Mar 2025 17:14:10 +0100 Subject: [PATCH 31/37] [ot] hw/opentitan: ot_entropy_src: add missing read access to 1 register Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index f738171bd05dd..6b8b8b826f81c 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1180,6 +1180,7 @@ ot_entropy_src_regs_read(void *opaque, hwaddr addr, unsigned size) case R_EXTHT_FAIL_COUNTS: case R_FW_OV_CONTROL: case R_FW_OV_SHA3_START: + case R_FW_OV_RD_FIFO_OVERFLOW: case R_OBSERVE_FIFO_THRESH: case R_RECOV_ALERT_STS: case R_ERR_CODE: From cba42e2c27aea9fb6ed0b7aabbcbe6998362ef84 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Mar 2025 15:44:17 +0100 Subject: [PATCH 32/37] [ot] python/qemu: ot.eflash.gen: fix a bug when storing an ELF file When an ELF file is used to store an app in the flash image, and not binary physical file exists, the binary data is automatically generated but no match should be attempted between the ELF metadata and the generated binary content. Signed-off-by: Emmanuel Blot --- python/qemu/ot/eflash/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qemu/ot/eflash/gen.py b/python/qemu/ot/eflash/gen.py index 71d81324c58a8..5ee3f80f097f1 100644 --- a/python/qemu/ot/eflash/gen.py +++ b/python/qemu/ot/eflash/gen.py @@ -301,7 +301,7 @@ def store_rom_ext(self, bank: int, dfp: BinaryIO, elif elfpath and not no_header: self._log.warning('Discarding ELF as input binary file is invalid') elfpath = None - if elfpath and not no_header: + if elfpath and isfile(dfp.name) and not no_header: elftime = stat(elfpath).st_mtime bintime = stat(dfp.name).st_mtime if bintime < elftime: From 4b69ff850c375de1a7458d896ea972079be0db59 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Mar 2025 15:49:37 +0100 Subject: [PATCH 33/37] [ot] hw/opentitan: ot_entropy_src: ignore generation identifier The documented rules 1. ENTROPY_SRC may only be disabled if CSRNG is disabled. 3. Once disabled, CSRNG may only be re-enabled after ENTROPY_SRC has been disabled and re-enabled. no longer apply: remove the related checks. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 22 +++------------------- hw/opentitan/trace-events | 1 - 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 6b8b8b826f81c..d712deef3bce7 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -396,7 +396,6 @@ struct OtEntropySrcState { OtFifo32 final_fifo; /* output FIFO */ hash_state sha3_state; /* libtomcrypt hash state */ OtEntropySrcFsmState state; - unsigned gennum; unsigned cond_word; /* count of words processed with SHA3 till hash */ unsigned noise_count; /* count of consumed noise words since enabled */ unsigned packet_count; /* count of output packets since enabled */ @@ -478,7 +477,7 @@ static int ot_entropy_src_get_generation(OtRandomSrcIf *dev) { OtEntropySrcState *s = OT_ENTROPY_SRC(dev); - return ot_entropy_src_is_module_enabled(s) ? (int)s->gennum : 0; + return ot_entropy_src_is_module_enabled(s) ? -1 : 0; } static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, @@ -486,25 +485,13 @@ static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, bool *fips) { OtEntropySrcState *s = OT_ENTROPY_SRC(dev); + (void)genid; /* accept any generation identifier */ if (!ot_entropy_src_is_module_enabled(s)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: entropy_src is down\n", __func__); return -2; } - if (genid != (int)s->gennum) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: entropy_src gennum mismatch req:%d cur:%u\n", - __func__, genid, s->gennum); - /* - * Continue anyway as it seems HW does not enforce what is documented. - * Force the generation id so the warning message is only shown once. - */ - if (genid != 0) { - s->gennum = (unsigned)genid; - } - } - bool fips_compliant; switch (s->state) { @@ -1354,8 +1341,6 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; } if ((old ^ s->regs[reg]) && ot_entropy_src_is_module_enabled(s)) { - s->gennum += 1; - trace_ot_entropy_src_update_generation(s->gennum); if (ot_entropy_src_is_fips_enabled(s)) { /* start up phase */ ot_entropy_src_change_state(s, @@ -1364,7 +1349,6 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, /* boot phase */ ot_entropy_src_change_state(s, ENTROPY_SRC_BOOT_HT_RUNNING); } - trace_ot_entropy_src_info("initial schedule"); uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); timer_mod(s->scheduler, (int64_t)(now + @@ -1616,7 +1600,7 @@ static void ot_entropy_src_reset(DeviceState *dev) ot_fifo32_reset(&s->observe_fifo); ot_fifo32_reset(&s->swread_fifo); ot_fifo32_reset(&s->final_fifo); - /* note: s->gennum should not be updated on reset */ + s->cond_word = 0u; s->noise_count = 0u; s->packet_count = 0u; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index e5f141b0422a5..7a2affa2fbe3b 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -180,7 +180,6 @@ ot_entropy_src_push_bypass_entropy(unsigned slot) "final FIFO depth: %u" ot_entropy_src_reset(void) "" ot_entropy_src_show_buffer(const char *func, int line, const char *msg, const char *hexstr) "%s:%u %s: %s" ot_entropy_src_update_filler(bool iok, bool ook, bool pok, bool all) "in %u, out %u, proc %u -> %u" -ot_entropy_src_update_generation(unsigned gennum) "%u" # ot_flash.c From 106fc51546e49467554f9567c0b3f911ba3974ed Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Mar 2025 16:17:06 +0100 Subject: [PATCH 34/37] [ot] hw/opentitan: ot_entropy_src: fix initial values They had not been updated when registers had. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index d712deef3bce7..81d9e86467a2e 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1334,8 +1334,6 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, s->regs[reg] = val32; CHECK_MULTIBOOT(s, MODULE_ENABLE, MODULE_ENABLE); if (ot_entropy_src_is_module_disabled(s)) { - /* change state in disable mode can discard an error state */ - ot_entropy_src_change_state(s, ENTROPY_SRC_IDLE); /* reset takes care of cancelling the scheduler timer */ ot_entropy_src_reset(DEVICE(s)); break; @@ -1576,7 +1574,7 @@ static void ot_entropy_src_reset(DeviceState *dev) s->regs[R_REGWEN] = 0x1u; s->regs[R_REV] = 0x10303u; s->regs[R_MODULE_ENABLE] = 0x9u; - s->regs[R_CONF] = 0x909099u; + s->regs[R_CONF] = 0x2649999u; s->regs[R_ENTROPY_CONTROL] = 0x99u; s->regs[R_HEALTH_TEST_WINDOWS] = 0x600200u; s->regs[R_REPCNT_THRESHOLDS] = 0xffffffffu; @@ -1591,7 +1589,7 @@ static void ot_entropy_src_reset(DeviceState *dev) s->regs[R_ALERT_THRESHOLD] = 0xfffd0002u; s->regs[R_FW_OV_CONTROL] = 0x99u; s->regs[R_FW_OV_SHA3_START] = 0x9u; - s->regs[R_OBSERVE_FIFO_THRESH] = 0x20u; + s->regs[R_OBSERVE_FIFO_THRESH] = 0x10u; s->regs[R_DEBUG_STATUS] = 0x10000u; ot_fifo32_reset(&s->input_fifo); @@ -1604,6 +1602,7 @@ static void ot_entropy_src_reset(DeviceState *dev) s->cond_word = 0u; s->noise_count = 0u; s->packet_count = 0u; + s->obs_fifo_en = false; ot_entropy_src_update_irqs(s); for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { @@ -1615,8 +1614,6 @@ static void ot_entropy_src_reset(DeviceState *dev) const OtOTPEntropyCfg *entropy_cfg = oc->get_entropy_cfg(s->otp_ctrl); g_assert(entropy_cfg); - s->obs_fifo_en = false; - ot_entropy_src_change_state(s, ENTROPY_SRC_IDLE); } From b9a909be86b7f195198ef3396e49f9f8d04525c1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Mar 2025 17:06:51 +0100 Subject: [PATCH 35/37] [ot] hw/opentitan: ot_otbn: fix FIPS compliance management FIPS status should be reset after each entropy packet is delivered to the OTBN core. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otbn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/opentitan/ot_otbn.c b/hw/opentitan/ot_otbn.c index da5f72fcc4d12..7a63b3b5bd8df 100644 --- a/hw/opentitan/ot_otbn.c +++ b/hw/opentitan/ot_otbn.c @@ -328,6 +328,7 @@ static void ot_otbn_fill_entropy(void *opaque, uint32_t bits, bool fips) break; } ot_fifo32_reset(&rnd->packer); + rnd->no_fips = false; if (res) { trace_ot_otbn_error("cannot push entropy"); } From 9d8ad42cbb7975d032d6940c1fa53201964877cf Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 31 Mar 2025 10:57:44 +0200 Subject: [PATCH 36/37] [ot] hw/opentitan: ot_random_src: rework interface and implementation Get rid of the generation identifier. This mechanism had initially be implemented to verify early EarlGrey entropy source constraint, quoting: "CSRNG may only be enabled if ENTROPY_SRC is enabled. Once disabled, CSRNG may only be re-enabled after ENTROPY_SRC has been disabled and re-enabled." This constraint has been removed in recent EarlGrey implementations while Darjeeling implementation did not follow this constraint, as it did not make use of the entropy source IP. Generation number is therefore removed; get_random_generation API is no longer needed, neither is the genid argument of get_random_values. Error management can be simplified accordingly. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ast_dj.c | 21 ++------- hw/opentitan/ot_csrng.c | 69 +++++----------------------- hw/opentitan/ot_entropy_src.c | 16 ++----- hw/opentitan/trace-events | 2 +- include/hw/opentitan/ot_random_src.h | 31 +++---------- 5 files changed, 26 insertions(+), 113 deletions(-) diff --git a/hw/opentitan/ot_ast_dj.c b/hw/opentitan/ot_ast_dj.c index cfaad3d521a54..4dda91601ebd2 100644 --- a/hw/opentitan/ot_ast_dj.c +++ b/hw/opentitan/ot_ast_dj.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling Analog Sensor Top device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -152,26 +152,12 @@ struct OtASTDjState { /* Private implementation */ /* -------------------------------------------------------------------------- */ -static int ot_ast_dj_get_generation(OtRandomSrcIf *dev) -{ - (void)dev; - - return -1; -} - -static int ot_ast_dj_get_random(OtRandomSrcIf *dev, int genid, - uint64_t random[OT_RANDOM_SRC_DWORD_COUNT], - bool *fips) +static int ot_ast_dj_get_random( + OtRandomSrcIf *dev, uint64_t random[OT_RANDOM_SRC_DWORD_COUNT], bool *fips) { OtASTDjState *s = OT_AST_DJ(dev); OtASTDjRandom *rnd = &s->random; - if (genid != -1) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: AST gennum mismatch req:%d\n", - __func__, genid); - return -2; - } - if (!rnd->avail) { /* not ready */ trace_ot_ast_no_entropy(0); @@ -441,7 +427,6 @@ static void ot_ast_dj_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); OtRandomSrcIfClass *rdc = OT_RANDOM_SRC_IF_CLASS(klass); - rdc->get_random_generation = &ot_ast_dj_get_generation; rdc->get_random_values = &ot_ast_dj_get_random; } diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index e286903849027..601d5d57e0961 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -343,12 +343,10 @@ struct OtCSRNGState { bool enabled; bool sw_app_granted; bool read_int_granted; - bool es_available; /* guest warning if entropy power cycling is invalid */ uint32_t scheduled_cmd; unsigned entropy_delay; unsigned es_retry_count; unsigned state_db_ix; - int entropy_gennum; int aes_cipher; /* AES handle for tomcrypt */ OtCSRNGFsmState state; OtCSRNGInstance *instances; @@ -777,18 +775,6 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) drng->seeded = false; if (!flag0) { - if (!s->es_available) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Requesting entropy w/o power cycling ES\n", - __func__); - /* - * Continue anyway as it seems HW does not enforce what is - * documented. Force the flag so the warning message is only - * shown once (it does not serve any other purpose). - */ - s->es_available = true; - } - uint64_t buffer[OT_RANDOM_SRC_DWORD_COUNT]; memset(buffer, 0, sizeof(buffer)); unsigned len = drng->material_len * sizeof(uint32_t); @@ -798,20 +784,21 @@ ot_csrng_drng_reseed(OtCSRNGInstance *inst, DeviceState *rand_dev, bool flag0) uint64_t entropy[OT_RANDOM_SRC_DWORD_COUNT]; int res; bool fips; - trace_ot_csrng_request_entropy(slot, s->entropy_gennum); + trace_ot_csrng_request_entropy(slot); OtRandomSrcIfClass *cls = OT_RANDOM_SRC_IF_GET_CLASS(rand_dev); OtRandomSrcIf *randif = OT_RANDOM_SRC_IF(rand_dev); - res = cls->get_random_values(randif, s->entropy_gennum, entropy, &fips); - if (res) { + res = cls->get_random_values(randif, entropy, &fips); + + if (res < 0) { + s->entropy_delay = 0; + trace_ot_csrng_entropy_rejected(slot, "error", res); + return CSRNG_CMD_STALLED; + } + + if (res > 0) { s->entropy_delay = (res > 1) ? (unsigned)res : 0; - trace_ot_csrng_entropy_rejected(slot, - res < 0 ? (res == -2 ? "stalled" : - "error") : - "not ready", - res); - return res < 0 ? (res == -2 ? CSRNG_CMD_STALLED : - CSRNG_CMD_RESEED_CNT_EXCEEDED) : - CSRNG_CMD_RETRY; + trace_ot_csrng_entropy_rejected(slot, "not ready", res); + return CSRNG_CMD_RETRY; } /* always perform XOR which is a no-op if material_len is zero */ @@ -987,39 +974,11 @@ static void ot_csrng_release_hw_app(OtCSRNGInstance *inst) static void ot_csrng_handle_enable(OtCSRNGState *s) { - /* - * As per EarlGrey 2.5.2-rc0: - * "CSRNG may only be enabled if ENTROPY_SRC is enabled. CSRNG may only be - * disabled if all EDNs are disabled. Once disabled, CSRNG may only be - * re-enabled after ENTROPY_SRC has been disabled and re-enabled." - */ - OtRandomSrcIfClass *cls = OT_RANDOM_SRC_IF_GET_CLASS(s->random_src); - OtRandomSrcIf *randif = OT_RANDOM_SRC_IF(s->random_src); - if (ot_csrng_is_ctrl_enabled(s)) { xtrace_ot_csrng_info("enabling CSRNG", 0); - int gennum = cls->get_random_generation(randif); - if (gennum >= 0) { - /* - * however it is not re-enabling CSRNG w/o cycling the entropy_src - * that is prohibited, but to request entropy from it. The check is - * therefore deferred to the reseed handling which makes use of the - * entropy_src only if flag0 is not set. - */ - s->es_available = gennum > s->entropy_gennum; - xtrace_ot_csrng_info("enable: new ES generation", gennum); - } else { - /* - * tracking enablement/disablement order is not supported by the - * entropy source (such as on Darjeeling) - */ - s->es_available = true; - xtrace_ot_csrng_info("enable: no ES gen tracking", gennum); - } s->enabled = true; s->regs[R_SW_CMD_STS] |= R_SW_CMD_STS_CMD_RDY_MASK; s->es_retry_count = ENTROPY_SRC_INITIAL_REQUEST_COUNT; - s->entropy_gennum = gennum; } if (ot_csrng_is_ctrl_disabled(s)) { @@ -1040,8 +999,6 @@ static void ot_csrng_handle_enable(OtCSRNGState *s) s->enabled = false; s->regs[R_SW_CMD_STS] &= ~R_SW_CMD_STS_CMD_RDY_MASK; s->es_retry_count = 0; - s->entropy_gennum = cls->get_random_generation(randif); - xtrace_ot_csrng_info("disable: last RS generation", s->entropy_gennum); /* cancel any outstanding asynchronous request */ qemu_bh_cancel(s->cmd_scheduler); @@ -2024,8 +1981,6 @@ static void ot_csrng_reset(DeviceState *dev) s->regs[R_INT_STATE_READ_ENABLE] = 0x7u; s->regs[R_MAIN_SM_STATE] = 0x4eu; s->enabled = false; - s->es_available = false; - s->entropy_gennum = 0; s->sw_app_granted = false; s->read_int_granted = false; s->es_retry_count = 0; diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 81d9e86467a2e..34201bb562d41 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -473,23 +473,14 @@ static void ot_entropy_src_reset(DeviceState *dev); static void ot_entropy_src_update_alerts(OtEntropySrcState *s); static void ot_entropy_src_update_filler(OtEntropySrcState *s); -static int ot_entropy_src_get_generation(OtRandomSrcIf *dev) +static int ot_entropy_src_get_random( + OtRandomSrcIf *dev, uint64_t random[OT_RANDOM_SRC_DWORD_COUNT], bool *fips) { OtEntropySrcState *s = OT_ENTROPY_SRC(dev); - return ot_entropy_src_is_module_enabled(s) ? -1 : 0; -} - -static int ot_entropy_src_get_random(OtRandomSrcIf *dev, int genid, - uint64_t random[OT_RANDOM_SRC_DWORD_COUNT], - bool *fips) -{ - OtEntropySrcState *s = OT_ENTROPY_SRC(dev); - (void)genid; /* accept any generation identifier */ - if (!ot_entropy_src_is_module_enabled(s)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: entropy_src is down\n", __func__); - return -2; + return -1; } bool fips_compliant; @@ -1653,7 +1644,6 @@ static void ot_entropy_src_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); OtRandomSrcIfClass *rdc = OT_RANDOM_SRC_IF_CLASS(klass); - rdc->get_random_generation = &ot_entropy_src_get_generation; rdc->get_random_values = &ot_entropy_src_get_random; } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 7a2affa2fbe3b..f26e003119e1c 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -83,7 +83,7 @@ ot_csrng_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%0 ot_csrng_push_command(unsigned slot, const char *cmd, unsigned acmd, char code, unsigned len) "#%u: %s(%u) %clen: %u" ot_csrng_read_state_db(unsigned slot, unsigned pos, uint32_t val) "#%u [%u] = 0x%08x" ot_csrng_reject_command(unsigned slot, uint32_t command, int res) "#%u: cmd: 0x%08x res: %d" -ot_csrng_request_entropy(unsigned slot, int gen) "#%u gen %d" +ot_csrng_request_entropy(unsigned slot) "#%u" ot_csrng_reset(void) "" ot_csrng_retry_es_init(unsigned retry_count) "rescheduling initial ES request: %u to go" ot_csrng_schedule(unsigned slot, const char *kind) "#%u: %s" diff --git a/include/hw/opentitan/ot_random_src.h b/include/hw/opentitan/ot_random_src.h index a7ceee1608eaa..606ef74091dea 100644 --- a/include/hw/opentitan/ot_random_src.h +++ b/include/hw/opentitan/ot_random_src.h @@ -1,7 +1,9 @@ /* * QEMU OpenTitan Random Source interface * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. + * * * Author(s): * Emmanuel Blot @@ -48,39 +50,20 @@ typedef struct OtRandomSrcIf OtRandomSrcIf; struct OtRandomSrcIfClass { InterfaceClass parent_class; - /* - * Tell whether the random source is available, i.e. whether the random - * source module has been enabled. - * - * @dev the random source instance - * @return 0 if the random_src is disabled, otherwise: - * * a positive, monotonic increase generation number which indicates the - * number of time the random_src has been cycled (enabled from a - * disable state). This generation identifier should be passed on any - * subsequent #get_random_values request, or - * * a negative number, which indicates that the random source is enabled, - * but the generation number should be simply ignored. - */ - int (*get_random_generation)(OtRandomSrcIf *dev); - /* * Fill up a buffer with random values * * @dev the random source instance - * @genid the generation identifier, from #get_random_generation * @random the buffer to fill in with random data * @fips on success, updated to @true if random data are FIPS-compliant * @return 0 on success, - * >=1 if the source is initializing, if >1, indicates the hint on - * how many ns to wait before retrying, + * >=1 if the random source is still initializing or not enough + * entropy is available to fill the output buffer; + * if >1, indicates a hint on how many ns to wait before retrying, * -1 if the random source is not available, i.e. if the module is * not enabled or if the selected route is not the HW one, - * -2 if the generation ID does not match and execution cannot - * process any further, 1 if the random source is still - * initializing or not enough entropy is available to fill the - * output buffer. */ - int (*get_random_values)(OtRandomSrcIf *dev, int genid, + int (*get_random_values)(OtRandomSrcIf *dev, uint64_t random[OT_RANDOM_SRC_DWORD_COUNT], bool *fips); }; From 3838be98fecc5ddcd16e6dd5b8afe72584c76b13 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Mar 2025 10:50:10 +0100 Subject: [PATCH 37/37] [ot] .gitlab-ci.d: opentitan: update BM tests Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index e0800b57d884d..2e0fdae3a1ea5 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "b0-250310-1" + BAREMETAL_REF: "b0-250325-1" QEMU_BUILD_OPTS: "--disable-install-blobs" include: