From 1d32e160d7bb2a9b3e32656733577504ab2299a7 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 27 Aug 2025 12:02:00 +0100 Subject: [PATCH 1/6] [ot] hw/opentitan: ot_keymgr: Initial keymgr FSM with reset state Introduce the initial logic comprising the keymgr FSM, stubbing out all internal FSM states apart from the `RESET` state, which is implemented. Note however that this will not currently function, because keymgr enablement via lc_ctrl has not yet been added. Adds logic for scheduling the FSM using a short timer (200 ns) instead of a BottomHalf because of potential reliability issues. Using a BH / Timer allows deferring execution to the end of I/O calls. Signed-off-by: Alex Jones --- hw/opentitan/ot_keymgr.c | 189 ++++++++++++++++++++++++++++++++++- hw/opentitan/ot_keymgr_dpe.c | 10 +- hw/opentitan/trace-events | 5 + 3 files changed, 197 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index ceaa54e344098..29df2d9a7637d 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -91,6 +91,9 @@ static_assert(KEYMGR_GEN_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, static_assert(KEYMGR_LFSR_SEED_BYTES <= KEYMGR_SEED_BYTES, "Keymgr LFSR seed is larger than generic KeyMgr seed size"); +/* Use a timer with a small delay instead of a BH for reliability */ +#define FSM_TICK_DELAY 200u /* 200 ns */ + /* clang-format off */ REG32(INTR_STATE, 0x0u) SHARED_FIELD(INTR_OP_DONE, 0u, 1u) @@ -314,12 +317,27 @@ enum { KEYMGR_SEED_COUNT, }; +typedef enum { + KEYMGR_ST_RESET, + KEYMGR_ST_ENTROPY_RESEED, + KEYMGR_ST_RANDOM, + KEYMGR_ST_ROOT_KEY, + KEYMGR_ST_INIT, + KEYMGR_ST_CREATOR_ROOT_KEY, + KEYMGR_ST_OWNER_INT_KEY, + KEYMGR_ST_OWNER_KEY, + KEYMGR_ST_DISABLED, + KEYMGR_ST_WIPE, + KEYMGR_ST_INVALID, +} OtKeyMgrFSMState; + typedef struct OtKeyMgrState { SysBusDevice parent_obj; MemoryRegion mmio; IbexIRQ irq; IbexIRQ alerts[ALERT_COUNT]; + QEMUTimer *fsm_tick_timer; uint32_t regs[REGS_COUNT]; OtShadowReg control; @@ -331,6 +349,8 @@ typedef struct OtKeyMgrState { uint8_t *sealing_sw_binding; uint8_t *attest_sw_binding; + bool enabled; + OtKeyMgrFSMState state; uint8_t *seeds[KEYMGR_SEED_COUNT]; /* properties */ @@ -413,6 +433,43 @@ static const char *REG_NAMES[REGS_COUNT] = { #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") +#define WORKING_STATE_ENTRY(_st_) \ + [KEYMGR_WORKING_STATE_##_st_] = stringify(_st_) +static const char *WORKING_STATE_NAMES[] = { + WORKING_STATE_ENTRY(RESET), + WORKING_STATE_ENTRY(INIT), + WORKING_STATE_ENTRY(CREATOR_ROOT_KEY), + WORKING_STATE_ENTRY(OWNER_INTERMEDIATE_KEY), + WORKING_STATE_ENTRY(OWNER_KEY), + WORKING_STATE_ENTRY(DISABLED), + WORKING_STATE_ENTRY(INVALID), +}; +#undef WORKING_STATE_ENTRY +#define WORKING_STATE_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(WORKING_STATE_NAMES) ? \ + WORKING_STATE_NAMES[(_st_)] : \ + "?") + +#define FST_ENTRY(_st_) [KEYMGR_ST_##_st_] = stringify(_st_) +static const char *FST_NAMES[] = { + /* clang-format off */ + FST_ENTRY(RESET), + FST_ENTRY(ENTROPY_RESEED), + FST_ENTRY(RANDOM), + FST_ENTRY(ROOT_KEY), + FST_ENTRY(INIT), + FST_ENTRY(CREATOR_ROOT_KEY), + FST_ENTRY(OWNER_INT_KEY), + FST_ENTRY(OWNER_KEY), + FST_ENTRY(DISABLED), + FST_ENTRY(WIPE), + FST_ENTRY(INVALID), + /* clang-format on */ +}; +#undef FST_ENTRY +#define FST_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(FST_NAMES) ? FST_NAMES[(_st_)] : "?") + static void ot_keymgr_update_irq(OtKeyMgrState *s) { bool level = (bool)(s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]); @@ -464,6 +521,128 @@ static void ot_keymgr_update_alerts(OtKeyMgrState *s) } } +#define ot_keymgr_change_working_state(_s_, _working_state_) \ + ot_keymgr_xchange_working_state(_s_, _working_state_, __LINE__) + +static OtKeyMgrWorkingState ot_keymgr_get_working_state(const OtKeyMgrState *s) +{ + uint32_t working_state = + FIELD_EX32(s->regs[R_WORKING_STATE], WORKING_STATE, STATE); + + if (working_state <= KEYMGR_WORKING_STATE_INVALID) { + return working_state; + } + return KEYMGR_WORKING_STATE_INVALID; +} + +static void ot_keymgr_xchange_working_state( + OtKeyMgrState *s, OtKeyMgrWorkingState working_state, int line) +{ + OtKeyMgrWorkingState prev_working_state = ot_keymgr_get_working_state(s); + + if (prev_working_state != working_state) { + trace_ot_keymgr_change_working_state(s->ot_id, line, + WORKING_STATE_NAME( + prev_working_state), + prev_working_state, + WORKING_STATE_NAME(working_state), + working_state); + s->regs[R_WORKING_STATE] = working_state; + } +} + +#define ot_keymgr_schedule_fsm(_s_) \ + ot_keymgr_xschedule_fsm(_s_, __func__, __LINE__) + +static void ot_keymgr_xschedule_fsm(OtKeyMgrState *s, const char *func, + int line) +{ + trace_ot_keymgr_schedule_fsm(s->ot_id, func, line); + if (!timer_pending(s->fsm_tick_timer)) { + uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); + timer_mod(s->fsm_tick_timer, (int64_t)(now + FSM_TICK_DELAY)); + } +} + +#define ot_keymgr_change_main_fsm_state(_s_, _op_status_) \ + ot_keymgr_xchange_main_fsm_state(_s_, _op_status_, __LINE__) + +static void ot_keymgr_xchange_main_fsm_state(OtKeyMgrState *s, + OtKeyMgrFSMState state, int line) +{ + if (s->state != state) { + trace_ot_keymgr_change_main_fsm_state(s->ot_id, line, + FST_NAME(s->state), s->state, + FST_NAME(state), state); + s->state = state; + } +} + +/* Tick the main FSM. Returns whether there was any state change this tick. */ +static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) +{ + /* store current state so we can determine (& return) any state change */ + OtKeyMgrFSMState state = s->state; + bool op_start = s->regs[R_START] & R_START_EN_MASK; + bool invalid_state = s->regs[R_FAULT_STATUS] & FAULT_STATUS_MASK; + uint32_t ctrl = ot_shadow_reg_peek(&s->control); + + trace_ot_keymgr_main_fsm_tick(s->ot_id, FST_NAME(s->state), s->state); + + switch (s->state) { + case KEYMGR_ST_RESET: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_RESET); + bool op_advance = + FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION) == KEYMGR_OP_ADVANCE; + /* @todo: add keymgr enablement via lc_ctrl */ + bool advance_sel = op_start && op_advance && s->enabled; + if (op_start && !advance_sel) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; + } + + if (invalid_state) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + } else if (advance_sel) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_ENTROPY_RESEED); + } + break; + case KEYMGR_ST_ENTROPY_RESEED: + case KEYMGR_ST_RANDOM: + case KEYMGR_ST_ROOT_KEY: + case KEYMGR_ST_INIT: + case KEYMGR_ST_CREATOR_ROOT_KEY: + case KEYMGR_ST_OWNER_INT_KEY: + case KEYMGR_ST_OWNER_KEY: + case KEYMGR_ST_DISABLED: + case KEYMGR_ST_WIPE: + case KEYMGR_ST_INVALID: + /* @todo: implement other keymgr FSM states */ + qemu_log_mask(LOG_UNIMP, "%s: %s: FSM State %s is not implemented.\n", + __func__, s->ot_id, FST_NAME(s->state)); + break; + default: + break; + } + + /* @todo: update ERR_CODE, OP_STATUS & CFG_REGWEN based on FSM changes */ + + return state != s->state; +} + +static void ot_keymgr_fsm_tick(void *opaque) +{ + OtKeyMgrState *s = opaque; + + bool fsm_state_changed = ot_keymgr_main_fsm_tick(s); + if (fsm_state_changed) { + /* FSM state changed, so schedule an FSM update once more */ + ot_keymgr_schedule_fsm(s); + } else { + /* no FSM state change, so go idle and wait for some external event */ + trace_ot_keymgr_go_idle(s->ot_id); + } +} + #define ot_keymgr_check_reg_write(_s_, _reg_, _regwen_) \ ot_keymgr_check_reg_write_func(__func__, _s_, _reg_, _regwen_) @@ -642,7 +821,7 @@ static void ot_keymgr_write(void *opaque, hwaddr addr, uint64_t val64, } val32 &= R_START_EN_MASK; s->regs[reg] = val32; - /* @todo: implement R_START */ + ot_keymgr_fsm_tick(s); break; case R_CONTROL_SHADOWED: if (!ot_keymgr_check_reg_write(s, reg, R_CFG_REGWEN)) { @@ -916,6 +1095,8 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) c->parent_phases.enter(obj, type); } + timer_del(s->fsm_tick_timer); + /* reset registers */ memset(s->regs, 0u, sizeof(s->regs)); s->regs[R_CFG_REGWEN] = 0x1u; @@ -933,6 +1114,10 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) memset(s->sealing_sw_binding, 0u, KEYMGR_SW_BINDING_BYTES); memset(s->attest_sw_binding, 0u, KEYMGR_SW_BINDING_BYTES); + /* reset internal state */ + s->enabled = false; + s->state = KEYMGR_ST_RESET; + /* update IRQ and alert states */ ot_keymgr_update_irq(s); ot_keymgr_update_alerts(s); @@ -983,6 +1168,8 @@ static void ot_keymgr_init(Object *obj) for (unsigned ix = 0u; ix < ARRAY_SIZE(s->seeds); ix++) { s->seeds[ix] = g_new0(uint8_t, KEYMGR_SEED_BYTES); } + + s->fsm_tick_timer = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_keymgr_fsm_tick, s); } static void ot_keymgr_class_init(ObjectClass *klass, void *data) diff --git a/hw/opentitan/ot_keymgr_dpe.c b/hw/opentitan/ot_keymgr_dpe.c index 9dfd16ec2d81c..c5cc7e66a43c9 100644 --- a/hw/opentitan/ot_keymgr_dpe.c +++ b/hw/opentitan/ot_keymgr_dpe.c @@ -653,14 +653,12 @@ ot_keymgr_dpe_get_working_state(const OtKeyMgrDpeState *s) static void ot_keymgr_dpe_xchange_working_state( OtKeyMgrDpeState *s, OtKeyMgrDpeWorkingState working_state, int line) { - OtKeyMgrDpeWorkingState prev_working_state = - ot_keymgr_dpe_get_working_state(s); + OtKeyMgrDpeWorkingState prev_state = ot_keymgr_dpe_get_working_state(s); - if (prev_working_state != working_state) { + if (prev_state != working_state) { trace_ot_keymgr_dpe_change_working_state(s->ot_id, line, - WORKING_STATE_NAME( - prev_working_state), - prev_working_state, + WORKING_STATE_NAME(prev_state), + prev_state, WORKING_STATE_NAME( working_state), working_state); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 25ab414ed5943..c7e58e2abdb84 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -278,10 +278,15 @@ 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 halted, bool in_reset, bool cpu_en) "%s: bm:0x%x esc:%u halted:%u in_reset:%u -> CPU enable %u" # ot_keymgr.c +ot_keymgr_change_main_fsm_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_keymgr_change_working_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_keymgr_go_idle(const char *id) "%s" ot_keymgr_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%08x, pc=0x%x" ot_keymgr_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_keymgr_irq(const char *id, uint32_t active, uint32_t mask, bool level) "%s: act:0x%08x msk:0x%08x lvl:%u" +ot_keymgr_main_fsm_tick(const char *id, const char *st, int nst) "%s: [%s:%d]" ot_keymgr_reset(const char *id, const char *stage) "%s: %s" +ot_keymgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d" ot_keymgr_seed_missing(const char *id, unsigned ix) "%s: #%u" ot_keymgr_update_alert(const char *id, int prev, int next) "%s: %d -> %d" From 64c9fe91eb9dff682b3b4a0de2322c4bd4e26f1e Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 27 Aug 2025 12:32:40 +0100 Subject: [PATCH 2/6] [ot] hw/opentitan: ot_keymgr: Add lc_ctrl keymgr enable connection When the keymgr is disabled by the lc_ctrl, this also temporarily locks the CFG_REGWEN. Signed-off-by: Alex Jones --- hw/opentitan/ot_keymgr.c | 28 +++++++++++++++++++++++++++- hw/opentitan/trace-events | 1 + include/hw/opentitan/ot_keymgr.h | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index 29df2d9a7637d..7260905b0d4e0 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -594,7 +594,6 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_RESET); bool op_advance = FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION) == KEYMGR_OP_ADVANCE; - /* @todo: add keymgr enablement via lc_ctrl */ bool advance_sel = op_start && op_advance && s->enabled; if (op_start && !advance_sel) { s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; @@ -643,6 +642,31 @@ static void ot_keymgr_fsm_tick(void *opaque) } } +static void ot_keymgr_lc_signal(void *opaque, int irq, int level) +{ + OtKeyMgrState *s = opaque; + bool enable_keymgr = (bool)level; + + g_assert(irq == 0); + + trace_ot_keymgr_lc_signal(s->ot_id, level); + + bool enablement_changed = enable_keymgr ^ s->enabled; + if (!enablement_changed) { + return; + } + + s->enabled = enable_keymgr; + + if (s->enabled) { + s->regs[R_CFG_REGWEN] |= R_CFG_REGWEN_EN_MASK; + } else { + s->regs[R_CFG_REGWEN] &= ~R_CFG_REGWEN_EN_MASK; + } + + ot_keymgr_schedule_fsm(s); +} + #define ot_keymgr_check_reg_write(_s_, _reg_, _regwen_) \ ot_keymgr_check_reg_write_func(__func__, _s_, _reg_, _regwen_) @@ -1161,6 +1185,8 @@ static void ot_keymgr_init(Object *obj) for (unsigned ix = 0u; ix < ALERT_COUNT; ix++) { ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); } + qdev_init_gpio_in_named(DEVICE(s), ot_keymgr_lc_signal, OT_KEYMGR_ENABLE, + 1); s->salt = g_new0(uint8_t, KEYMGR_SALT_BYTES); s->sealing_sw_binding = g_new0(uint8_t, KEYMGR_SW_BINDING_BYTES); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index c7e58e2abdb84..7d72dff267b4f 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -284,6 +284,7 @@ ot_keymgr_go_idle(const char *id) "%s" ot_keymgr_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%08x, pc=0x%x" ot_keymgr_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_keymgr_irq(const char *id, uint32_t active, uint32_t mask, bool level) "%s: act:0x%08x msk:0x%08x lvl:%u" +ot_keymgr_lc_signal(const char *id, int level) "%s: LC signal level:%d" ot_keymgr_main_fsm_tick(const char *id, const char *st, int nst) "%s: [%s:%d]" ot_keymgr_reset(const char *id, const char *stage) "%s: %s" ot_keymgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d" diff --git a/include/hw/opentitan/ot_keymgr.h b/include/hw/opentitan/ot_keymgr.h index 7f99a0c72cc92..6ac454df0886b 100644 --- a/include/hw/opentitan/ot_keymgr.h +++ b/include/hw/opentitan/ot_keymgr.h @@ -34,4 +34,7 @@ #define TYPE_OT_KEYMGR "ot-keymgr" OBJECT_DECLARE_TYPE(OtKeyMgrState, OtKeyMgrClass, OT_KEYMGR) +/* Input signal to enable the key manager (from lifecycle controller) */ +#define OT_KEYMGR_ENABLE TYPE_OT_KEYMGR "-enable" + #endif /* HW_OPENTITAN_OT_KEYMGR_H */ From 4a0acc7a2a0603844f08b518334ca3e005afb42b Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 27 Aug 2025 14:18:28 +0100 Subject: [PATCH 3/6] [ot] hw/riscv: ot_earlgrey: Connect lc_ctrl keymgr broadcast signal Signed-off-by: Alex Jones --- hw/riscv/ot_earlgrey.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 28199492b334c..7f1e32a34ca09 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -735,9 +735,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(1, 17), OT_EG_SOC_GPIO_ALERT(2, 18), /* - * TODO: add missing life cycle broadcast signals when the required - * supporting HW is available: - * - OT_LC_KEYMGR_EN (when keymgr is implemented) + * @todo: check for missing life cycle broadcast signal connections + * and add them when the required supporting HW is available. */ /* Splitters for signals that go to many blocks. */ OT_EG_SOC_D2S(OT_LC_BROADCAST, OT_LC_HW_DEBUG_EN, LC_HW_DEBUG), @@ -748,6 +747,9 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { /* 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 keymgr */ + OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_KEYMGR_EN, KEYMGR, + OT_KEYMGR_ENABLE, 0), /* Signals to flash_ctrl */ OT_EG_SOC_SIGNAL(OT_LC_BROADCAST, OT_LC_OWNER_SEED_SW_RW_EN, FLASH_CTRL, OT_LC_BROADCAST, From d9c2246638abd9e3302e52296231c8a82cd19254 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 27 Aug 2025 17:14:20 +0100 Subject: [PATCH 4/6] [ot] hw/opentitan: ot_keymgr: Reseed entropy from the EDN Add a device link to endpoint 0 of EDN0 through which the Keymgr can request entropy for refreshing its seed. Implements the `ENTROPY_RESEED` and `RANDOM` states for the Keymgr FSM which use this connection. Signed-off-by: Alex Jones --- hw/opentitan/ot_keymgr.c | 117 ++++++++++++++++++++++++++++++++++++++ hw/opentitan/trace-events | 2 + hw/riscv/ot_earlgrey.c | 8 ++- 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index 7260905b0d4e0..a53f5e6ae910b 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -33,7 +33,9 @@ #include "qapi/error.h" #include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_edn.h" #include "hw/opentitan/ot_keymgr.h" +#include "hw/opentitan/ot_prng.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -76,6 +78,12 @@ #define KEYMGR_KDF_BUFFER_BYTES ((KEYMGR_KDF_BUFFER_WIDTH) / 8u) #define KEYMGR_SEED_BYTES (KEYMGR_KEY_BYTES) +/* + * The EDN provides words of entropy at a time, so the keymgr needs + * to send multiple entropy requests to reseeds its internal LFSR. + */ +#define KEYMGR_RESEED_COUNT (KEYMGR_LFSR_WIDTH / (8u * sizeof(uint32_t))) + static_assert(KEYMGR_ADV_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, "KeyMgr ADV data does not fit in KDF buffer"); static_assert(KEYMGR_ID_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, @@ -331,6 +339,20 @@ typedef enum { KEYMGR_ST_INVALID, } OtKeyMgrFSMState; +typedef struct { + OtEDNState *device; + uint8_t ep; + bool connected; + bool scheduled; +} OtKeyMgrEDN; + +typedef struct { + OtPrngState *state; + bool reseed_req; + bool reseed_ack; + uint8_t reseed_cnt; +} OtKeyMgrPrng; + typedef struct OtKeyMgrState { SysBusDevice parent_obj; @@ -351,10 +373,12 @@ typedef struct OtKeyMgrState { bool enabled; OtKeyMgrFSMState state; + OtKeyMgrPrng prng; uint8_t *seeds[KEYMGR_SEED_COUNT]; /* properties */ char *ot_id; + OtKeyMgrEDN edn; char *seed_xstrs[KEYMGR_SEED_COUNT]; } OtKeyMgrState; @@ -564,6 +588,57 @@ static void ot_keymgr_xschedule_fsm(OtKeyMgrState *s, const char *func, } } +static void ot_keymgr_request_entropy(OtKeyMgrState *s); + +static void ot_keymgr_push_entropy(void *opaque, uint32_t bits, bool fips) +{ + (void)fips; + OtKeyMgrState *s = opaque; + OtKeyMgrEDN *edn = &s->edn; + OtKeyMgrPrng *prng = &s->prng; + + if (!edn->scheduled) { + trace_ot_keymgr_error(s->ot_id, "Unexpected entropy"); + return; + } + edn->scheduled = false; + + ot_prng_reseed(prng->state, bits); + prng->reseed_cnt++; + + bool reschedule = prng->reseed_cnt < KEYMGR_RESEED_COUNT; + + trace_ot_keymgr_entropy(s->ot_id, prng->reseed_cnt, reschedule); + + if (reschedule) { + /* we need more entropy */ + ot_keymgr_request_entropy(s); + } else if (prng->reseed_req) { + prng->reseed_ack = true; + prng->reseed_cnt = 0u; + ot_keymgr_schedule_fsm(s); + } +} + +static void ot_keymgr_request_entropy(OtKeyMgrState *s) +{ + OtKeyMgrEDN *edn = &s->edn; + + if (!edn->connected) { + ot_edn_connect_endpoint(edn->device, edn->ep, &ot_keymgr_push_entropy, + s); + edn->connected = true; + } + + if (!edn->scheduled && s->prng.reseed_req) { + edn->scheduled = true; + if (ot_edn_request_entropy(edn->device, edn->ep)) { + error_setg(&error_fatal, "%s: %s: keymgr failed to request entropy", + __func__, s->ot_id); + } + } +} + #define ot_keymgr_change_main_fsm_state(_s_, _op_status_) \ ot_keymgr_xchange_main_fsm_state(_s_, _op_status_, __LINE__) @@ -606,8 +681,39 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) } break; case KEYMGR_ST_ENTROPY_RESEED: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_RESET); + /* + * The Earlgrey 1.0.0 keymgr does not immediately transition to + * ST_INVALID if disabled by the lc_ctrl whilst reseeding entropy. + */ + if (!s->prng.reseed_req) { + s->prng.reseed_ack = false; + s->prng.reseed_req = true; + ot_keymgr_request_entropy(s); + } else if (s->prng.reseed_ack) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_RANDOM); + } + break; case KEYMGR_ST_RANDOM: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_RESET); + /* + * The Earlgrey 1.0.0 keymgr does not immediately transition to + * ST_INVALID if disabled by the lc_ctrl whilst reseeding entropy. + * + * @todo: normally, this state would initialise the key state with + * some random entropy for masking of key shares, but we leave these + * uninitialised while there is no KMAC masking. + */ + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_ROOT_KEY); + break; case KEYMGR_ST_ROOT_KEY: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_INIT); + if (!s->enabled) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_INVALID); + } else { + /* @todo: retrieve root key from OTP and change state */ + } + break; case KEYMGR_ST_INIT: case KEYMGR_ST_CREATOR_ROOT_KEY: case KEYMGR_ST_OWNER_INT_KEY: @@ -1075,6 +1181,9 @@ static void ot_keymgr_configure_constants(OtKeyMgrState *s) static Property ot_keymgr_properties[] = { DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtKeyMgrState, ot_id), + DEFINE_PROP_LINK("edn", OtKeyMgrState, edn.device, TYPE_OT_EDN, + OtEDNState *), + DEFINE_PROP_UINT8("edn-ep", OtKeyMgrState, edn.ep, UINT8_MAX), DEFINE_PROP_STRING("lfsr_seed", OtKeyMgrState, seed_xstrs[KEYMGR_SEED_LFSR]), DEFINE_PROP_STRING("revision_seed", OtKeyMgrState, @@ -1121,6 +1230,9 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) timer_del(s->fsm_tick_timer); + g_assert(s->edn.device); + g_assert(s->edn.ep != UINT8_MAX); + /* reset registers */ memset(s->regs, 0u, sizeof(s->regs)); s->regs[R_CFG_REGWEN] = 0x1u; @@ -1141,6 +1253,9 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) /* reset internal state */ s->enabled = false; s->state = KEYMGR_ST_RESET; + s->prng.reseed_req = false; + s->prng.reseed_ack = false; + s->prng.reseed_cnt = 0u; /* update IRQ and alert states */ ot_keymgr_update_irq(s); @@ -1188,6 +1303,8 @@ static void ot_keymgr_init(Object *obj) qdev_init_gpio_in_named(DEVICE(s), ot_keymgr_lc_signal, OT_KEYMGR_ENABLE, 1); + s->prng.state = ot_prng_allocate(); + s->salt = g_new0(uint8_t, KEYMGR_SALT_BYTES); s->sealing_sw_binding = g_new0(uint8_t, KEYMGR_SW_BINDING_BYTES); s->attest_sw_binding = g_new0(uint8_t, KEYMGR_SW_BINDING_BYTES); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 7d72dff267b4f..5467a070b64d3 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -280,6 +280,8 @@ ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool halte # ot_keymgr.c ot_keymgr_change_main_fsm_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_change_working_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_keymgr_entropy(const char *id, unsigned reseed_cnt, bool resched) "%s: reseed_cnt: %u, resched: %u" +ot_keymgr_error(const char *id, const char *msg) "%s: %s" ot_keymgr_go_idle(const char *id) "%s" ot_keymgr_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%08x, pc=0x%x" ot_keymgr_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" diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 7f1e32a34ca09..5241fa85b51a5 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -1184,7 +1184,13 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 173), OT_EG_SOC_GPIO_ALERT(0, 49), OT_EG_SOC_GPIO_ALERT(1, 50) - ) + ), + .link = IBEXDEVICELINKDEFS( + OT_EG_SOC_DEVLINK("edn", EDN0) + ), + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("edn-ep", 0u) + ), }, [OT_EG_SOC_DEV_CSRNG] = { .type = TYPE_OT_CSRNG, From a9388e51bd1ccc545d09e43a302e3e1d3d913557 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 28 Aug 2025 03:59:41 +0100 Subject: [PATCH 5/6] [ot] hw/opentitan: ot_keymgr: Add root key loading from OTP Adds a connection between the keymgr and the otp_ctrl, using the implemented `otp_ctrl` methods to retrieve the creator root key from OTP. Also change the behaviour of the `ROOT_KEY` FSM state so that an invalid root key (or disablement) will cause a transition to the `WIPE` state. Signed-off-by: Alex Jones --- hw/opentitan/ot_keymgr.c | 80 ++++++++++++++++++++++++++++++++++++++- hw/opentitan/trace-events | 1 + hw/riscv/ot_earlgrey.c | 3 +- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index a53f5e6ae910b..19589c08005e2 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -35,6 +35,8 @@ #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_edn.h" #include "hw/opentitan/ot_keymgr.h" +#include "hw/opentitan/ot_kmac.h" +#include "hw/opentitan/ot_otp.h" #include "hw/opentitan/ot_prng.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" @@ -90,6 +92,12 @@ static_assert(KEYMGR_ID_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, "KeyMgr ID data does not fit in KDF buffer"); static_assert(KEYMGR_GEN_DATA_BYTES <= KEYMGR_KDF_BUFFER_BYTES, "KeyMgr GEN data does not fit in KDF buffer"); +/* NOLINTBEGIN(misc-redundant-expression) */ +static_assert(KEYMGR_KEY_BYTES == OT_OTP_KEYMGR_SECRET_SIZE, + "KeyMgr key size does not match OTP KeyMgr secret size"); +static_assert(KEYMGR_KEY_BYTES == OT_KMAC_KEY_SIZE, + "KeyMgr key size does not match KMAC key size"); +/* NOLINTEND(misc-redundant-expression) */ #define KEYMGR_ENTROPY_WIDTH (KEYMGR_LFSR_WIDTH / 2u) #define KEYMGR_ENTROPY_ROUNDS (KEYMGR_KEY_WIDTH / KEYMGR_ENTROPY_WIDTH) @@ -353,6 +361,12 @@ typedef struct { uint8_t reseed_cnt; } OtKeyMgrPrng; +typedef struct { + uint8_t share0[KEYMGR_KEY_BYTES]; + uint8_t share1[KEYMGR_KEY_BYTES]; + bool valid; +} OtKeyMgrKey; + typedef struct OtKeyMgrState { SysBusDevice parent_obj; @@ -376,9 +390,15 @@ typedef struct OtKeyMgrState { OtKeyMgrPrng prng; uint8_t *seeds[KEYMGR_SEED_COUNT]; + /* key states */ + OtKeyMgrKey *key_states; + + char *hexstr; + /* properties */ char *ot_id; OtKeyMgrEDN edn; + OtOTPState *otp_ctrl; char *seed_xstrs[KEYMGR_SEED_COUNT]; } OtKeyMgrState; @@ -494,6 +514,11 @@ static const char *FST_NAMES[] = { #define FST_NAME(_st_) \ (((unsigned)(_st_)) < ARRAY_SIZE(FST_NAMES) ? FST_NAMES[(_st_)] : "?") +#define OT_KEYMGR_HEXSTR_SIZE 256u + +#define ot_keymgr_dump_bigint(_s_, _b_, _l_) \ + ot_common_lhexdump(_b_, _l_, true, (_s_)->hexstr, OT_KEYMGR_HEXSTR_SIZE) + static void ot_keymgr_update_irq(OtKeyMgrState *s) { bool level = (bool)(s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]); @@ -639,6 +664,28 @@ static void ot_keymgr_request_entropy(OtKeyMgrState *s) } } +static void ot_keymgr_get_root_key(OtKeyMgrState *s, OtOTPKeyMgrSecret *share0, + OtOTPKeyMgrSecret *share1) +{ + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); + g_assert(oc); + oc->get_keymgr_secret(s->otp_ctrl, + OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, share0); + oc->get_keymgr_secret(s->otp_ctrl, + OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1, share1); + + if (trace_event_get_state(TRACE_OT_KEYMGR_DUMP_CREATOR_ROOT_KEY)) { + trace_ot_keymgr_dump_creator_root_key( + s->ot_id, 0, share0->valid, + ot_keymgr_dump_bigint(s, share0->secret, + OT_OTP_KEYMGR_SECRET_SIZE)); + trace_ot_keymgr_dump_creator_root_key( + s->ot_id, 1, share1->valid, + ot_keymgr_dump_bigint(s, share1->secret, + OT_OTP_KEYMGR_SECRET_SIZE)); + } +} + #define ot_keymgr_change_main_fsm_state(_s_, _op_status_) \ ot_keymgr_xchange_main_fsm_state(_s_, _op_status_, __LINE__) @@ -708,10 +755,30 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) break; case KEYMGR_ST_ROOT_KEY: ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_INIT); + /* If the keymgr is disabled or the root key is invalid, we must wipe */ if (!s->enabled) { - ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_INVALID); + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + break; + } + /* @todo: update `op_done_o` as `init_o = 1` here. */ + + /* Retrieve Creator Root Key from OTP */ + OtOTPKeyMgrSecret secret_share0 = { 0u }; + OtOTPKeyMgrSecret secret_share1 = { 0u }; + ot_keymgr_get_root_key(s, &secret_share0, &secret_share1); + + if (secret_share0.valid && secret_share1.valid) { + memset(s->key_states, 0u, NUM_CDIS * sizeof(OtKeyMgrKey)); + for (unsigned cdi = 0u; cdi < NUM_CDIS; cdi++) { + memcpy(s->key_states[cdi].share0, secret_share0.secret, + OT_OTP_KEYMGR_SECRET_SIZE); + memcpy(s->key_states[cdi].share1, secret_share1.secret, + OT_OTP_KEYMGR_SECRET_SIZE); + s->key_states[cdi].valid = true; + } + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_INIT); } else { - /* @todo: retrieve root key from OTP and change state */ + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); } break; case KEYMGR_ST_INIT: @@ -1184,6 +1251,8 @@ static Property ot_keymgr_properties[] = { DEFINE_PROP_LINK("edn", OtKeyMgrState, edn.device, TYPE_OT_EDN, OtEDNState *), DEFINE_PROP_UINT8("edn-ep", OtKeyMgrState, edn.ep, UINT8_MAX), + DEFINE_PROP_LINK("otp_ctrl", OtKeyMgrState, otp_ctrl, TYPE_OT_OTP, + OtOTPState *), DEFINE_PROP_STRING("lfsr_seed", OtKeyMgrState, seed_xstrs[KEYMGR_SEED_LFSR]), DEFINE_PROP_STRING("revision_seed", OtKeyMgrState, @@ -1232,6 +1301,7 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) g_assert(s->edn.device); g_assert(s->edn.ep != UINT8_MAX); + g_assert(s->otp_ctrl); /* reset registers */ memset(s->regs, 0u, sizeof(s->regs)); @@ -1257,6 +1327,9 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) s->prng.reseed_ack = false; s->prng.reseed_cnt = 0u; + /* reset key state */ + memset(s->key_states, 0u, NUM_CDIS * sizeof(OtKeyMgrKey)); + /* update IRQ and alert states */ ot_keymgr_update_irq(s); ot_keymgr_update_alerts(s); @@ -1311,8 +1384,11 @@ static void ot_keymgr_init(Object *obj) for (unsigned ix = 0u; ix < ARRAY_SIZE(s->seeds); ix++) { s->seeds[ix] = g_new0(uint8_t, KEYMGR_SEED_BYTES); } + s->key_states = g_new0(OtKeyMgrKey, NUM_CDIS); s->fsm_tick_timer = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_keymgr_fsm_tick, s); + + s->hexstr = g_new0(char, OT_KEYMGR_HEXSTR_SIZE); } static void ot_keymgr_class_init(ObjectClass *klass, void *data) diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 5467a070b64d3..fe12267eec13a 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -280,6 +280,7 @@ ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool halte # ot_keymgr.c ot_keymgr_change_main_fsm_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_change_working_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_keymgr_dump_creator_root_key(const char *id, unsigned idx, bool valid, const char *hexstr) "%s: share:%u valid:%u %s" ot_keymgr_entropy(const char *id, unsigned reseed_cnt, bool resched) "%s: reseed_cnt: %u, resched: %u" ot_keymgr_error(const char *id, const char *msg) "%s: %s" ot_keymgr_go_idle(const char *id) "%s" diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 5241fa85b51a5..c53d0f21d5370 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -1186,7 +1186,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(1, 50) ), .link = IBEXDEVICELINKDEFS( - OT_EG_SOC_DEVLINK("edn", EDN0) + OT_EG_SOC_DEVLINK("edn", EDN0), + OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("edn-ep", 0u) From c4401a3eceeff2daf200641032929e56a7243c16 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Fri, 29 Aug 2025 01:20:24 +0100 Subject: [PATCH 6/6] [ot] hw/opentitan: ot_keymgr: Add operation status & init FSM state This commit focuses on creating a (mostly complete) implementation of the `INIT` keymgr FSM state, which performs some checks before starting an advance or disable operation. These are both stubbed out for now. Logic (which is the same as the keymgr_dpe logic) is added for updating the `OP_STATUS`, so that it latches and reflects the last requested operation. Also, functionality is added to lock `CFG_REGWEN` during an operation. Signed-off-by: Alex Jones --- hw/opentitan/ot_keymgr.c | 139 ++++++++++++++++++++++++++++++++++++-- hw/opentitan/trace-events | 2 + 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index 19589c08005e2..f893cccfd530f 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -367,6 +367,11 @@ typedef struct { bool valid; } OtKeyMgrKey; +typedef struct { + bool op_req; + bool op_ack; +} OtKeyMgrOpState; + typedef struct OtKeyMgrState { SysBusDevice parent_obj; @@ -388,6 +393,7 @@ typedef struct OtKeyMgrState { bool enabled; OtKeyMgrFSMState state; OtKeyMgrPrng prng; + OtKeyMgrOpState op_state; uint8_t *seeds[KEYMGR_SEED_COUNT]; /* key states */ @@ -477,6 +483,18 @@ static const char *REG_NAMES[REGS_COUNT] = { #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") +#define OP_ENTRY(_op_) [KEYMGR_OP_##_op_] = stringify(_op_) +static const char *OP_NAMES[] = { + OP_ENTRY(ADVANCE), + OP_ENTRY(GENERATE_ID), + OP_ENTRY(GENERATE_SW_OUTPUT), + OP_ENTRY(GENERATE_HW_OUTPUT), + OP_ENTRY(DISABLE), +}; +#undef OP_ENTRY +#define OP_NAME(_op_) \ + (((unsigned)(_op_)) < ARRAY_SIZE(OP_NAMES) ? OP_NAMES[(_op_)] : "?") + #define WORKING_STATE_ENTRY(_st_) \ [KEYMGR_WORKING_STATE_##_st_] = stringify(_st_) static const char *WORKING_STATE_NAMES[] = { @@ -494,6 +512,19 @@ static const char *WORKING_STATE_NAMES[] = { WORKING_STATE_NAMES[(_st_)] : \ "?") +#define OP_STATUS_ENTRY(_st_) [KEYMGR_OP_STATUS_##_st_] = stringify(_st_) +static const char *OP_STATUS_NAMES[] = { + OP_STATUS_ENTRY(IDLE), + OP_STATUS_ENTRY(WIP), + OP_STATUS_ENTRY(DONE_SUCCESS), + OP_STATUS_ENTRY(DONE_ERROR), +}; +#undef OP_STATUS_ENTRY +#define OP_STATUS_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(OP_STATUS_NAMES) ? \ + OP_STATUS_NAMES[(_st_)] : \ + "?") + #define FST_ENTRY(_st_) [KEYMGR_ST_##_st_] = stringify(_st_) static const char *FST_NAMES[] = { /* clang-format off */ @@ -600,6 +631,33 @@ static void ot_keymgr_xchange_working_state( } } +static OtKeyMgrOpStatus ot_keymgr_get_op_status(const OtKeyMgrState *s) +{ + uint32_t op_status = FIELD_EX32(s->regs[R_OP_STATUS], OP_STATUS, STATUS); + + if (op_status <= KEYMGR_OP_STATUS_DONE_ERROR) { + return op_status; + } + g_assert_not_reached(); +} + +#define ot_keymgr_change_op_status(_s_, _op_status_) \ + ot_keymgr_xchange_op_status(_s_, _op_status_, __LINE__) + +static void ot_keymgr_xchange_op_status(OtKeyMgrState *s, + OtKeyMgrOpStatus op_status, int line) +{ + OtKeyMgrOpStatus prev_status = ot_keymgr_get_op_status(s); + if (prev_status != op_status) { + trace_ot_keymgr_change_op_status(s->ot_id, line, + OP_STATUS_NAME(prev_status), + prev_status, OP_STATUS_NAME(op_status), + op_status); + s->regs[R_OP_STATUS] = + FIELD_DP32(s->regs[R_OP_STATUS], OP_STATUS, STATUS, op_status); + } +} + #define ot_keymgr_schedule_fsm(_s_) \ ot_keymgr_xschedule_fsm(_s_, __func__, __LINE__) @@ -700,6 +758,26 @@ static void ot_keymgr_xchange_main_fsm_state(OtKeyMgrState *s, } } +static void ot_keymgr_start_operation(OtKeyMgrState *s) +{ + uint32_t ctrl = ot_shadow_reg_peek(&s->control); + int op = (int)FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); + + trace_ot_keymgr_operation(s->ot_id, OP_NAME(op), op); + + switch (op) { + case KEYMGR_OP_ADVANCE: + case KEYMGR_OP_DISABLE: + /* @todo: implement keymgr operations */ + qemu_log_mask(LOG_UNIMP, "%s: %s, Operation %s is not implemented.\n", + __func__, s->ot_id, OP_NAME(op)); + break; + default: + /* should only be called with a valid init operation */ + g_assert_not_reached(); + } +} + /* Tick the main FSM. Returns whether there was any state change this tick. */ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) { @@ -707,6 +785,7 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) OtKeyMgrFSMState state = s->state; bool op_start = s->regs[R_START] & R_START_EN_MASK; bool invalid_state = s->regs[R_FAULT_STATUS] & FAULT_STATUS_MASK; + bool init = false; uint32_t ctrl = ot_shadow_reg_peek(&s->control); trace_ot_keymgr_main_fsm_tick(s->ot_id, FST_NAME(s->state), s->state); @@ -760,7 +839,7 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); break; } - /* @todo: update `op_done_o` as `init_o = 1` here. */ + init = true; /* Retrieve Creator Root Key from OTP */ OtOTPKeyMgrSecret secret_share0 = { 0u }; @@ -782,6 +861,23 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) } break; case KEYMGR_ST_INIT: + ot_keymgr_change_working_state(s, KEYMGR_WORKING_STATE_INIT); + if (!s->enabled || invalid_state) { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + break; + } + if (!op_start) { + break; /* no state change if op_start is not set */ + } + uint32_t op = FIELD_EX32(ctrl, CONTROL_SHADOWED, OPERATION); + bool valid_op = (op == KEYMGR_OP_ADVANCE) || (op == KEYMGR_OP_DISABLE); + if (!valid_op) { + s->regs[R_ERR_CODE] |= R_ERR_CODE_INVALID_OP_MASK; + } else if (!s->op_state.op_req) { + s->op_state.op_req = true; + ot_keymgr_start_operation(s); + } + break; case KEYMGR_ST_CREATOR_ROOT_KEY: case KEYMGR_ST_OWNER_INT_KEY: case KEYMGR_ST_OWNER_KEY: @@ -796,7 +892,36 @@ static bool ot_keymgr_main_fsm_tick(OtKeyMgrState *s) break; } - /* @todo: update ERR_CODE, OP_STATUS & CFG_REGWEN based on FSM changes */ + /* update the last requested operation status */ + bool invalid_op = (bool)(s->regs[R_ERR_CODE] & R_ERR_CODE_INVALID_OP_MASK); + bool op_done = + s->op_state.op_req ? s->op_state.op_ack : (init || invalid_op); + if (op_done) { + s->op_state.op_req = false; + s->op_state.op_ack = false; + s->regs[R_START] &= ~R_START_EN_MASK; + if (s->regs[R_ERR_CODE] || s->regs[R_FAULT_STATUS]) { + ot_keymgr_update_alerts(s); + ot_keymgr_change_op_status(s, KEYMGR_OP_STATUS_DONE_ERROR); + } else { + ot_keymgr_change_op_status(s, KEYMGR_OP_STATUS_DONE_SUCCESS); + } + s->regs[R_INTR_STATE] |= INTR_OP_DONE_MASK; + ot_keymgr_update_irq(s); + } else if (op_start) { + ot_keymgr_change_op_status(s, KEYMGR_OP_STATUS_WIP); + } else { + ot_keymgr_change_op_status(s, KEYMGR_OP_STATUS_IDLE); + } + + /* lock CFG_REGWEN when an operation is ongoing */ + if (s->enabled && op_start) { + if (op_done) { + s->regs[R_CFG_REGWEN] |= R_CFG_REGWEN_EN_MASK; + } else { + s->regs[R_CFG_REGWEN] &= ~R_CFG_REGWEN_EN_MASK; + } + } return state != s->state; } @@ -1172,11 +1297,13 @@ static void ot_keymgr_write(void *opaque, hwaddr addr, uint64_t val64, ot_keymgr_update_alerts(s); } break; - case R_OP_STATUS: + case R_OP_STATUS: { val32 &= R_OP_STATUS_STATUS_MASK; - s->regs[reg] &= ~val32; /* RW1C */ - /* @todo: implement trace on R_OP_STATUS change? */ + ot_keymgr_change_op_status(s, s->regs[reg] & ~val32); /* RW1C */ + /* SW write may clear `WIP` here which may be immediately set again */ + ot_keymgr_fsm_tick(s); break; + } case R_ERR_CODE: val32 &= ERR_CODE_MASK; s->regs[reg] &= ~val32; /* RW1C */ @@ -1326,6 +1453,8 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) s->prng.reseed_req = false; s->prng.reseed_ack = false; s->prng.reseed_cnt = 0u; + s->op_state.op_req = false; + s->op_state.op_ack = false; /* reset key state */ memset(s->key_states, 0u, NUM_CDIS * sizeof(OtKeyMgrKey)); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index fe12267eec13a..56d6263c6faf1 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -279,6 +279,7 @@ ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool halte # ot_keymgr.c ot_keymgr_change_main_fsm_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_keymgr_change_op_status(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_change_working_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" ot_keymgr_dump_creator_root_key(const char *id, unsigned idx, bool valid, const char *hexstr) "%s: share:%u valid:%u %s" ot_keymgr_entropy(const char *id, unsigned reseed_cnt, bool resched) "%s: reseed_cnt: %u, resched: %u" @@ -289,6 +290,7 @@ ot_keymgr_io_write(const char *id, uint32_t addr, const char *regname, uint32_t ot_keymgr_irq(const char *id, uint32_t active, uint32_t mask, bool level) "%s: act:0x%08x msk:0x%08x lvl:%u" ot_keymgr_lc_signal(const char *id, int level) "%s: LC signal level:%d" ot_keymgr_main_fsm_tick(const char *id, const char *st, int nst) "%s: [%s:%d]" +ot_keymgr_operation(const char *id, const char *op, int nop) "%s: op:[%s:%d]" ot_keymgr_reset(const char *id, const char *stage) "%s: %s" ot_keymgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d" ot_keymgr_seed_missing(const char *id, unsigned ix) "%s: #%u"