diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index ceaa54e344098..f893cccfd530f 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -33,7 +33,11 @@ #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_kmac.h" +#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_prng.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -76,12 +80,24 @@ #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, "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) @@ -91,6 +107,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 +333,52 @@ 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 { + 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 { + uint8_t share0[KEYMGR_KEY_BYTES]; + uint8_t share1[KEYMGR_KEY_BYTES]; + bool valid; +} OtKeyMgrKey; + +typedef struct { + bool op_req; + bool op_ack; +} OtKeyMgrOpState; + 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,10 +390,21 @@ typedef struct OtKeyMgrState { uint8_t *sealing_sw_binding; uint8_t *attest_sw_binding; + bool enabled; + OtKeyMgrFSMState state; + OtKeyMgrPrng prng; + OtKeyMgrOpState op_state; 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; @@ -413,6 +483,73 @@ 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[] = { + 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 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 */ + 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_)] : "?") + +#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]); @@ -464,6 +601,370 @@ 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; + } +} + +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__) + +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)); + } +} + +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); + } + } +} + +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__) + +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; + } +} + +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) +{ + /* 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; + 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); + + 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; + 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: + 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 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_WIPE); + break; + } + init = true; + + /* 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 { + ot_keymgr_change_main_fsm_state(s, KEYMGR_ST_WIPE); + } + 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: + 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; + } + + /* 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; +} + +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); + } +} + +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_) @@ -642,7 +1143,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)) { @@ -796,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 */ @@ -872,6 +1375,11 @@ 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_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, @@ -916,6 +1424,12 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) c->parent_phases.enter(obj, type); } + timer_del(s->fsm_tick_timer); + + 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)); s->regs[R_CFG_REGWEN] = 0x1u; @@ -933,6 +1447,18 @@ 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; + 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)); + /* update IRQ and alert states */ ot_keymgr_update_irq(s); ot_keymgr_update_alerts(s); @@ -976,6 +1502,10 @@ 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->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); @@ -983,6 +1513,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/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..56d6263c6faf1 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -278,10 +278,21 @@ 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_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" +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" 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" ot_keymgr_update_alert(const char *id, int prev, int next) "%s: %d -> %d" diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 28199492b334c..c53d0f21d5370 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, @@ -1182,7 +1184,14 @@ 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), + OT_EG_SOC_DEVLINK("otp_ctrl", OTP_CTRL) + ), + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("edn-ep", 0u) + ), }, [OT_EG_SOC_DEV_CSRNG] = { .type = TYPE_OT_CSRNG, 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 */