From 0f68547197f8bb8849ea0fb307480a44eb529c4e Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Mon, 8 Sep 2025 20:01:43 +0100 Subject: [PATCH 1/6] [ot] hw/opentitan: ot_flash: Reduce init delay & document issues To work around issues seen in some OpenTitan flash testing where SW relies on the bus stalling reads to the FIFOs after initialisation before checking that the initialisation has completed, reduce the initialisation delay to 10 microseconds, and add an explanatory TODO comment about the behaviour of reading from `R_RD_FIFO` and some of the complications of emulating this within QEMU. Signed-off-by: Alex Jones --- hw/opentitan/ot_flash.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index 096cdb172955e..6a131a2de2be6 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -721,7 +721,7 @@ enum { BIN_APP_COUNT, }; -#define OP_INIT_DURATION_NS 1000000u /* 1 ms */ +#define OP_INIT_DURATION_NS 10000u /* 10 us */ #define ELFNAME_SIZE 256u #define OT_FLASH_READ_FIFO_SIZE 16u #define OT_FLASH_PROG_FIFO_SIZE 16u @@ -2144,7 +2144,29 @@ static uint64_t ot_flash_regs_read(void *opaque, hwaddr addr, unsigned size) ot_flash_op_execute(s); } } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Read empty FIFO\n", __func__); + /** + * @todo: technically, the hardware appears to stall the bus read + * from the read FIFO until there is valid data to read; this is + * fine in QEMU for most cases as we complete synchronously, except + * for initialisation, where we cannot trivially stall for + * completion to avoid blocking the main IO thread. + * + * For now, the initialization delay is set sufficiently small so + * as to make it unlikely that this case will be hit in OT testing; + * a better long term solution might be removing the initialisation + * delay entirely, or introducing some logic here to cancel the + * timer and complete init, though this would need to be careful + * with races and incomplete operations. + */ + if (!ot_flash_is_initialized(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bus stalling on reads from uninit FIFO are " + "not supported in QEMU\n", + __func__); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read empty FIFO\n", + __func__); + } val32 = 0u; } break; From 05edea1db6228267b91b057d64987f0633750c29 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Tue, 9 Sep 2025 11:28:13 +0100 Subject: [PATCH 2/6] [ot] hw/opentitan: ot_flash: Add keymgr seed retrieval Fill out more of the flash controller initialization logic, now implementing the retrieval of the keymgr (owner and creator) seeds from flash during initialization, which is the first instance of a hardware request being used through the flash controller model. These keys are loaded into the flash controller and stored until they are requested by the keymgr, though this is not hooked up to the keymgr currently. Also performs a few more sanity checks with some errors: check that we aren't already initialized (or initializing) when we try to initialize. Signed-off-by: Alex Jones --- hw/opentitan/ot_flash.c | 320 ++++++++++++++++++++++++++++---- hw/opentitan/trace-events | 3 + include/hw/opentitan/ot_flash.h | 48 +++++ 3 files changed, 331 insertions(+), 40 deletions(-) diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index 6a131a2de2be6..4b08645699ba2 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -48,6 +48,7 @@ #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_fifo32.h" #include "hw/opentitan/ot_flash.h" +#include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties-system.h" #include "hw/qdev-properties.h" @@ -72,6 +73,12 @@ #define FLASH_SEED_BANK 0u #define FLASH_SEED_INFO_PARTITION 0u +#define FLASH_SEED_WIDTH 256u +#define FLASH_SEED_WORDS ((FLASH_SEED_WIDTH) / sizeof(uint32_t)) +#define FLASH_SEED_BYTES ((FLASH_SEED_WIDTH) / 8u) + +static_assert(FLASH_SEED_BYTES == OT_FLASH_KEYMGR_SECRET_BYTES, + "Flash seed & keymgr secret sizes do not match"); /* clang-format off */ REG32(INTR_STATE, 0x0u) @@ -682,6 +689,19 @@ static const char *PROGRAM_SELECTION_NAMES[] = { PROGRAM_SELECTION_NAMES[(_st_)] : \ "?") +#define SECRET_NAME_ENTRY(_st_) [FLASH_KEYMGR_SECRET_##_st_] = stringify(_st_) + +static const char *FLASH_KEYMGR_SECRET_NAMES[] = { + SECRET_NAME_ENTRY(CREATOR_SEED), + SECRET_NAME_ENTRY(OWNER_SEED), +}; + +#undef SECRET_NAME_ENTRY +#define FLASH_KEYMGR_SECRET_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(FLASH_KEYMGR_SECRET_NAMES) ? \ + FLASH_KEYMGR_SECRET_NAMES[(_st_)] : \ + "?") + /** * Bank 0 information partition type 0 pages. * @@ -727,6 +747,8 @@ enum { #define OT_FLASH_PROG_FIFO_SIZE 16u #define BUS_PGM_RES ((REG_BUS_PGM_RES_BYTES) / (OT_TL_UL_D_WIDTH_BYTES)) +#define LC_BROADCAST_DELAY 200u /* 200 ns */ + #define WORD_ALIGN_ADDR(_addr_) ((_addr_) & ~3u) typedef struct { @@ -790,6 +812,16 @@ typedef struct { uint32_t num; } OtFlashFifo; +typedef struct { + QEMUTimer *timer; + uint16_t incoming_signal_bm; /* each bit tells if signal needs handling */ + uint16_t incoming_level_bm; /* level (0/1) of the incoming signals */ + uint16_t current_level_bm; /* current (latched) level of all signals */ +} OtFlashLcBroadcast; + +static_assert(OT_FLASH_LC_BROADCAST_COUNT < 8 * sizeof(uint16_t), + "Invalid OT_FLASH_LC_BROADCAST_COUNT"); + struct OtFlashState { SysBusDevice parent_obj; @@ -808,6 +840,8 @@ struct OtFlashState { /* "sticky" alerts that should stay signaled after firing */ uint32_t latched_alerts; + OtFlashKeyMgrSecret keymgr_seeds[FLASH_KEYMGR_SECRET_COUNT]; + struct { OtFlashOperation kind; unsigned count; @@ -821,6 +855,7 @@ struct OtFlashState { bool hw; /* hw- or sw-requested operation? */ } op; OtFlashLifeCyclePhase phase; /* HW LC phase for memory protection / RMA */ + OtFlashLcBroadcast lc_broadcast; OtFifo32 rd_fifo; OtFifo32 prog_fifo; OtFlashStorage flash; @@ -834,6 +869,7 @@ struct OtFlashState { BlockBackend *blk; /* Flash backend */ OtVMapperState *vmapper; /* to disable execution from flash */ bool no_mem_prot; /* Flag to disable mem protection features */ + bool fatal_escalate; }; /* Flash memory protection rules */ @@ -915,11 +951,6 @@ static const OtFlashHwInfoPageRule OT_FLASH_HW_INFO_PAGE_RULES[] = { }, }; -struct OtFlashClass { - SysBusDeviceClass parent_class; - ResettablePhases parent_phases; -}; - static void ot_flash_update_irqs(OtFlashState *s) { uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -976,7 +1007,10 @@ static bool ot_flash_write_backend(OtFlashState *s, const void *buffer, static bool ot_flash_is_disabled(const OtFlashState *s) { - return s->regs[R_DIS] != OT_MULTIBITBOOL4_FALSE; + bool reg_dis = s->regs[R_DIS] != OT_MULTIBITBOOL4_FALSE; + bool lc_escalate_dis = + s->lc_broadcast.current_level_bm & BIT(OT_FLASH_LC_ESCALATE_EN); + return reg_dis || lc_escalate_dis; } static bool ot_flash_regs_is_wr_enabled(const OtFlashState *s, unsigned regwen) @@ -1043,27 +1077,6 @@ static bool ot_flash_operation_ongoing(const OtFlashState *s) return s->op.kind != OP_NONE && s->op.count; } -static void ot_flash_initialize(OtFlashState *s) -{ - if (ot_flash_in_operation(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot initialize while in op", - __func__); - return; - } - - s->phase = LC_PHASE_SEED; - trace_ot_flash_change_lc_phase(LC_PHASE_NAME(s->phase), s->phase); - - s->op.kind = OP_INIT; - s->op.hw = false; - trace_ot_flash_op_start(OP_NAME(s->op.kind), s->op.hw); - s->regs[R_STATUS] = FIELD_DP32(s->regs[R_STATUS], STATUS, INIT_WIP, 1u); - s->regs[R_PHY_STATUS] = - FIELD_DP32(s->regs[R_PHY_STATUS], PHY_STATUS, INIT_WIP, 1u); - timer_mod(s->op_delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + OP_INIT_DURATION_NS); -} - static void ot_flash_reset_rd_fifo(OtFlashState *s) { ot_fifo32_reset(&s->rd_fifo); @@ -1215,29 +1228,30 @@ static OtFlashPropertyCfg ot_flash_get_info_page_reg_cfg( * https://opentitan.org/book/hw/top_earlgrey/ip_autogen/flash_ctrl/ * index.html#secret-information-partitions * + * @s The flash device * @bank The bank being accessed * @info_partition The info partition being accessed * @page The page being accessed * @cfg The configuration to update/mask (outparam). */ -static void -ot_flash_update_info_page_qualification(unsigned bank, unsigned info_partition, - unsigned page, OtFlashPropertyCfg *cfg) +static void ot_flash_update_info_page_qualification( + const OtFlashState *s, unsigned bank, unsigned info_partition, + unsigned page, OtFlashPropertyCfg *cfg) { OtFlashPropertyCfg qual; qual.scramble_en = true; qual.ecc_en = true; qual.he_en = true; - /* - * TODO: these signals are stubbed out to always give permissions to any - * qualified info pages for now, but in reality they should be connected - * to the lc_ctrl broadcast signals. - */ - bool creator_en = true; - bool owner_en = true; - bool isolated_rd_en = true; - bool isolated_wr_en = true; + /* extra quals depend on lc_ctrl broadcast signals */ + bool creator_en = s->lc_broadcast.current_level_bm & + BIT(OT_FLASH_LC_CREATOR_SEED_SW_RW_EN); + bool owner_en = + s->lc_broadcast.current_level_bm & BIT(OT_FLASH_LC_OWNER_SEED_SW_RW_EN); + bool isolated_rd_en = + s->lc_broadcast.current_level_bm & BIT(OT_FLASH_LC_ISO_PART_SW_RD_EN); + bool isolated_wr_en = + s->lc_broadcast.current_level_bm & BIT(OT_FLASH_LC_ISO_PART_SW_WR_EN); /* retrieve additional qualifications for pages containing secrets */ if (bank != FLASH_SEED_BANK || @@ -1443,7 +1457,7 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) return address; } cfg = ot_flash_get_info_page_reg_cfg(s, info_page_cfg_reg); - ot_flash_update_info_page_qualification(bank, info_partition, page, + ot_flash_update_info_page_qualification(s, bank, info_partition, page, &cfg); } @@ -1876,6 +1890,115 @@ static void ot_flash_op_start(OtFlashState *s) ot_flash_op_execute(s); } +static unsigned ot_flash_get_op_address_from_page(unsigned bank, unsigned page) +{ + return page * BYTES_PER_PAGE + bank * BYTES_PER_BANK; +} + +static void ot_flash_read_keymgr_seed(OtFlashState *s, unsigned page, + OtFlashKeyMgrSecret *seed) +{ + ot_fifo32_reset(&s->hw_rd_fifo); + + s->op.kind = OP_READ; + s->op.address = ot_flash_get_op_address_from_page(FLASH_SEED_BANK, page); + s->op.info_part = true; + s->op.info_sel = FLASH_SEED_INFO_PARTITION; + s->op.count = FLASH_SEED_WORDS; + /* + * init is triggered by SW, but the key reads during init are triggered by + * the flash controller HW + */ + s->op.hw = true; + s->op.failed = false; + s->op.remaining = s->op.count; + + ot_flash_op_start(s); + + uint32_t seed_words[FLASH_SEED_WORDS] = { 0 }; + ot_fifo32_pop_buf(&s->hw_rd_fifo, FLASH_SEED_WORDS, seed_words); + memcpy(seed->secret, seed_words, FLASH_SEED_BYTES); + seed->valid = !s->op.failed; + + /* todo: dump the seed in the trace? */ + trace_ot_flash_read_keymgr_seed(page, !s->op.failed); + + if (s->op.failed) { + ot_flash_set_error(s, R_FAULT_STATUS_SEED_ERR_MASK, s->op.address); + } +} + +static void ot_flash_initialize(OtFlashState *s) +{ + bool initialized = (bool)FIELD_EX32(s->regs[R_STATUS], STATUS, INITIALIZED); + bool init_wip = (bool)FIELD_EX32(s->regs[R_STATUS], STATUS, INIT_WIP); + if (ot_flash_in_operation(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot initialize while in op", + __func__); + return; + } + if (initialized) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: initialize is meaningless when already initialized", + __func__); + return; + } + if (init_wip) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: initialize is meaningless when currently initializing", + __func__); + return; + } + + /* Start the INIT operation. */ + s->op.kind = OP_INIT; + s->op.hw = false; + trace_ot_flash_op_start(OP_NAME(s->op.kind), s->op.hw); + s->regs[R_STATUS] = FIELD_DP32(s->regs[R_STATUS], STATUS, INIT_WIP, 1u); + s->regs[R_PHY_STATUS] = + FIELD_DP32(s->regs[R_PHY_STATUS], PHY_STATUS, INIT_WIP, 1u); + + /* + * TODO: this flash life cycle management logic is currently missing the + * ability to receive RMA requests from the lc_ctrl and wipe. + * + * TODO: implement reading of flash address and data keys from OTP + */ + + /* + * only read the flash seeds if `lc_seed_hw_rd_en` is received from the + * lc_ctrl to indicate "good otp/lc initialization". + */ + bool seed_hw_rd_en = + s->lc_broadcast.current_level_bm & BIT(OT_FLASH_LC_SEED_HW_RD_EN); + if (seed_hw_rd_en) { + /* Read & latch seeds stored in flash on initialisation */ + s->phase = LC_PHASE_SEED; + trace_ot_flash_change_lc_phase(LC_PHASE_NAME(s->phase), s->phase); + + ot_flash_read_keymgr_seed( + s, FLASH_QUAL_INFO_PAGE_CREATOR, + &s->keymgr_seeds[FLASH_KEYMGR_SECRET_CREATOR_SEED]); + ot_flash_read_keymgr_seed( + s, FLASH_QUAL_INFO_PAGE_OWNER, + &s->keymgr_seeds[FLASH_KEYMGR_SECRET_OWNER_SEED]); + } else { + /* TODO: Lock up & wait for RMA entry to reseed and then wipe */ + s->phase = LC_PHASE_NONE; + trace_ot_flash_change_lc_phase(LC_PHASE_NAME(s->phase), s->phase); + /* TODO: should this still complete the init operation? */ + } + + /* continue the init operation */ + s->op.kind = OP_INIT; + s->op.hw = false; + + /* Delay to emulate taking time to process the `INIT` op. */ + timer_mod(s->op_delay, + qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + OP_INIT_DURATION_NS); +} + static void ot_flash_update_exec(OtFlashState *s) { OtVMapperClass *vm = OT_VMAPPER_GET_CLASS(s->vmapper); @@ -2671,6 +2794,100 @@ static void ot_flash_csrs_write(void *opaque, hwaddr addr, uint64_t val64, } } +static void ot_flash_lc_broadcast_recv(void *opaque, int n, int level) +{ + OtFlashState *s = opaque; + OtFlashLcBroadcast *bcast = &s->lc_broadcast; + + g_assert((unsigned)n < OT_FLASH_LC_BROADCAST_COUNT); + + uint16_t bit = 1u << (unsigned)n; + bcast->incoming_signal_bm |= bit; + /* + * As these signals are only used to change permissions, it is valid to + * override a signal value that has not been processed yet. + */ + if (level) { + bcast->incoming_level_bm |= bit; + } else { + bcast->incoming_level_bm &= ~bit; + } + + /* Use a short timer to decouple IRQ signaling from actual handling */ + uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); + timer_mod(s->lc_broadcast.timer, (int64_t)(now + LC_BROADCAST_DELAY)); +} + +static void ot_flash_lc_broadcast(void *opaque) +{ + OtFlashState *s = opaque; + OtFlashLcBroadcast *bcast = &s->lc_broadcast; + + /* handle all flagged signals */ + while (bcast->incoming_signal_bm) { + /* pick the first seen signal and clear it */ + unsigned signal = ctz16(bcast->incoming_signal_bm); + uint16_t signal_mask = 1u << signal; + bcast->incoming_signal_bm &= ~signal_mask; + bcast->current_level_bm &= ~signal_mask; + bcast->current_level_bm |= (bcast->incoming_level_bm & signal_mask); + bool level = (bool)(bcast->current_level_bm & signal_mask); + + trace_ot_flash_lc_broadcast(signal, level); + + switch (signal) { + case OT_FLASH_LC_SEED_HW_RD_EN: + case OT_FLASH_LC_CREATOR_SEED_SW_RW_EN: + case OT_FLASH_LC_OWNER_SEED_SW_RW_EN: + case OT_FLASH_LC_ISO_PART_SW_RD_EN: + case OT_FLASH_LC_ISO_PART_SW_WR_EN: + /* nothing to do here, flag is latched in current_level */ + break; + case OT_FLASH_LC_ESCALATE_EN: + /* flash disabling is detected from latch in current_level */ + /* todo: also change the flash lcmgr lc_state? */ + if (s->fatal_escalate) { + error_setg(&error_fatal, "%s: Flash LC escalate", __func__); + } + break; + case OT_FLASH_LC_NVM_DEBUG_EN: + qemu_log_mask( + LOG_UNIMP, + "%s: lc_nvm_debug_en for JTAG connection is ignored\n", + __func__); + break; + default: + error_setg(&error_fatal, "%s: unexpected LC broadcast %d", __func__, + signal); + g_assert_not_reached(); + break; + } + } +} + +static void ot_flash_get_keymgr_secret(const OtFlashState *s, + OtFlashKeyMgrSecretType type, + OtFlashKeyMgrSecret *secret) +{ + trace_ot_flash_get_keymgr_secret(FLASH_KEYMGR_SECRET_NAME(type), type); + + switch (type) { + case FLASH_KEYMGR_SECRET_CREATOR_SEED: + case FLASH_KEYMGR_SECRET_OWNER_SEED: + memcpy(secret, &s->keymgr_seeds[type], sizeof(OtFlashKeyMgrSecret)); + bool invalid_seed = + (bool)(s->regs[R_FAULT_STATUS] & R_FAULT_STATUS_SEED_ERR_MASK); + secret->valid = !invalid_seed; + return; + default: + error_report("%s: invalid flash keymgr secret type: %d", __func__, + type); + secret->valid = false; + memset(secret->secret, 0u, OT_FLASH_KEYMGR_SECRET_BYTES); + return; + } +} + static void ot_flash_load(OtFlashState *s, Error **errp) { OtFlashStorage *flash = &s->flash; @@ -2877,6 +3094,7 @@ static Property ot_flash_properties[] = { /* Optionally disable memory protection, as searching for valid memory regions and checking their config can slow down regular operation. */ DEFINE_PROP_BOOL("no-mem-prot", OtFlashState, no_mem_prot, false), + DEFINE_PROP_BOOL("fatal_escalate", OtFlashState, fatal_escalate, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2918,6 +3136,8 @@ static void ot_flash_reset_enter(Object *obj, ResetType type) c->parent_phases.enter(obj, type); } + timer_del(s->lc_broadcast.timer); + timer_del(s->op_delay); s->op.kind = OP_NONE; @@ -3005,13 +3225,24 @@ static void ot_flash_reset_enter(Object *obj, ResetType type) s->latched_alerts = 0u; + s->lc_broadcast.incoming_signal_bm = 0u; + s->lc_broadcast.incoming_level_bm = 0u; + s->lc_broadcast.current_level_bm = 0u; + s->phase = LC_PHASE_NONE; + /* wipe internal secrets latched on initialisation */ + for (unsigned ix = 0; ix < FLASH_KEYMGR_SECRET_COUNT; ix++) { + memset(s->keymgr_seeds[ix].secret, 0u, OT_FLASH_KEYMGR_SECRET_BYTES); + s->keymgr_seeds[ix].valid = false; + } + ot_flash_update_irqs(s); ot_flash_update_alerts(s); ot_flash_reset_rd_fifo(s); ot_flash_reset_prog_fifo(s); + ot_fifo32_reset(&s->hw_rd_fifo); } static void ot_flash_reset_exit(Object *obj, ResetType type) @@ -3066,6 +3297,7 @@ static void ot_flash_init(Object *obj) s->regs = g_new0(uint32_t, REGS_COUNT); s->csrs = g_new0(uint32_t, CSRS_COUNT); ot_fifo32_create(&s->rd_fifo, OT_FLASH_READ_FIFO_SIZE); + ot_fifo32_create(&s->hw_rd_fifo, FLASH_SEED_WORDS); ot_fifo32_create(&s->prog_fifo, OT_FLASH_PROG_FIFO_SIZE); for (unsigned ix = 0; ix < PARAM_NUM_IRQS; ix++) { @@ -3074,6 +3306,12 @@ static void ot_flash_init(Object *obj) for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); } + + qdev_init_gpio_in_named(DEVICE(obj), &ot_flash_lc_broadcast_recv, + OT_LC_BROADCAST, OT_FLASH_LC_BROADCAST_COUNT); + + s->lc_broadcast.timer = + timer_new_ns(OT_VIRTUAL_CLOCK, &ot_flash_lc_broadcast, s); s->op_delay = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_flash_init_complete, s); } @@ -3091,6 +3329,8 @@ static void ot_flash_class_init(ObjectClass *klass, void *data) resettable_class_set_parent_phases(rc, &ot_flash_reset_enter, NULL, &ot_flash_reset_exit, &fc->parent_phases); + + fc->get_keymgr_secret = &ot_flash_get_keymgr_secret; } static const TypeInfo ot_flash_info = { diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 9272c442d72fd..e09a71ef2cdd0 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -200,11 +200,13 @@ ot_flash_change_lc_phase(const char *phase, int nphase) "[%s:%d]" ot_flash_data_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u addr 0x%06x" ot_flash_erase(bool info_part, unsigned word_addr, unsigned num_bytes) "info_part %u word_addr %u num_bytes %u" ot_flash_error(const char *func, int line, const char *err) "%s:%d %s" +ot_flash_get_keymgr_secret(const char *name, unsigned type) "%s [%d]" ot_flash_info(const char *func, int line, const char *msg, uint32_t value) "%s:%d %s: 0x%08x" ot_flash_info_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned infosel, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u infosel %u addr 0x%06x" ot_flash_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_flash_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_flash_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%08x eff:0x%08x" +ot_flash_lc_broadcast(unsigned sig, bool level) "bcast %u, level %u" ot_flash_mem_read_out(uint32_t addr, unsigned size, uint32_t val, uint32_t pc) "addr=0x%02x (%u), val=0x%08x, pc=0x%x" ot_flash_merge_info_qual(unsigned bank, unsigned infosel, unsigned page, uint8_t reg_cfg, uint8_t qual, uint8_t merged) "bank:%u infosel:%u page:%u, reg_cfg:0x%02x & qual:0x%02x = 0x%02x" ot_flash_op_complete(const char *op, bool hw, bool success) "%s (hw=%u): %u" @@ -212,6 +214,7 @@ ot_flash_op_execute(const char *op, bool hw) "%s (hw=%u)" ot_flash_op_prog(unsigned op_addr, unsigned count, unsigned remaining, bool failed, bool fifo_empty) "op_addr 0x%06x count %u remaining %u failed %u fifo_empty %u" ot_flash_op_start(const char *op, bool hw) "%s (hw=%u)" ot_flash_prog_word(bool info_part, unsigned word_addr, uint32_t word) "info_part %u word_addr %u word %u" +ot_flash_read_keymgr_seed(unsigned page, bool success) "%u: %u" ot_flash_reset_fifo(const char *name) "%s" ot_flash_set_error(const char *op, bool hw, uint32_t err_code, uint32_t err_addr) "%s (hw=%u): err=0x%08x at addr=0x%06x" ot_flash_update_alert(int prev, int next) "%d -> %d" diff --git a/include/hw/opentitan/ot_flash.h b/include/hw/opentitan/ot_flash.h index 59e483bc5230f..dac02939c81d7 100644 --- a/include/hw/opentitan/ot_flash.h +++ b/include/hw/opentitan/ot_flash.h @@ -33,4 +33,52 @@ #define TYPE_OT_FLASH "ot-flash" OBJECT_DECLARE_TYPE(OtFlashState, OtFlashClass, OT_FLASH) +/* Input signals from the lc_ctrl */ +typedef enum { + /* "Indication ... that software is allowed to read/write CREATOR_SEED" */ + OT_FLASH_LC_CREATOR_SEED_SW_RW_EN, + /* "Indication ... that software is allowed to read/write OWNER_SEED" */ + OT_FLASH_LC_OWNER_SEED_SW_RW_EN, + /* "Indication ... that hardware is allowed to read {CREATOR,OWNER}_SEED" */ + OT_FLASH_LC_SEED_HW_RD_EN, + /* "Indication ... that software is allowed to read the isolated part" */ + OT_FLASH_LC_ISO_PART_SW_RD_EN, + /* "Indication ... that software is allowed to write the isolated part" */ + OT_FLASH_LC_ISO_PART_SW_WR_EN, + /* "Escalation indication" - move FSMs into the error state */ + OT_FLASH_LC_ESCALATE_EN, + /* "Indication ... that non-volatile memory debug is allowed" */ + OT_FLASH_LC_NVM_DEBUG_EN, + OT_FLASH_LC_BROADCAST_COUNT, +} OtFlashLcBroadcastType; + +typedef enum { + FLASH_KEYMGR_SECRET_CREATOR_SEED, + FLASH_KEYMGR_SECRET_OWNER_SEED, + FLASH_KEYMGR_SECRET_COUNT +} OtFlashKeyMgrSecretType; + +#define OT_FLASH_KEYMGR_SECRET_BYTES 32u /* 256 bits */ + +typedef struct { + uint8_t secret[OT_FLASH_KEYMGR_SECRET_BYTES]; /* seed data */ + bool valid; /* whether the seed data is valid */ +} OtFlashKeyMgrSecret; + +struct OtFlashClass { + DeviceClass parent_class; + ResettablePhases parent_phases; + + /* + * Retrieve Key Manager secret (seed) from flash. + * + * @s the flash device + * @type the type of secret to retrieve + * @secret the key manager secret record to update + */ + void (*get_keymgr_secret)(const OtFlashState *s, + OtFlashKeyMgrSecretType type, + OtFlashKeyMgrSecret *secret); +}; + #endif /* HW_OPENTITAN_OT_FLASH_H */ From 0292981bccd30f943719604f3dc2449884aa1702 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Sat, 30 Aug 2025 07:04:04 +0100 Subject: [PATCH 3/6] [ot] hw/riscv: ot_earlgrey: Connect lc_ctrl broadcast signals to flash This commit primarily focuses on adding the LC broadcast signal connections to the flash now that it can support many of these signals. It also does a bit of cleanup, re-organising the many signals coming from the lifecycle manager. Since both the OTP and Flash need the `OT_LC_ESCALATE_EN` and `LC_SEED_HW_RD_EN` signals, these are passed through IRQ splitters. Signed-off-by: Alex Jones --- hw/riscv/ot_earlgrey.c | 56 +++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 69e5418e51895..da742e5925959 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -161,6 +161,7 @@ enum OtEGSocDevice { OT_EG_SOC_SPLITTER_LC_HW_DEBUG, OT_EG_SOC_SPLITTER_LC_ESCALATE, OT_EG_SOC_SPLITTER_LC_SEED_HW_RD, + OT_EG_SOC_SPLITTER_LC_CREATOR_SEED_SW_RW, }; enum OtEgResetRequest { @@ -735,23 +736,34 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { /* * TODO: add missing life cycle broadcast signals when the required * supporting HW is available: - * - OT_LC_NVM_DEBUG_EN (for embed. flash) * - OT_LC_KEYMGR_EN (when keymgr is implemented) - * - OT_LC_CREATOR_SEED_SW_RW_EN (for embed. flash) - * - OT_LC_OWNER_SEED_SW_RW_EN (for embed. flash) - * - OT_LC_ISO_PART_SW_RD_EN (for embed. flash) - * - OT_LC_ISO_PART_SW_WR_EN (for embed. flash) - * - OT_LC_SEED_HW_RD_EN (for embed. flash) */ + /* Splitters for signals that go to many blocks. */ OT_EG_SOC_D2S(OT_LC_BROADCAST, OT_LC_HW_DEBUG_EN, LC_HW_DEBUG), OT_EG_SOC_D2S(OT_LC_BROADCAST, OT_LC_ESCALATE_EN, LC_ESCALATE), OT_EG_SOC_D2S(OT_LC_BROADCAST, OT_LC_SEED_HW_RD_EN, LC_SEED_HW_RD), + OT_EG_SOC_D2S(OT_LC_BROADCAST, OT_LC_CREATOR_SEED_SW_RW_EN, + LC_CREATOR_SEED_SW_RW), + /* Signals to ibex_wrapper */ OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_CPU_EN, IBEX_WRAPPER, OT_IBEX_WRAPPER_CPU_EN, OT_IBEX_LC_CTRL_CPU_EN), + /* Signals to flash_ctrl */ + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_OWNER_SEED_SW_RW_EN, + FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_OWNER_SEED_SW_RW_EN), + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_SEED_HW_RD_EN, FLASH_CTRL, + OT_LC_BROADCAST, OT_FLASH_LC_SEED_HW_RD_EN), + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_ISO_PART_SW_RD_EN, + FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_ISO_PART_SW_RD_EN), + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_ISO_PART_SW_WR_EN, + FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_ISO_PART_SW_WR_EN), + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_NVM_DEBUG_EN, FLASH_CTRL, + OT_LC_BROADCAST, OT_FLASH_LC_NVM_DEBUG_EN), + /* Signals to OTP */ OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_CHECK_BYP_EN, OTP_CTRL, - OT_LC_BROADCAST, OT_OTP_LC_CHECK_BYP_EN), - OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_CREATOR_SEED_SW_RW_EN, OTP_CTRL, - OT_LC_BROADCAST, OT_OTP_LC_CREATOR_SEED_SW_RW_EN) + OT_LC_BROADCAST, OT_OTP_LC_CHECK_BYP_EN) ), .link = IBEXDEVICELINKDEFS( OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL), @@ -1377,29 +1389,45 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { [OT_EG_SOC_SPLITTER_LC_HW_DEBUG] = { .type = TYPE_SPLIT_IRQ, .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("num-lines", 1u) // to be changed + IBEX_DEV_UINT_PROP("num-lines", 1u) /** @todo to be changed */ ) }, [OT_EG_SOC_SPLITTER_LC_ESCALATE] = { .type = TYPE_SPLIT_IRQ, .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_S2D(0, OTP_CTRL, OT_LC_BROADCAST, - OT_OTP_LC_ESCALATE_EN) + OT_OTP_LC_ESCALATE_EN), + OT_EG_SOC_S2D(1, FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_ESCALATE_EN) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("num-lines", 1u) // to be changed + IBEX_DEV_UINT_PROP("num-lines", 2u) /** @todo to be changed */ ) }, [OT_EG_SOC_SPLITTER_LC_SEED_HW_RD] = { .type = TYPE_SPLIT_IRQ, .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_S2D(0, OTP_CTRL, OT_LC_BROADCAST, - OT_OTP_LC_SEED_HW_RD_EN) + OT_OTP_LC_SEED_HW_RD_EN), + OT_EG_SOC_S2D(1, FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_SEED_HW_RD_EN) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("num-lines", 2u) ) - } + }, + [OT_EG_SOC_SPLITTER_LC_CREATOR_SEED_SW_RW] = { + .type = TYPE_SPLIT_IRQ, + .gpio = IBEXGPIOCONNDEFS( + OT_EG_SOC_S2D(0, OTP_CTRL, OT_LC_BROADCAST, + OT_OTP_LC_CREATOR_SEED_SW_RW_EN), + OT_EG_SOC_S2D(1, FLASH_CTRL, OT_LC_BROADCAST, + OT_FLASH_LC_CREATOR_SEED_SW_RW_EN) + ), + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("num-lines", 2u) + ) + }, /* clang-format on */ }; From e82a76b51f1944e62d22f819c854a0bde67e1885 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Sat, 30 Aug 2025 12:20:14 +0100 Subject: [PATCH 4/6] [ot] hw/opentitan: ot_flash: Add device ID to traces and logs To bring the flash controller more in line with other modernized QEMU IP, add an `ot_id` property which is added as context to all traces and logs. This is primarily useful if you have multiple instances of a block (e.g. if we had a future top with multiple flash controllers) or if you want to rename a device, but is added regardless to be consistent with other OpenTitan QEMU devices. All traces are updated to start with the ID, and all of the qemu masked logs and errors also include the ID. Signed-off-by: Alex Jones --- hw/opentitan/ot_flash.c | 341 +++++++++++++++++++++----------------- hw/opentitan/trace-events | 48 +++--- 2 files changed, 212 insertions(+), 177 deletions(-) diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index 4b08645699ba2..45842c70b32e6 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -730,10 +730,10 @@ static const char *FLASH_KEYMGR_SECRET_NAMES[] = { * (InfoPageOwnerCerificate1, 0xec309461, 1, 9) */ -#define xtrace_ot_flash_error(_msg_) \ - trace_ot_flash_error(__func__, __LINE__, _msg_) -#define xtrace_ot_flash_info(_msg_, _val_) \ - trace_ot_flash_info(__func__, __LINE__, _msg_, _val_) +#define xtrace_ot_flash_error(_id_, _msg_) \ + trace_ot_flash_error(_id_, __func__, __LINE__, _msg_) +#define xtrace_ot_flash_info(_id_, _msg_, _val_) \ + trace_ot_flash_info(_id_, __func__, __LINE__, _msg_, _val_) enum { BIN_APP_OTRE, @@ -866,6 +866,7 @@ struct OtFlashState { */ OtFifo32 hw_rd_fifo; + char *ot_id; BlockBackend *blk; /* Flash backend */ OtVMapperState *vmapper; /* to disable execution from flash */ bool no_mem_prot; /* Flag to disable mem protection features */ @@ -954,7 +955,8 @@ static const OtFlashHwInfoPageRule OT_FLASH_HW_INFO_PAGE_RULES[] = { static void ot_flash_update_irqs(OtFlashState *s) { uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; - trace_ot_flash_irqs(s->regs[R_INTR_STATE], s->regs[R_INTR_ENABLE], level); + trace_ot_flash_irqs(s->ot_id, s->regs[R_INTR_STATE], s->regs[R_INTR_ENABLE], + level); for (unsigned ix = 0u; ix < PARAM_NUM_IRQS; ix++) { ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1u)); } @@ -969,7 +971,8 @@ static void ot_flash_update_alerts(OtFlashState *s) for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { int level = (int)((levels >> ix) & 0x1u); if (level != ibex_irq_get_level(&s->alerts[ix])) { - trace_ot_flash_update_alert(ibex_irq_get_level(&s->alerts[ix]), + trace_ot_flash_update_alert(s->ot_id, + ibex_irq_get_level(&s->alerts[ix]), level); } ibex_irq_set(&s->alerts[ix], level); @@ -983,7 +986,8 @@ static void ot_flash_update_alerts(OtFlashState *s) for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { int level = (int)((levels >> ix) & 0x1u); if (level != ibex_irq_get_level(&s->alerts[ix])) { - trace_ot_flash_update_alert(ibex_irq_get_level(&s->alerts[ix]), + trace_ot_flash_update_alert(s->ot_id, + ibex_irq_get_level(&s->alerts[ix]), level); } ibex_irq_set(&s->alerts[ix], level); @@ -1031,7 +1035,7 @@ static void ot_flash_update_rd_watermark(OtFlashState *s) } else { s->regs[R_INTR_STATE] &= ~INTR_RD_LVL_MASK; } - trace_ot_flash_update_rd_watermark(lvl, rd_watermark_level); + trace_ot_flash_update_rd_watermark(s->ot_id, lvl, rd_watermark_level); ot_flash_update_irqs(s); } @@ -1048,7 +1052,7 @@ static void ot_flash_update_prog_watermark(OtFlashState *s) } else { s->regs[R_INTR_STATE] &= ~INTR_PROG_LVL_MASK; } - trace_ot_flash_update_prog_watermark(lvl, prog_watermark_level); + trace_ot_flash_update_prog_watermark(s->ot_id, lvl, prog_watermark_level); ot_flash_update_irqs(s); } @@ -1083,7 +1087,7 @@ static void ot_flash_reset_rd_fifo(OtFlashState *s) s->regs[R_STATUS] |= R_STATUS_RD_EMPTY_MASK; s->regs[R_STATUS] &= ~R_STATUS_RD_FULL_MASK; s->regs[R_INTR_STATE] &= ~INTR_RD_FULL_MASK; - trace_ot_flash_reset_fifo("rd"); + trace_ot_flash_reset_fifo(s->ot_id, "rd"); ot_flash_update_rd_watermark(s); } @@ -1093,7 +1097,7 @@ static void ot_flash_reset_prog_fifo(OtFlashState *s) s->regs[R_STATUS] |= R_STATUS_PROG_EMPTY_MASK; s->regs[R_STATUS] &= ~R_STATUS_PROG_FULL_MASK; s->regs[R_INTR_STATE] |= INTR_PROG_EMPTY_MASK; - trace_ot_flash_reset_fifo("prog"); + trace_ot_flash_reset_fifo(s->ot_id, "prog"); ot_flash_update_prog_watermark(s); } @@ -1113,7 +1117,8 @@ static void ot_flash_set_error(OtFlashState *s, uint32_t ebit, uint32_t eaddr) } ot_flash_update_alerts(s); } - trace_ot_flash_set_error(OP_NAME(s->op.kind), s->op.hw, ebit, eaddr); + trace_ot_flash_set_error(s->ot_id, OP_NAME(s->op.kind), s->op.hw, ebit, + eaddr); } static void ot_flash_op_complete(OtFlashState *s) @@ -1130,13 +1135,13 @@ static void ot_flash_op_complete(OtFlashState *s) ot_flash_reset_prog_fifo(s); } s->regs[R_CTRL_REGWEN] |= R_CTRL_REGWEN_EN_MASK; - trace_ot_flash_op_complete(OP_NAME(s->op.kind), s->op.hw, + trace_ot_flash_op_complete(s->ot_id, OP_NAME(s->op.kind), s->op.hw, !s->regs[R_ERR_CODE]); s->op.kind = OP_NONE; } static uint32_t ot_flash_get_info_page_cfg_reg( - unsigned bank, unsigned info_partition, unsigned page) + OtFlashState *s, unsigned bank, unsigned info_partition, unsigned page) { switch (bank) { case 0u: @@ -1148,8 +1153,9 @@ static uint32_t ot_flash_get_info_page_cfg_reg( case 2u: return R_BANK0_INFO2_PAGE_CFG_0 + page; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid info partition: %u\n", - __func__, info_partition); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: invalid info partition: %u\n", __func__, + s->ot_id, info_partition); return 0u; } case 1u: @@ -1161,13 +1167,14 @@ static uint32_t ot_flash_get_info_page_cfg_reg( case 2u: return R_BANK1_INFO2_PAGE_CFG_0 + page; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid info partition: %u\n", - __func__, info_partition); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: invalid info partition: %u\n", __func__, + s->ot_id, info_partition); return 0u; } default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__, - bank); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid bank: %d\n", __func__, + s->ot_id, bank); return 0u; } } @@ -1284,8 +1291,8 @@ static void ot_flash_update_info_page_qualification( /* merge the reg cfg and the page qualifications */ uint8_t prev_cfg = cfg->bitmap; cfg->bitmap &= qual.bitmap; - trace_ot_flash_merge_info_qual(bank, info_partition, page, prev_cfg, - qual.bitmap, cfg->bitmap); + trace_ot_flash_merge_info_qual(s->ot_id, bank, info_partition, page, + prev_cfg, qual.bitmap, cfg->bitmap); } static bool ot_flash_info_page_cfg_op_enabled(const OtFlashState *s, @@ -1299,10 +1306,10 @@ static bool ot_flash_info_page_cfg_op_enabled(const OtFlashState *s, case OP_ERASE: return cfg->erase_en; case OP_NONE: - xtrace_ot_flash_error("cannot check mp without operation"); + xtrace_ot_flash_error(s->ot_id, "cannot check mp without operation"); return false; default: - xtrace_ot_flash_error("unsupported operation?"); + xtrace_ot_flash_error(s->ot_id, "unsupported operation?"); return false; } } @@ -1324,10 +1331,10 @@ ot_flash_mp_region_cfg_op_enabled(const OtFlashState *s, uint32_t mp_cfg_reg) SHARED_FIELD_EX32(s->regs[mp_cfg_reg], MP_REGION_CFG_ERASE_EN); break; case OP_NONE: - xtrace_ot_flash_error("cannot check mp without operation"); + xtrace_ot_flash_error(s->ot_id, "cannot check mp without operation"); return false; default: - xtrace_ot_flash_error("unsupported operation?"); + xtrace_ot_flash_error(s->ot_id, "unsupported operation?"); return false; } return en_field == OT_MULTIBITBOOL4_TRUE; @@ -1349,10 +1356,10 @@ static bool ot_flash_default_region_cfg_op_enabled(const OtFlashState *s) FIELD_EX32(s->regs[R_DEFAULT_REGION], DEFAULT_REGION, ERASE_EN); break; case OP_NONE: - xtrace_ot_flash_error("cannot check mp without operation"); + xtrace_ot_flash_error(s->ot_id, "cannot check mp without operation"); return false; default: - xtrace_ot_flash_error("unsupported operation?"); + xtrace_ot_flash_error(s->ot_id, "unsupported operation?"); return false; } return en_field == OT_MULTIBITBOOL4_TRUE; @@ -1369,8 +1376,8 @@ static bool ot_flash_can_erase_bank(const OtFlashState *s, unsigned bank) MP_BANK_CFG_SHADOWED, ERASE_EN_1); default: qemu_log_mask(LOG_GUEST_ERROR, - "%s: unknown bank %u for bank erase operation", __func__, - bank); + "%s: %s: unknown bank %u for bank erase operation", + __func__, s->ot_id, bank); return false; } } @@ -1388,8 +1395,8 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) unsigned op_address = s->op.address + op_offset; if (info_partition >= storage->info_part_count) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid info partition: %u\n", - __func__, s->op.info_sel); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid info partition: %u\n", + __func__, s->ot_id, s->op.info_sel); ot_flash_set_error(s, mp_err_ebit, op_address); s->op.failed = true; return op_address; @@ -1398,8 +1405,8 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) /* extract the bank & bank-relative address from the address */ unsigned bank = op_address / bank_size; if (bank >= storage->bank_count) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__, - bank); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid bank: %d\n", __func__, + s->ot_id, bank); ot_flash_set_error(s, mp_err_ebit, op_address); s->op.failed = true; return op_address; @@ -1411,8 +1418,8 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) * the RTL only errors when it attempts to read an invalid address. */ qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid address in partition: %u %u\n", __func__, - op_address, info_partition); + "%s: %s: invalid address in partition: %u %u\n", __func__, + s->ot_id, op_address, info_partition); ot_flash_set_error(s, mp_err_ebit, op_address); s->op.failed = true; return op_address; @@ -1422,8 +1429,8 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) unsigned bank_offset = bank * storage->info_size; unsigned info_part_offset = storage->info_parts[info_partition].offset; unsigned address = address_in_bank + bank_offset + info_part_offset; - trace_ot_flash_info_part(s->op.address, s->op.count, s->op.remaining, bank, - info_partition, address); + trace_ot_flash_info_part(s->ot_id, s->op.address, s->op.count, + s->op.remaining, bank, info_partition, address); if (s->no_mem_prot || (s->op.kind == OP_ERASE && s->op.erase_sel == ERASE_SEL_BANK)) { return address; @@ -1450,7 +1457,7 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) cfg.ecc_en &= !ecc_disable; } else { uint32_t info_page_cfg_reg = - ot_flash_get_info_page_cfg_reg(bank, info_partition, page); + ot_flash_get_info_page_cfg_reg(s, bank, info_partition, page); if (!info_page_cfg_reg) { ot_flash_set_error(s, mp_err_ebit, op_address); s->op.failed = true; @@ -1463,11 +1470,12 @@ static unsigned ot_flash_next_info_address(OtFlashState *s) if (!cfg.en || !ot_flash_info_page_cfg_op_enabled(s, &cfg)) { const char *op_type = (s->op.hw) ? "hardware" : "software"; - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s operation %s on info page %u in partition %u of " - "bank %u is disabled by page config\n", - __func__, op_type, OP_NAME(s->op.kind), page, bank, - info_partition); + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: %s operation %s on info page %u in partition %u of " + "bank %u is disabled by page config\n", + __func__, s->ot_id, op_type, OP_NAME(s->op.kind), page, bank, + info_partition); ot_flash_set_error(s, mp_err_ebit, op_address); s->op.failed = true; return address; @@ -1488,14 +1496,14 @@ static unsigned ot_flash_next_data_address(OtFlashState *s) unsigned address = s->op.address + op_offset; unsigned bank = address / bank_size; if (bank >= storage->bank_count) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid bank: %d\n", __func__, - bank); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid bank: %d\n", __func__, + s->ot_id, bank); ot_flash_set_error(s, mp_err_ebit, address); s->op.failed = true; return address; } - trace_ot_flash_data_part(s->op.address, s->op.count, s->op.remaining, bank, - address); + trace_ot_flash_data_part(s->ot_id, s->op.address, s->op.count, + s->op.remaining, bank, address); if (s->no_mem_prot || (s->op.kind == OP_ERASE && s->op.erase_sel == ERASE_SEL_BANK)) { @@ -1512,9 +1520,9 @@ static unsigned ot_flash_next_data_address(OtFlashState *s) } qemu_log_mask( LOG_GUEST_ERROR, - "%s: hardware operation %s on data pages is not permitted " + "%s: %s: hardware operation %s on data pages is not permitted " "in the %s flash_ctrl lc phase\n", - __func__, OP_NAME(s->op.kind), LC_PHASE_NAME(s->phase)); + __func__, s->ot_id, OP_NAME(s->op.kind), LC_PHASE_NAME(s->phase)); ot_flash_set_error(s, mp_err_ebit, address); s->op.failed = true; return address; @@ -1557,9 +1565,9 @@ static unsigned ot_flash_next_data_address(OtFlashState *s) if (!ot_flash_mp_region_cfg_op_enabled(s, r_region_cfg)) { qemu_log_mask( LOG_GUEST_ERROR, - "%s: software operation %s on page %u of data partition " + "%s: %s: software operation %s on page %u of data partition " "in bank %u is disabled by MP region %u\n", - __func__, OP_NAME(s->op.kind), page, bank, region); + __func__, s->ot_id, OP_NAME(s->op.kind), page, bank, region); ot_flash_set_error(s, mp_err_ebit, address); s->op.failed = true; return address; @@ -1569,10 +1577,11 @@ static unsigned ot_flash_next_data_address(OtFlashState *s) /* If page not in any region, apply the default region's permissions. */ if (!matching_region_found && !ot_flash_default_region_cfg_op_enabled(s)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: software operation %s on page %u of data partition " - "in bank %u is disabled by default region\n", - __func__, OP_NAME(s->op.kind), page, bank); + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: software operation %s on page %u of data partition " + "in bank %u is disabled by default region\n", + __func__, s->ot_id, OP_NAME(s->op.kind), page, bank); ot_flash_set_error(s, mp_err_ebit, address); s->op.failed = true; return address; @@ -1586,7 +1595,7 @@ static void ot_flash_op_read(OtFlashState *s) g_assert(rd_fifo); if (ot_fifo32_is_full(rd_fifo)) { - xtrace_ot_flash_error("read while RD FIFO full"); + xtrace_ot_flash_error(s->ot_id, "read while RD FIFO full"); return; } @@ -1630,7 +1639,7 @@ static void ot_flash_op_read(OtFlashState *s) static void ot_flash_op_prog(OtFlashState *s) { if (ot_fifo32_is_empty(&s->prog_fifo)) { - xtrace_ot_flash_error("prog while prog FIFO empty"); + xtrace_ot_flash_error(s->ot_id, "prog while prog FIFO empty"); return; } @@ -1655,8 +1664,8 @@ static void ot_flash_op_prog(OtFlashState *s) * the prog FIFO regardless, hence we retrieve the word to program * before checking for errors. */ - trace_ot_flash_op_prog(s->op.address, s->op.count, s->op.remaining, - s->op.failed, fifo_empty); + trace_ot_flash_op_prog(s->ot_id, s->op.address, s->op.count, + s->op.remaining, s->op.failed, fifo_empty); if (s->op.failed) { if (!fifo_empty) { continue; @@ -1672,14 +1681,15 @@ static void ot_flash_op_prog(OtFlashState *s) ((s->op.info_part ? storage->info_size : storage->data_size) * storage->bank_count)); dest[address] &= word; - trace_ot_flash_prog_word(s->op.info_part, address, word); + trace_ot_flash_prog_word(s->ot_id, s->op.info_part, address, word); if (ot_flash_is_backend_writable(s)) { uintptr_t dest_offset = (uintptr_t)dest - (uintptr_t)storage->data; if (ot_flash_write_backend(s, &dest[address], (unsigned)(dest_offset + address), sizeof(uint32_t))) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: cannot update flash backend\n", __func__); + "%s: %s: cannot update flash backend\n", __func__, + s->ot_id); uint32_t ebit = s->op.hw ? R_FAULT_STATUS_PROG_ERR_MASK : R_ERR_CODE_PROG_ERR_MASK; ot_flash_set_error(s, ebit, address * sizeof(uint32_t)); @@ -1712,14 +1722,15 @@ static void ot_flash_op_erase_page(OtFlashState *s, unsigned address) ((s->op.info_part ? storage->info_size : storage->data_size) * storage->bank_count)); memset(&dest[page_address], 0xFFu, page_size); - trace_ot_flash_erase(s->op.info_part, page_address, page_size); + trace_ot_flash_erase(s->ot_id, s->op.info_part, page_address, page_size); if (ot_flash_is_backend_writable(s)) { uintptr_t dest_offset = (uintptr_t)dest - (uintptr_t)storage->data; if (ot_flash_write_backend(s, &dest[page_address], (unsigned)(dest_offset + page_address), page_size)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot update flash backend\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: cannot update flash backend\n", __func__, + s->ot_id); uint32_t ebit = s->op.hw ? R_FAULT_STATUS_PROG_ERR_MASK : R_ERR_CODE_PROG_ERR_MASK; ot_flash_set_error(s, ebit, address * sizeof(uint32_t)); @@ -1741,8 +1752,8 @@ static void ot_flash_op_erase_bank(OtFlashState *s, unsigned address) if (!ot_flash_can_erase_bank(s, bank)) { qemu_log_mask( LOG_GUEST_ERROR, - "%s: cannot erase bank %u when bank-wide erase not enabled\n", - __func__, bank); + "%s: %s: cannot erase bank %u when bank-wide erase not enabled\n", + __func__, s->ot_id, bank); uint32_t ebit = s->op.hw ? R_FAULT_STATUS_MP_ERR_MASK : R_ERR_CODE_MP_ERR_MASK; ot_flash_set_error(s, ebit, address); @@ -1763,20 +1774,21 @@ static void ot_flash_op_erase_bank(OtFlashState *s, unsigned address) g_assert((data_address + bank_size) <= (storage->data_size * storage->bank_count)); memset(&storage->data[data_address], 0xFFu, bank_size); - trace_ot_flash_erase(s->op.info_part, data_address, bank_size); + trace_ot_flash_erase(s->ot_id, s->op.info_part, data_address, + bank_size); } else { info_address = bank_address; g_assert((info_address + bank_size) <= (storage->info_size * storage->bank_count)); memset(&storage->info[info_address], 0xFFu, bank_size); - trace_ot_flash_erase(true, info_address, bank_size); + trace_ot_flash_erase(s->ot_id, true, info_address, bank_size); bank_size = storage->data_size; data_address = bank_size * bank / sizeof(uint32_t); g_assert((data_address + bank_size) <= (storage->data_size * storage->bank_count)); memset(&storage->data[data_address], 0xFFu, bank_size); - trace_ot_flash_erase(false, data_address, bank_size); + trace_ot_flash_erase(s->ot_id, false, data_address, bank_size); } if (ot_flash_is_backend_writable(s)) { @@ -1795,8 +1807,9 @@ static void ot_flash_op_erase_bank(OtFlashState *s, unsigned address) } if (data_write_err || info_write_err) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot update flash backend\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: cannot update flash backend\n", __func__, + s->ot_id); uint32_t ebit = s->op.hw ? R_FAULT_STATUS_PROG_ERR_MASK : R_ERR_CODE_PROG_ERR_MASK; ot_flash_set_error(s, ebit, address * sizeof(uint32_t)); @@ -1855,20 +1868,20 @@ static void ot_flash_op_execute(OtFlashState *s) { switch (s->op.kind) { case OP_READ: - trace_ot_flash_op_execute(OP_NAME(s->op.kind), s->op.hw); + trace_ot_flash_op_execute(s->ot_id, OP_NAME(s->op.kind), s->op.hw); ot_flash_op_read(s); break; case OP_PROG: - trace_ot_flash_op_execute(OP_NAME(s->op.kind), s->op.hw); + trace_ot_flash_op_execute(s->ot_id, OP_NAME(s->op.kind), s->op.hw); ot_flash_op_prog(s); break; case OP_ERASE: - trace_ot_flash_op_execute(OP_NAME(s->op.kind), s->op.hw); + trace_ot_flash_op_execute(s->ot_id, OP_NAME(s->op.kind), s->op.hw); ot_flash_op_erase(s); break; default: s->regs[R_CTRL_REGWEN] |= R_CTRL_REGWEN_EN_MASK; - xtrace_ot_flash_error("unsupported"); + xtrace_ot_flash_error(s->ot_id, "unsupported"); break; } @@ -1880,7 +1893,7 @@ static void ot_flash_op_execute(OtFlashState *s) static void ot_flash_op_start(OtFlashState *s) { - trace_ot_flash_op_start(OP_NAME(s->op.kind), s->op.hw); + trace_ot_flash_op_start(s->ot_id, OP_NAME(s->op.kind), s->op.hw); s->regs[R_CTRL_REGWEN] &= ~R_CTRL_REGWEN_EN_MASK; if (s->op.hw) { @@ -1921,7 +1934,7 @@ static void ot_flash_read_keymgr_seed(OtFlashState *s, unsigned page, seed->valid = !s->op.failed; /* todo: dump the seed in the trace? */ - trace_ot_flash_read_keymgr_seed(page, !s->op.failed); + trace_ot_flash_read_keymgr_seed(s->ot_id, page, !s->op.failed); if (s->op.failed) { ot_flash_set_error(s, R_FAULT_STATUS_SEED_ERR_MASK, s->op.address); @@ -1933,28 +1946,29 @@ static void ot_flash_initialize(OtFlashState *s) bool initialized = (bool)FIELD_EX32(s->regs[R_STATUS], STATUS, INITIALIZED); bool init_wip = (bool)FIELD_EX32(s->regs[R_STATUS], STATUS, INIT_WIP); if (ot_flash_in_operation(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: cannot initialize while in op", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot initialize while in op", + __func__, s->ot_id); return; } if (initialized) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: initialize is meaningless when already initialized", - __func__); + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: initialize is meaningless when already initialized", + __func__, s->ot_id); return; } if (init_wip) { qemu_log_mask( LOG_GUEST_ERROR, - "%s: initialize is meaningless when currently initializing", - __func__); + "%s: %s: initialize is meaningless when currently initializing", + __func__, s->ot_id); return; } /* Start the INIT operation. */ s->op.kind = OP_INIT; s->op.hw = false; - trace_ot_flash_op_start(OP_NAME(s->op.kind), s->op.hw); + trace_ot_flash_op_start(s->ot_id, OP_NAME(s->op.kind), s->op.hw); s->regs[R_STATUS] = FIELD_DP32(s->regs[R_STATUS], STATUS, INIT_WIP, 1u); s->regs[R_PHY_STATUS] = FIELD_DP32(s->regs[R_PHY_STATUS], PHY_STATUS, INIT_WIP, 1u); @@ -1975,7 +1989,8 @@ static void ot_flash_initialize(OtFlashState *s) if (seed_hw_rd_en) { /* Read & latch seeds stored in flash on initialisation */ s->phase = LC_PHASE_SEED; - trace_ot_flash_change_lc_phase(LC_PHASE_NAME(s->phase), s->phase); + trace_ot_flash_change_lc_phase(s->ot_id, LC_PHASE_NAME(s->phase), + s->phase); ot_flash_read_keymgr_seed( s, FLASH_QUAL_INFO_PAGE_CREATOR, @@ -1986,7 +2001,8 @@ static void ot_flash_initialize(OtFlashState *s) } else { /* TODO: Lock up & wait for RMA entry to reseed and then wipe */ s->phase = LC_PHASE_NONE; - trace_ot_flash_change_lc_phase(LC_PHASE_NAME(s->phase), s->phase); + trace_ot_flash_change_lc_phase(s->ot_id, LC_PHASE_NAME(s->phase), + s->phase); /* TODO: should this still complete the init operation? */ } @@ -2015,10 +2031,10 @@ static bool ot_flash_check_program_resolution(OtFlashState *s) unsigned end_window = end_address / BUS_PGM_RES; if (start_window != end_window) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: program resolution error for addr=%u, count=%u " + "%s: %s: program resolution error for addr=%u, count=%u " "(start_window=%u, end_window=%u)\n", - __func__, s->op.address, s->op.count, start_window, - end_window); + __func__, s->ot_id, s->op.address, s->op.count, + start_window, end_window); ot_flash_set_error(s, R_ERR_CODE_PROG_WIN_ERR_MASK, s->op.address); ot_flash_op_complete(s); return false; @@ -2035,8 +2051,8 @@ static bool ot_flash_check_program_type(OtFlashState *s) en_mask = R_PROG_TYPE_EN_REPAIR_MASK; } if (!(s->regs[R_PROG_TYPE_EN] & en_mask)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: program type not enabled: %s\n", - __func__, PROGRAM_NAME(s->op.prog_sel)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: program type not enabled: %s\n", + __func__, s->ot_id, PROGRAM_NAME(s->op.prog_sel)); ot_flash_set_error(s, R_ERR_CODE_PROG_TYPE_ERR_MASK, s->op.address); ot_flash_op_complete(s); return false; @@ -2068,8 +2084,8 @@ static void ot_flash_process_control_op(OtFlashState *s) * flash protocol controller MP errors back all controller initiated ops. */ if (ot_flash_is_disabled(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: flash has been disabled\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: flash has been disabled\n", + __func__, s->ot_id); ot_flash_set_error(s, R_ERR_CODE_MP_ERR_MASK, 0u); return; } @@ -2080,7 +2096,7 @@ static void ot_flash_process_control_op(OtFlashState *s) s->op.address = WORD_ALIGN_ADDR(s->regs[R_ADDR]); s->op.info_part = part_sel; s->op.info_sel = info_sel; - xtrace_ot_flash_info("Read from", s->op.address); + xtrace_ot_flash_info(s->ot_id, "Read from", s->op.address); s->op.count = num + 1u; break; case CONTROL_OP_PROG: @@ -2098,7 +2114,7 @@ static void ot_flash_process_control_op(OtFlashState *s) !ot_flash_check_program_type(s)) { break; } - xtrace_ot_flash_info("Write to", s->op.address); + xtrace_ot_flash_info(s->ot_id, "Write to", s->op.address); break; case CONTROL_OP_ERASE: s->op.kind = OP_ERASE; @@ -2108,11 +2124,11 @@ static void ot_flash_process_control_op(OtFlashState *s) s->op.erase_sel = (bool)erase_sel; /* Erase ops neither go through FIFOs nor use/require a word count */ s->op.count = 0u; - xtrace_ot_flash_info("Erase at", s->op.address); + xtrace_ot_flash_info(s->ot_id, "Erase at", s->op.address); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Operation %u (%s) is invalid\n", - __func__, op, CONTROL_OP_NAME(op)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Operation %u (%s) is invalid\n", + __func__, s->ot_id, op, CONTROL_OP_NAME(op)); ot_flash_set_error(s, R_ERR_CODE_OP_ERR_MASK, 0u); ot_flash_op_complete(s); return; @@ -2131,7 +2147,7 @@ static void ot_flash_init_complete(void *opaque) s->regs[R_PHY_STATUS] = FIELD_DP32(s->regs[R_PHY_STATUS], PHY_STATUS, INIT_WIP, 0u); - trace_ot_flash_op_complete(OP_NAME(s->op.kind), s->op.hw, true); + trace_ot_flash_op_complete(s->ot_id, OP_NAME(s->op.kind), s->op.hw, true); s->op.kind = OP_NONE; ot_flash_process_control_op(s); @@ -2283,12 +2299,12 @@ static uint64_t ot_flash_regs_read(void *opaque, hwaddr addr, unsigned size) */ if (!ot_flash_is_initialized(s)) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bus stalling on reads from uninit FIFO are " - "not supported in QEMU\n", - __func__); + "%s: %s: Bus stalling on reads from uninit FIFO " + "are not supported in QEMU\n", + __func__, s->ot_id); } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Read empty FIFO\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Read empty FIFO\n", + __func__, s->ot_id); } val32 = 0u; } @@ -2302,19 +2318,21 @@ static uint64_t ot_flash_regs_read(void *opaque, hwaddr addr, unsigned size) case R_ALERT_TEST: case R_PROG_FIFO: qemu_log_mask(LOG_GUEST_ERROR, - "%s: W/O register 0x%03" HWADDR_PRIx " (%s)\n", __func__, - addr, REG_NAME(reg)); + "%s: %s: W/O register 0x%03" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); val32 = 0; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); val32 = 0; break; } uint32_t pc = ibex_get_current_pc(); - trace_ot_flash_io_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_flash_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); return (uint64_t)val32; }; @@ -2329,16 +2347,17 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, hwaddr reg = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_flash_io_write((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_flash_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, pc); switch (reg) { case R_INTR_STATE: { uint32_t rw1c_mask = INTR_CORR_ERR_MASK | INTR_OP_DONE_MASK; if (val32 & ~rw1c_mask) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to R/O field in register 0x%03" HWADDR_PRIx - " (%s)\n", - __func__, addr, REG_NAME(reg)); + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: Write to R/O field in register 0x%03" HWADDR_PRIx + " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); } val32 &= rw1c_mask; s->regs[reg] &= ~val32; @@ -2364,7 +2383,7 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, val32 &= R_DIS_VAL_MASK; s->regs[reg] = ot_multibitbool_w1s_write(s->regs[reg], val32, 4u); if (ot_flash_is_disabled(s)) { - xtrace_ot_flash_error("flash controller disabled by SW"); + xtrace_ot_flash_error(s->ot_id, "flash controller disabled by SW"); memory_region_set_enabled(&s->mmio.mem, false); } break; @@ -2382,8 +2401,9 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, case R_CONTROL: if (!(s->regs[R_CTRL_REGWEN] & R_CTRL_REGWEN_EN_MASK)) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s is not enabled, so %s is protected\n", - __func__, REG_NAME(R_CTRL_REGWEN), REG_NAME(reg)); + "%s: %s: %s is not enabled, so %s is protected\n", + __func__, s->ot_id, REG_NAME(R_CTRL_REGWEN), + REG_NAME(reg)); break; } @@ -2600,8 +2620,8 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, * operation has been initiated via the `CONTROL` register." */ qemu_log_mask(LOG_GUEST_ERROR, - "%s: write prog fifo when not in a sw prog op\n", - __func__); + "%s: %s: write prog fifo when not in a sw prog op\n", + __func__, s->ot_id); break; } if (!ot_fifo32_is_full(&s->prog_fifo)) { @@ -2619,17 +2639,19 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, ot_flash_op_execute(s); } } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Write full FIFO\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Write full FIFO\n", + __func__, s->ot_id); } break; case R_FAULT_STATUS: { uint32_t rw0c_mask = (R_FAULT_STATUS_PHY_RELBL_ERR_MASK | R_FAULT_STATUS_PHY_STORAGE_ERR_MASK); if (val32 & ~rw0c_mask) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to R/O field in register 0x%03" HWADDR_PRIx - " (%s)\n", - __func__, addr, REG_NAME(reg)); + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: Write to R/O field in register 0x%03" HWADDR_PRIx + " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); } val32 &= rw0c_mask; val32 |= ~rw0c_mask; @@ -2647,12 +2669,13 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, case R_PHY_STATUS: case R_CURR_FIFO_LVL: qemu_log_mask(LOG_GUEST_ERROR, - "%s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", __func__, - addr, REG_NAME(reg)); + "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); break; } }; @@ -2690,14 +2713,16 @@ static uint64_t ot_flash_csrs_read(void *opaque, hwaddr addr, unsigned size) val32 = s->csrs[csr]; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); val32 = 0; break; } uint32_t pc = ibex_get_current_pc(); - trace_ot_flash_io_read_out((uint32_t)addr, CSR_NAME(csr), val32, pc); + trace_ot_flash_io_read_out(s->ot_id, (uint32_t)addr, CSR_NAME(csr), val32, + pc); return (uint64_t)val32; }; @@ -2712,7 +2737,7 @@ static void ot_flash_csrs_write(void *opaque, hwaddr addr, uint64_t val64, hwaddr csr = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_flash_io_write((uint32_t)addr, CSR_NAME(csr), val32, pc); + trace_ot_flash_io_write(s->ot_id, (uint32_t)addr, CSR_NAME(csr), val32, pc); bool enable = s->csrs[R_CSR0_REGWEN] & R_CSR0_REGWEN_FIELD0_MASK; switch (csr) { @@ -2784,8 +2809,9 @@ static void ot_flash_csrs_write(void *opaque, hwaddr addr, uint64_t val64, break; default: enable = false; - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); break; } @@ -2833,7 +2859,7 @@ static void ot_flash_lc_broadcast(void *opaque) bcast->current_level_bm |= (bcast->incoming_level_bm & signal_mask); bool level = (bool)(bcast->current_level_bm & signal_mask); - trace_ot_flash_lc_broadcast(signal, level); + trace_ot_flash_lc_broadcast(s->ot_id, signal, level); switch (signal) { case OT_FLASH_LC_SEED_HW_RD_EN: @@ -2847,18 +2873,19 @@ static void ot_flash_lc_broadcast(void *opaque) /* flash disabling is detected from latch in current_level */ /* todo: also change the flash lcmgr lc_state? */ if (s->fatal_escalate) { - error_setg(&error_fatal, "%s: Flash LC escalate", __func__); + error_setg(&error_fatal, "%s: %s: Flash LC escalate", __func__, + s->ot_id); } break; case OT_FLASH_LC_NVM_DEBUG_EN: qemu_log_mask( LOG_UNIMP, - "%s: lc_nvm_debug_en for JTAG connection is ignored\n", - __func__); + "%s: %s: lc_nvm_debug_en for JTAG connection is ignored\n", + __func__, s->ot_id); break; default: - error_setg(&error_fatal, "%s: unexpected LC broadcast %d", __func__, - signal); + error_setg(&error_fatal, "%s: %s: unexpected LC broadcast %d", + __func__, s->ot_id, signal); g_assert_not_reached(); break; } @@ -2869,7 +2896,8 @@ static void ot_flash_get_keymgr_secret(const OtFlashState *s, OtFlashKeyMgrSecretType type, OtFlashKeyMgrSecret *secret) { - trace_ot_flash_get_keymgr_secret(FLASH_KEYMGR_SECRET_NAME(type), type); + trace_ot_flash_get_keymgr_secret(s->ot_id, FLASH_KEYMGR_SECRET_NAME(type), + type); switch (type) { case FLASH_KEYMGR_SECRET_CREATOR_SEED: @@ -2880,8 +2908,8 @@ static void ot_flash_get_keymgr_secret(const OtFlashState *s, secret->valid = !invalid_seed; return; default: - error_report("%s: invalid flash keymgr secret type: %d", __func__, - type); + error_report("%s: %s: invalid flash keymgr secret type: %d", __func__, + s->ot_id, type); secret->valid = false; memset(secret->secret, 0u, OT_FLASH_KEYMGR_SECRET_BYTES); return; @@ -2915,16 +2943,20 @@ static void ot_flash_load(OtFlashState *s, Error **errp) /* NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) */ rc = blk_pread(s->blk, 0, sizeof(*header), header, 0); if (rc < 0) { - error_setg(errp, "failed to read the flash header content: %d", rc); + error_setg(errp, + "%s: %s: failed to read the flash header content: %d", + __func__, s->ot_id, rc); return; } if (memcmp(header->magic, "vFSH", sizeof(header->magic)) != 0) { - error_setg(errp, "Flash file is not a valid flash backend"); + error_setg(errp, "%s: %s: Flash file is not a valid flash backend", + __func__, s->ot_id); return; } if (header->version != 1u) { - error_setg(errp, "Flash file version is not supported"); + error_setg(errp, "%s: %s: Flash file version is not supported", + __func__, s->ot_id); return; } @@ -2992,7 +3024,8 @@ static void ot_flash_load(OtFlashState *s, Error **errp) if (elflen > 0 && elflen < ELFNAME_SIZE) { if (!access(elfname, F_OK)) { if (load_elf_sym(elfname, 0, EM_RISCV, 1)) { - xtrace_ot_flash_error("Cannot load ELF symbols"); + xtrace_ot_flash_error(s->ot_id, + "Cannot load ELF symbols"); } } } @@ -3056,8 +3089,8 @@ static uint64_t ot_flash_mem_read(void *opaque, hwaddr addr, unsigned size) uint32_t val32; if (ot_flash_is_disabled(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: flash has been disabled\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: flash has been disabled\n", + __func__, s->ot_id); return 0u; } @@ -3078,8 +3111,8 @@ static uint64_t ot_flash_mem_read(void *opaque, hwaddr addr, unsigned size) } else { uint32_t pc = ibex_get_current_pc(); qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%" HWADDR_PRIx ", pc=0x%x\n", __func__, - addr, pc); + "%s: %s: Bad offset 0x%" HWADDR_PRIx ", pc=0x%x\n", + __func__, s->ot_id, addr, pc); val32 = 0; } @@ -3088,6 +3121,7 @@ static uint64_t ot_flash_mem_read(void *opaque, hwaddr addr, unsigned size) #endif /* #if DATA_PART_USE_IO_OPS */ static Property ot_flash_properties[] = { + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtFlashState, ot_id), DEFINE_PROP_DRIVE("drive", OtFlashState, blk), DEFINE_PROP_LINK("vmapper", OtFlashState, vmapper, TYPE_OT_VMAPPER, OtVMapperState *), @@ -3262,6 +3296,7 @@ static void ot_flash_realize(DeviceState *dev, Error **errp) OtFlashState *s = OT_FLASH(dev); (void)errp; + g_assert(s->ot_id); g_assert(s->vmapper); ot_flash_load(s, &error_fatal); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index e09a71ef2cdd0..e05d744abcfde 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -196,30 +196,30 @@ ot_entropy_src_update_filler(bool iok, bool ook, bool pok, bool all) "in %u, out # ot_flash.c -ot_flash_change_lc_phase(const char *phase, int nphase) "[%s:%d]" -ot_flash_data_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u addr 0x%06x" -ot_flash_erase(bool info_part, unsigned word_addr, unsigned num_bytes) "info_part %u word_addr %u num_bytes %u" -ot_flash_error(const char *func, int line, const char *err) "%s:%d %s" -ot_flash_get_keymgr_secret(const char *name, unsigned type) "%s [%d]" -ot_flash_info(const char *func, int line, const char *msg, uint32_t value) "%s:%d %s: 0x%08x" -ot_flash_info_part(unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned infosel, unsigned addr) "op_addr 0x%06x count %u remaining %u bank %u infosel %u addr 0x%06x" -ot_flash_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_flash_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_flash_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%08x eff:0x%08x" -ot_flash_lc_broadcast(unsigned sig, bool level) "bcast %u, level %u" -ot_flash_mem_read_out(uint32_t addr, unsigned size, uint32_t val, uint32_t pc) "addr=0x%02x (%u), val=0x%08x, pc=0x%x" -ot_flash_merge_info_qual(unsigned bank, unsigned infosel, unsigned page, uint8_t reg_cfg, uint8_t qual, uint8_t merged) "bank:%u infosel:%u page:%u, reg_cfg:0x%02x & qual:0x%02x = 0x%02x" -ot_flash_op_complete(const char *op, bool hw, bool success) "%s (hw=%u): %u" -ot_flash_op_execute(const char *op, bool hw) "%s (hw=%u)" -ot_flash_op_prog(unsigned op_addr, unsigned count, unsigned remaining, bool failed, bool fifo_empty) "op_addr 0x%06x count %u remaining %u failed %u fifo_empty %u" -ot_flash_op_start(const char *op, bool hw) "%s (hw=%u)" -ot_flash_prog_word(bool info_part, unsigned word_addr, uint32_t word) "info_part %u word_addr %u word %u" -ot_flash_read_keymgr_seed(unsigned page, bool success) "%u: %u" -ot_flash_reset_fifo(const char *name) "%s" -ot_flash_set_error(const char *op, bool hw, uint32_t err_code, uint32_t err_addr) "%s (hw=%u): err=0x%08x at addr=0x%06x" -ot_flash_update_alert(int prev, int next) "%d -> %d" -ot_flash_update_prog_watermark(unsigned val, unsigned watermark) "%u <= %u" -ot_flash_update_rd_watermark(unsigned val, unsigned watermark) "%u >= %u" +ot_flash_change_lc_phase(const char *id, const char *phase, int nphase) "%s: [%s:%d]" +ot_flash_data_part(const char *id, unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned addr) "%s: op_addr 0x%06x count %u remaining %u bank %u addr 0x%06x" +ot_flash_erase(const char *id, bool info_part, unsigned word_addr, unsigned num_bytes) "%s: info_part %u word_addr %u num_bytes %u" +ot_flash_error(const char *id, const char *func, int line, const char *err) "%s: %s:%d %s" +ot_flash_get_keymgr_secret(const char *id, const char *name, unsigned type) "%s: %s [%d]" +ot_flash_info(const char *id, const char *func, int line, const char *msg, uint32_t value) "%s: %s:%d %s: 0x%08x" +ot_flash_info_part(const char* id, unsigned op_addr, unsigned count, unsigned remaining, unsigned bank, unsigned infosel, unsigned addr) "%s: op_addr 0x%06x count %u remaining %u bank %u infosel %u addr 0x%06x" +ot_flash_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_flash_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_flash_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%08x msk:0x%08x eff:0x%08x" +ot_flash_lc_broadcast(const char* id, unsigned sig, bool level) "%s: bcast %u, level %u" +ot_flash_mem_read_out(const char* id, uint32_t addr, unsigned size, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%u), val=0x%08x, pc=0x%x" +ot_flash_merge_info_qual(const char* id, unsigned bank, unsigned infosel, unsigned page, uint8_t reg_cfg, uint8_t qual, uint8_t merged) "%s: bank:%u infosel:%u page:%u, reg_cfg:0x%02x & qual:0x%02x = 0x%02x" +ot_flash_op_complete(const char* id, const char *op, bool hw, bool success) "%s: %s (hw=%u): %u" +ot_flash_op_execute(const char* id, const char *op, bool hw) "%s: %s (hw=%u)" +ot_flash_op_prog(const char* id, unsigned op_addr, unsigned count, unsigned remaining, bool failed, bool fifo_empty) "%s: op_addr 0x%06x count %u remaining %u failed %u fifo_empty %u" +ot_flash_op_start(const char* id, const char *op, bool hw) "%s: %s (hw=%u)" +ot_flash_prog_word(const char* id, bool info_part, unsigned word_addr, uint32_t word) "%s: info_part %u word_addr %u word %u" +ot_flash_read_keymgr_seed(const char* id, unsigned page, bool success) "%s: %u: %u" +ot_flash_reset_fifo(const char* id, const char *name) "%s: %s" +ot_flash_set_error(const char* id, const char *op, bool hw, uint32_t err_code, uint32_t err_addr) "%s: %s (hw=%u): err=0x%08x at addr=0x%06x" +ot_flash_update_alert(const char* id, int prev, int next) "%s: %d -> %d" +ot_flash_update_prog_watermark(const char* id, unsigned val, unsigned watermark) "%s: %u <= %u" +ot_flash_update_rd_watermark(const char* id, unsigned val, unsigned watermark) "%s: %u >= %u" # ot_gpio.c From 8038a91af478658ebc355c3b7b02a2f458a48d1f Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Sat, 30 Aug 2025 07:07:22 +0100 Subject: [PATCH 5/6] [ot] docs/opentitan: earlgrey: Update flash_ctrl status in docs Also add more detailed documentation to the prelude of the flash controller source file that goes into more detail about what is still not implemented, and what may not be a goal for emulation. Signed-off-by: Alex Jones --- docs/opentitan/earlgrey.md | 5 +++-- hw/opentitan/ot_flash.c | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index 49915d7cbd9e1..fd76bda40c643 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -21,8 +21,9 @@ * CSRNG * EDN * Flash controller - * missing ECCs/ICVs, scrambling functionality and alerts - * no modelling of erase suspend + * largely functional but without ECCs/ICVs, scrambling functionality & alerts + * no modelling of erase suspend or RMA entry + * lc_ctrl NVM debug signal not implemented, escalation partially implemented * HMAC * OTBN * missing side-loading diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index 45842c70b32e6..0a35b7b0db71b 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -27,13 +27,22 @@ * THE SOFTWARE. * * Known limitations: - * - ECC/ICV/Scrambling functionality is not yet implemented in QEMU, + * - ECC/ICV/Scrambling functionality is completely unsupported in QEMU, * including ECC single error support. + * - In addition, the loading of flash address and flash data scrambling keys + * from OTP is likewise not included in initialisation. + * - The lc_ctrl `LC_NVM_DEBUG_EN` is currently unused. The `LC_ESCALATE_EN` + * signal is partially implemented (disables the flash). * - Alert functionality is only partially modelled. - * - Program Repair / High Endurance enables are meaningless in the OpenTitan - * Generic Flash Bank and so are not emulated. - * - Erase Suspend is not emulated in QEMU (erases are done synchronously, so - * you can suspend, but the bit will immediately be cleared). + * - Life cycle RMA Entry is not implemented. + * + * Other notes: + * - Program Repair / High Endurance enables are meaningless in the OT + * Generic Flash Bank and so are not emulated. + * - Erase suspend is not emulated as erases are done synchronously, so + * you can suspend, but the bit will be immediately cleared. + * - Flash operations are generally treated as synchronous, so arbitration + * between SW and HW is entirely unsupported. */ #include "qemu/osdep.h" From 417815efb10615376612b326995e188ebc345cb8 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Tue, 9 Sep 2025 12:04:53 +0100 Subject: [PATCH 6/6] [ot] scripts/opentitan: tests: Add new passing flash tests With the changes to add lc_ctrl connections and info page qualification support, this test is now passing in all of its different lc_ctrl lifecycle state variations. Signed-off-by: Alex Jones --- scripts/opentitan/tests-passing.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/opentitan/tests-passing.txt b/scripts/opentitan/tests-passing.txt index 6c4a97a547c9e..6dccbfb1bc8e1 100644 --- a/scripts/opentitan/tests-passing.txt +++ b/scripts/opentitan/tests-passing.txt @@ -89,9 +89,11 @@ //sw/device/tests:example_concurrency_test_sim_qemu_rom_with_fake_keys //sw/device/tests:example_test_from_flash_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_clock_freqs_test_sim_qemu_rom_with_fake_keys +//sw/device/tests:flash_ctrl_info_access_lc_dev_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_info_access_lc_prod_end_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_info_access_lc_prod_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_info_access_lc_rma_sim_qemu_rom_with_fake_keys +//sw/device/tests:flash_ctrl_info_access_lc_test_unlocked0_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_mem_protection_test_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_ops_test_sim_qemu_rom_with_fake_keys //sw/device/tests:flash_ctrl_test_sim_qemu_rom_with_fake_keys