diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index ad2b5dd61001f..d38dc28224af0 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -55,6 +55,7 @@ config OT_EDN config OT_ENTROPY_SRC select OT_NOISE_SRC + select OT_OTP_IF bool config OT_FLASH @@ -89,6 +90,7 @@ config OT_KEY_SINK config OT_KEYMGR select OT_KEY_SINK select OT_LC_CTRL + select OT_OTP_IF select OT_ROM_CTRL bool @@ -103,6 +105,7 @@ config OT_KMAC bool config OT_LC_CTRL + select OT_OTP_IF bool config OT_MBX @@ -117,23 +120,39 @@ config OT_OTBN select OT_BIGNUMBER bool -config OT_OTP - select OT_OTP_BE_IF - config OT_OTP_BE_IF bool config OT_OTP_DJ - select OT_OTP + select OT_OTP_ENGINE + select OT_OTP_IF + select OT_OTP_IMPL_IF select OT_PRESENT bool config OT_OTP_EG - select OT_OTP + select OT_OTP_ENGINE + select OT_OTP_IF + select OT_OTP_IMPL_IF + select OT_PRESENT + bool + +config OT_OTP_ENGINE + select OT_EDN + select OT_OTP_IF + select OT_OTP_IMPL_IF + select OT_PRESENT + bool + +config OT_OTP_IF + bool + +config OT_OTP_IMPL_IF bool config OT_OTP_OT_BE select OT_OTP_BE_IF + select OT_OTP_IF bool config OT_PINMUX_DJ @@ -184,6 +203,7 @@ config OT_SPI_HOST config OT_SRAM_CTRL select OT_PRESENT + select OT_OTP_IF bool config OT_TIMER diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 2049687268bc3..3acdfbc2ae9ba 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -34,10 +34,12 @@ system_ss.add(when: 'CONFIG_OT_LC_CTRL', if_true: files('ot_lc_ctrl.c')) system_ss.add(when: 'CONFIG_OT_MBX', if_true: files('ot_mbx.c')) system_ss.add(when: 'CONFIG_OT_NOISE_SRC', if_true: files('ot_noise_src.c')) system_ss.add(when: 'CONFIG_OT_OTBN', if_true: files('ot_otbn.c')) -system_ss.add(when: 'CONFIG_OT_OTP', if_true: files('ot_otp.c')) system_ss.add(when: 'CONFIG_OT_OTP_BE_IF', if_true: files('ot_otp_be_if.c')) system_ss.add(when: 'CONFIG_OT_OTP_DJ', if_true: files('ot_otp_dj.c')) system_ss.add(when: 'CONFIG_OT_OTP_EG', if_true: files('ot_otp_eg.c')) +system_ss.add(when: 'CONFIG_OT_OTP_ENGINE', if_true: files('ot_otp_engine.c')) +system_ss.add(when: 'CONFIG_OT_OTP_IF', if_true: files('ot_otp_if.c')) +system_ss.add(when: 'CONFIG_OT_OTP_IMPL_IF', if_true: files('ot_otp_impl_if.c')) system_ss.add(when: 'CONFIG_OT_OTP_OT_BE', if_true: files('ot_otp_ot_be.c')) system_ss.add(when: 'CONFIG_OT_PINMUX_EG', if_true: files('ot_pinmux_eg.c')) system_ss.add(when: 'CONFIG_OT_PINMUX_DJ', if_true: files('ot_pinmux_dj.c')) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index f21cf113f3d8f..07a292ec3d0c2 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -39,7 +39,7 @@ #include "hw/opentitan/ot_csrng.h" #include "hw/opentitan/ot_entropy_src.h" #include "hw/opentitan/ot_fifo32.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -353,7 +353,7 @@ struct OtCSRNGState { OtCSRNGQueue cmd_requests; OtEntropySrcState *entropy_src; - OtOTPState *otp_ctrl; + DeviceState *otp_ctrl; }; /* clang-format off */ @@ -1822,9 +1822,10 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, if (change) { xtrace_ot_csrng_info("handling CTRL change", val32); ot_csrng_handle_enable(s); - OtOTPClass *oc = - OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); - const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(s->otp_ctrl); + + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(oi); g_assert(hw_cfg); if (hw_cfg->en_csrng_sw_app_read_mb8 == OT_MULTIBITBOOL8_TRUE) { uint32_t sw_app_en = FIELD_EX32(val32, CTRL, SW_APP_ENABLE); @@ -1938,8 +1939,8 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, static Property ot_csrng_properties[] = { DEFINE_PROP_LINK("entropy-src", OtCSRNGState, entropy_src, TYPE_OT_ENTROPY_SRC, OtEntropySrcState *), - DEFINE_PROP_LINK("otp-ctrl", OtCSRNGState, otp_ctrl, TYPE_OT_OTP, - OtOTPState *), + DEFINE_PROP_LINK("otp-ctrl", OtCSRNGState, otp_ctrl, TYPE_OT_OTP_IF, + DeviceState *), DEFINE_PROP_END_OF_LIST(), }; @@ -2013,6 +2014,8 @@ static void ot_csrng_realize(DeviceState *dev, Error **errp) g_assert(s->entropy_src); g_assert(s->otp_ctrl); + + (void)OBJECT_CHECK(OtOTPIf, s->otp_ctrl, TYPE_OT_OTP_IF); } static void ot_csrng_init(Object *obj) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 2bfee3f802c8b..8cedd569bc3bd 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1,5 +1,5 @@ /* - * QEMU OpenTitan Earlgrey 1.0.0 Entropy Source device + * QEMU OpenTitan Entropy Source device * * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. @@ -43,7 +43,6 @@ #include "hw/opentitan/ot_entropy_src.h" #include "hw/opentitan/ot_fifo32.h" #include "hw/opentitan/ot_noise_src.h" -#include "hw/opentitan/ot_otp.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -489,7 +488,6 @@ struct OtEntropySrcState { char *ot_id; unsigned version; /* emulated version */ DeviceState *noise_src; - OtOTPState *otp_ctrl; }; static const uint16_t OtEDNFsmStateCode[] = { @@ -1803,8 +1801,6 @@ static Property ot_entropy_src_properties[] = { DEFINE_PROP_UINT32("version", OtEntropySrcState, version, 0), DEFINE_PROP_LINK("noise-src", OtEntropySrcState, noise_src, TYPE_DEVICE, DeviceState *), - DEFINE_PROP_LINK("otp-ctrl", OtEntropySrcState, otp_ctrl, TYPE_OT_OTP, - OtOTPState *), DEFINE_PROP_END_OF_LIST(), }; @@ -1901,7 +1897,6 @@ static void ot_entropy_src_realize(DeviceState *dev, Error **errp) /* emulated version should be specified */ g_assert(s->version > 0); g_assert(s->noise_src); - g_assert(s->otp_ctrl); (void)OBJECT_CHECK(OtNoiseSrcIf, s->noise_src, TYPE_OT_NOISE_SRC_IF); } diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index a4b59f803b422..ee5f3aa87ddcd 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -49,7 +49,7 @@ #include "hw/opentitan/ot_kmac.h" #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otbn.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_prng.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/qdev-properties.h" @@ -452,7 +452,7 @@ typedef struct OtKeyMgrState { uint8_t kmac_app; OtFlashState *flash_ctrl; OtLcCtrlState *lc_ctrl; - OtOTPState *otp_ctrl; + DeviceState *otp_ctrl; OtRomCtrlState *rom_ctrl; DeviceState *key_sinks[KEYMGR_KEY_SINK_COUNT]; char *seed_xstrs[KEYMGR_SEED_COUNT]; @@ -1068,8 +1068,9 @@ static size_t ot_keymgr_kdf_append_km_div(OtKeyMgrState *s) static size_t ot_keymgr_kdf_append_dev_id(OtKeyMgrState *s) { - OtOTPClass *otp_oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); - const OtOTPHWCfg *hw_cfg = otp_oc->get_hw_cfg(s->otp_ctrl); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(oi); ot_keymgr_kdf_push_bytes(s, hw_cfg->device_id, OT_OTP_HWCFG_DEVICE_ID_BYTES); @@ -1193,12 +1194,12 @@ static size_t ot_keymgr_kdf_append_key_version(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); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + share0); + oc->get_keymgr_secret(oi, OT_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( @@ -2332,8 +2333,8 @@ static Property ot_keymgr_properties[] = { DEFINE_PROP_UINT8("kmac-app", OtKeyMgrState, kmac_app, UINT8_MAX), DEFINE_PROP_LINK("lc-ctrl", OtKeyMgrState, lc_ctrl, TYPE_OT_LC_CTRL, OtLcCtrlState *), - DEFINE_PROP_LINK("otp-ctrl", OtKeyMgrState, otp_ctrl, TYPE_OT_OTP, - OtOTPState *), + DEFINE_PROP_LINK("otp-ctrl", OtKeyMgrState, otp_ctrl, TYPE_OT_OTP_IF, + DeviceState *), DEFINE_PROP_LINK("rom_ctrl", OtKeyMgrState, rom_ctrl, TYPE_OT_ROM_CTRL, OtRomCtrlState *), DEFINE_PROP_LINK("aes", OtKeyMgrState, key_sinks[KEYMGR_KEY_SINK_AES], @@ -2399,6 +2400,8 @@ static void ot_keymgr_reset_enter(Object *obj, ResetType type) g_assert(s->otp_ctrl); g_assert(s->rom_ctrl); + (void)OBJECT_CHECK(OtOTPIf, s->otp_ctrl, TYPE_OT_OTP_IF); + /* reset registers */ memset(s->regs, 0u, sizeof(s->regs)); s->regs[R_CFG_REGWEN] = 0x1u; diff --git a/hw/opentitan/ot_keymgr_dpe.c b/hw/opentitan/ot_keymgr_dpe.c index 422607e2df221..9ed3b22f796c0 100644 --- a/hw/opentitan/ot_keymgr_dpe.c +++ b/hw/opentitan/ot_keymgr_dpe.c @@ -41,7 +41,7 @@ #include "hw/opentitan/ot_kmac.h" #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otbn.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_prng.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/qdev-properties.h" @@ -410,7 +410,7 @@ typedef struct OtKeyMgrDpeState { OtKMACState *kmac; uint8_t kmac_app; OtLcCtrlState *lc_ctrl; - OtOTPState *otp; + DeviceState *otp_ctrl; OtRomCtrlState *rom_ctrl[NUM_ROM_DIGEST_INPUTS]; DeviceState *key_sinks[KEYMGR_DPE_KEY_SINK_COUNT]; char *seed_xstrs[KEYMGR_DPE_SEED_COUNT]; @@ -1027,9 +1027,9 @@ ot_keymgr_dpe_kdf_append_creator_seed(OtKeyMgrDpeState *s, bool *dvalid) { OtOTPKeyMgrSecret secret = { 0u }; - OtOTPClass *otp_oc = OBJECT_GET_CLASS(OtOTPClass, s->otp, TYPE_OT_OTP); - g_assert(otp_oc); - otp_oc->get_keymgr_secret(s->otp, OTP_KEYMGR_SECRET_CREATOR_SEED, &secret); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_SEED, &secret); ot_keymgr_dpe_kdf_push_bytes(s, secret.secret, OT_OTP_KEYMGR_SECRET_SIZE); *dvalid &= ot_keymgr_dpe_valid_data_check(secret.secret, @@ -1076,8 +1076,9 @@ static size_t ot_keymgr_dpe_kdf_append_km_div(OtKeyMgrDpeState *s, bool *dvalid) static size_t ot_keymgr_dpe_kdf_append_dev_id(OtKeyMgrDpeState *s, bool *dvalid) { - OtOTPClass *otp_oc = OBJECT_GET_CLASS(OtOTPClass, s->otp, TYPE_OT_OTP); - const OtOTPHWCfg *hw_cfg = otp_oc->get_hw_cfg(s->otp); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(oi); ot_keymgr_dpe_kdf_push_bytes(s, hw_cfg->device_id, OT_OTP_HWCFG_DEVICE_ID_BYTES); @@ -1106,8 +1107,9 @@ ot_keymgr_dpe_kdf_append_owner_seed(OtKeyMgrDpeState *s, bool *dvalid) { OtOTPKeyMgrSecret secret = { 0u }; - OtOTPClass *otp_oc = OBJECT_GET_CLASS(OtOTPClass, s->otp, TYPE_OT_OTP); - otp_oc->get_keymgr_secret(s->otp, OTP_KEYMGR_SECRET_OWNER_SEED, &secret); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_OWNER_SEED, &secret); ot_keymgr_dpe_kdf_push_bytes(s, secret.secret, OT_OTP_KEYMGR_SECRET_SIZE); *dvalid &= ot_keymgr_dpe_valid_data_check(secret.secret, @@ -1445,11 +1447,11 @@ static void ot_keymgr_dpe_xchange_main_fsm_state( static void ot_keymgr_dpe_get_root_key( OtKeyMgrDpeState *s, OtOTPKeyMgrSecret *share0, OtOTPKeyMgrSecret *share1) { - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp, TYPE_OT_OTP); - g_assert(oc); - oc->get_keymgr_secret(s->otp, OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, share0); - oc->get_keymgr_secret(s->otp, OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1, + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1, share1); if (trace_event_get_state(TRACE_OT_KEYMGR_DPE_DUMP_CREATOR_ROOT_KEY)) { @@ -2004,8 +2006,8 @@ static Property ot_keymgr_dpe_properties[] = { DEFINE_PROP_UINT8("kmac-app", OtKeyMgrDpeState, kmac_app, UINT8_MAX), DEFINE_PROP_LINK("lc-ctrl", OtKeyMgrDpeState, lc_ctrl, TYPE_OT_LC_CTRL, OtLcCtrlState *), - DEFINE_PROP_LINK("otp-ctrl", OtKeyMgrDpeState, otp, TYPE_OT_OTP, - OtOTPState *), + DEFINE_PROP_LINK("otp-ctrl", OtKeyMgrDpeState, otp_ctrl, TYPE_OT_OTP_IF, + DeviceState *), DEFINE_PROP_LINK("rom0", OtKeyMgrDpeState, rom_ctrl[0], TYPE_OT_ROM_CTRL, OtRomCtrlState *), DEFINE_PROP_LINK("rom1", OtKeyMgrDpeState, rom_ctrl[1], TYPE_OT_ROM_CTRL, @@ -2061,10 +2063,12 @@ static void ot_keymgr_dpe_reset_enter(Object *obj, ResetType type) g_assert(s->kmac); g_assert(s->kmac_app != UINT8_MAX); g_assert(s->lc_ctrl); - g_assert(s->otp); + g_assert(s->otp_ctrl); g_assert(s->rom_ctrl[0]); g_assert(s->rom_ctrl[1]); + (void)OBJECT_CHECK(OtOTPIf, s->otp_ctrl, TYPE_OT_OTP_IF); + s->key_sinks[KEYMGR_DPE_KEY_SINK_KMAC] = DEVICE(s->kmac); /* reset registers */ diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index 25be5342bd50d..93d8aa8179cd2 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -38,7 +38,7 @@ #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_kmac.h" #include "hw/opentitan/ot_lc_ctrl.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_socdbg_ctrl.h" #include "hw/qdev-properties.h" @@ -410,7 +410,7 @@ struct OtLcCtrlState { /* properties */ char *ot_id; - OtOTPState *otp_ctrl; + DeviceState *otp_ctrl; OtKMACState *kmac; char *raw_unlock_token_xstr; char *km_div_xstrs[LC_DIV_COUNT]; @@ -1136,14 +1136,14 @@ static void ot_lc_ctrl_kmac_handle_resp(void *opaque, const OtKMACAppRsp *rsp) static uint32_t ot_lc_ctrl_load_lc_info(OtLcCtrlState *s) { - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); OtLcCtrlStateValue lc_state; OtLcCtrlTransitionCountValue lc_tcount; uint8_t lc_valid; uint8_t secret_valid; const OtOTPTokens *tokens = NULL; - oc->get_lc_info(s->otp_ctrl, lc_tcount, lc_state, &lc_valid, &secret_valid, - &tokens); + oc->get_lc_info(oi, lc_tcount, lc_state, &lc_valid, &secret_valid, &tokens); if (s->force_raw) { trace_ot_lc_ctrl_load_lc_info_force_raw(s->ot_id); @@ -1192,9 +1192,9 @@ static uint32_t ot_lc_ctrl_load_lc_info(OtLcCtrlState *s) g_assert(tokens); uint32_t valid_bm = tokens->valid_bm; - for (unsigned otix = 0; otix < OTP_TOKEN_COUNT; otix++) { + for (unsigned otix = 0; otix < OT_OTP_TOKEN_COUNT; otix++) { /* beware: LC controller and OTP controller do not use same indices */ - unsigned ltix = otix + LC_TK_TEST_UNLOCK - OTP_TOKEN_TEST_UNLOCK; + unsigned ltix = otix + LC_TK_TEST_UNLOCK - OT_OTP_TOKEN_TEST_UNLOCK; /* 'valid' is OT terminology, should be considered as 'defined' */ bool valid = (bool)(valid_bm & (1u << otix)); if (valid) { @@ -1215,8 +1215,9 @@ static uint32_t ot_lc_ctrl_load_lc_info(OtLcCtrlState *s) static void ot_lc_ctrl_load_otp_hw_cfg(OtLcCtrlState *s) { - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); - const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(s->otp_ctrl); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(oi); static_assert(sizeof(hw_cfg->device_id) == LC_DEV_ID_WIDTH, "HW_CFG Device ID size does not match size in registers"); @@ -1294,7 +1295,8 @@ static void ot_lc_ctrl_handle_otp_ack(void *opaque, bool ack) static void ot_lc_ctrl_program_otp(OtLcCtrlState *s, unsigned lc_tcount, OtLcState lc_state) { - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); if (!oc->program_req) { qemu_log_mask(LOG_UNIMP, @@ -1310,7 +1312,7 @@ static void ot_lc_ctrl_program_otp(OtLcCtrlState *s, unsigned lc_tcount, const uint16_t *transition_val = s->lc_transitions[tcix]; const uint16_t *state_val = s->lc_states[stix]; - if (!oc->program_req(s->otp_ctrl, transition_val, state_val, + if (!oc->program_req(oi, transition_val, state_val, &ot_lc_ctrl_handle_otp_ack, s)) { trace_ot_lc_ctrl_error(s->ot_id, "OTP program request rejected"); s->regs[R_STATUS] |= R_STATUS_STATE_ERROR_MASK; @@ -2137,8 +2139,8 @@ static void ot_lc_ctrl_get_keymgr_div(const OtLcCtrlState *s, static Property ot_lc_ctrl_properties[] = { DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtLcCtrlState, ot_id), - DEFINE_PROP_LINK("otp-ctrl", OtLcCtrlState, otp_ctrl, TYPE_OT_OTP, - OtOTPState *), + DEFINE_PROP_LINK("otp-ctrl", OtLcCtrlState, otp_ctrl, TYPE_OT_OTP_IF, + DeviceState *), DEFINE_PROP_LINK("kmac", OtLcCtrlState, kmac, TYPE_OT_KMAC, OtKMACState *), DEFINE_PROP_STRING("raw_unlock_token", OtLcCtrlState, raw_unlock_token_xstr), @@ -2256,6 +2258,9 @@ static void ot_lc_ctrl_realize(DeviceState *dev, Error **errp) g_assert(s->otp_ctrl); g_assert(s->kmac); g_assert(s->kmac_app != UINT8_MAX); + + (void)OBJECT_CHECK(OtOTPIf, s->otp_ctrl, TYPE_OT_OTP_IF); + OtKMACClass *kc = OT_KMAC_GET_CLASS(s->kmac); g_assert(kc->connect_app); g_assert(kc->app_request); diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 85cd469cc60bc..a9bd0ed352d79 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1,7 +1,8 @@ /* - * QEMU OpenTitan Darjeeling One Time Programmable (OTP) memory controller + * QEMU OpenTitan Darjeeling One Time Programmable (OTP) implementation * * Copyright (c) 2023-2025 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. * * Author(s): * Emmanuel Blot @@ -29,59 +30,27 @@ * Based on OpenTitan 5fe6fe8605 */ +#define OT_OTP_COMPORTABLE_REGS + #include "qemu/osdep.h" -#include "qemu/bswap.h" #include "qemu/log.h" -#include "qemu/timer.h" -#include "qemu/typedefs.h" -#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_fifo32.h" -#include "hw/opentitan/ot_lc_ctrl.h" -#include "hw/opentitan/ot_otp_be_if.h" +#include "qom/object.h" #include "hw/opentitan/ot_otp_dj.h" -#include "hw/opentitan/ot_present.h" -#include "hw/opentitan/ot_prng.h" -#include "hw/opentitan/ot_pwrmgr.h" -#include "hw/qdev-properties-system.h" -#include "hw/qdev-properties.h" -#include "hw/registerfields.h" -#include "hw/riscv/ibex_common.h" -#include "hw/riscv/ibex_irq.h" -#include "hw/sysbus.h" -#include "sysemu/block-backend.h" +#include "hw/opentitan/ot_otp_engine.h" +#include "hw/opentitan/ot_otp_impl_if.h" #include "trace.h" -#undef OT_OTP_DEBUG - -#define NUM_IRQS 2u -#define NUM_ALERTS 5u -#define NUM_DAI_WORDS 2u -#define NUM_DIGEST_WORDS 2u #define NUM_ERROR_ENTRIES 24u #define NUM_PART 22u #define NUM_PART_BUF 7u #define NUM_PART_UNBUF 15u #define NUM_SRAM_KEY_REQ_SLOTS 4u #define NUM_SW_CFG_WINDOW_WORDS 4096u -#define NUM_ZER_WORDS 2u #define OTP_BYTE_ADDR_WIDTH 14u /* clang-format off */ -REG32(INTR_STATE, 0x000u) - SHARED_FIELD(INTR_OTP_OPERATION_DONE, 0u, 1u) - SHARED_FIELD(INTR_OTP_ERROR, 1u, 1u) -REG32(INTR_ENABLE, 0x004u) -REG32(INTR_TEST, 0x008u) -REG32(ALERT_TEST, 0x00cu) - SHARED_FIELD(ALERT_FATAL_MACRO_ERROR, 0u, 1u) - SHARED_FIELD(ALERT_FATAL_CHECK_ERROR, 1u, 1u) - SHARED_FIELD(ALERT_FATAL_BUS_INTEG_ERROR, 2u, 1u) - SHARED_FIELD(ALERT_FATAL_PRIM_OTP_ALERT, 3u, 1u) - SHARED_FIELD(ALERT_RECOV_PRIM_OTP_ALERT, 4u, 1u) +/* Core registers */ REG32(STATUS, 0x010u) FIELD(STATUS, VENDOR_TEST_ERROR, 0u, 1u) FIELD(STATUS, CREATOR_SW_CFG_ERROR, 1u, 1u) @@ -115,7 +84,6 @@ REG32(STATUS, 0x010u) FIELD(STATUS, DAI_IDLE, 29u, 1u) FIELD(STATUS, CHECK_PENDING, 30u, 1u) REG32(ERR_CODE_0, 0x014u) - SHARED_FIELD(ERR_CODE, 0u, 3u) REG32(ERR_CODE_1, 0x018u) REG32(ERR_CODE_2, 0x01cu) REG32(ERR_CODE_3, 0x020u) @@ -142,10 +110,6 @@ REG32(ERR_CODE_23, 0x070u) REG32(DIRECT_ACCESS_REGWEN, 0x074u) FIELD(DIRECT_ACCESS_REGWEN, REGWEN, 0u, 1u) REG32(DIRECT_ACCESS_CMD, 0x078u) - FIELD(DIRECT_ACCESS_CMD, RD, 0u, 1u) - FIELD(DIRECT_ACCESS_CMD, WR, 1u, 1u) - FIELD(DIRECT_ACCESS_CMD, DIGEST, 2u, 1u) - FIELD(DIRECT_ACCESS_CMD, ZEROIZE, 3u, 1u) REG32(DIRECT_ACCESS_ADDRESS, 0x07cu) FIELD(DIRECT_ACCESS_ADDRESS, ADDRESS, 0u, 14u) REG32(DIRECT_ACCESS_WDATA_0, 0x080u) @@ -163,7 +127,6 @@ REG32(CHECK_TIMEOUT, 0x09cu) REG32(INTEGRITY_CHECK_PERIOD, 0x0a0u) REG32(CONSISTENCY_CHECK_PERIOD, 0x0a4u) REG32(VENDOR_TEST_READ_LOCK, 0x0a8u) - SHARED_FIELD(READ_LOCK, 0u, 1u) REG32(CREATOR_SW_CFG_READ_LOCK, 0x0acu) REG32(OWNER_SW_CFG_READ_LOCK, 0x0b0u) REG32(OWNERSHIP_SLOT_STATE_READ_LOCK, 0x0b4u) @@ -217,7 +180,7 @@ REG32(SECRET2_DIGEST_1, 0x170u) REG32(SECRET3_DIGEST_0, 0x174u) REG32(SECRET3_DIGEST_1, 0x178u) -/* Software Config Window registers (at offset SW_CFG_WINDOW = +0x4000) */ +/* Software Config Window registers (at offset SW_CFG_WINDOW_OFFSET) */ REG32(VENDOR_TEST_SCRATCH, 0u) REG32(VENDOR_TEST_DIGEST, 56u) REG32(CREATOR_SW_CFG_AST_CFG, 64u) @@ -337,9 +300,7 @@ REG32(LC_TRANSITION_CNT, 16296u) REG32(LC_STATE, 16344u) /* clang-format on */ -#define VENDOR_TEST_SIZE 64u #define VENDOR_TEST_SCRATCH_SIZE 56u -#define CREATOR_SW_CFG_SIZE 304u #define CREATOR_SW_CFG_AST_CFG_SIZE 124u #define CREATOR_SW_CFG_AST_INIT_EN_SIZE 4u #define CREATOR_SW_CFG_OVERRIDES_SIZE 32u @@ -374,7 +335,6 @@ REG32(LC_STATE, 16344u) #define CREATOR_SW_CFG_RNG_ALERT_THRESHOLD_SIZE 4u #define CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST_SIZE 4u #define CREATOR_SW_CFG_SRAM_KEY_RENEW_EN_SIZE 4u -#define OWNER_SW_CFG_SIZE 600u #define OWNER_SW_CFG_ROM_ERROR_REPORTING_SIZE 4u #define OWNER_SW_CFG_ROM_BOOTSTRAP_DIS_SIZE 4u #define OWNER_SW_CFG_ROM_ALERT_CLASS_EN_SIZE 4u @@ -392,18 +352,15 @@ REG32(LC_STATE, 16344u) #define OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN_SIZE 4u #define OWNER_SW_CFG_MANUF_STATE_SIZE 4u #define OWNER_SW_CFG_ROM_RSTMGR_INFO_EN_SIZE 4u -#define OWNERSHIP_SLOT_STATE_SIZE 48u #define OWNERSHIP_SLOT_STATE_ROT_OWNER_AUTH_SIZE 16u #define OWNERSHIP_SLOT_STATE_PLAT_INTEG_AUTH_SIZE 16u #define OWNERSHIP_SLOT_STATE_PLAT_OWNER_AUTH_SIZE 16u -#define ROT_CREATOR_AUTH_SIZE 1424u #define ROT_CREATOR_AUTH_NON_RAW_MFW_CODESIGN_KEY_SIZE 160u #define ROT_CREATOR_AUTH_OWNERSHIP_STATE_SIZE 4u #define ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY_SIZE 160u #define ROT_CREATOR_AUTH_KEYMANIFEST_KEY_SIZE 160u #define ROT_CREATOR_AUTH_UNLOCK4XFER_KEY_SIZE 160u #define ROT_CREATOR_AUTH_IDENTITY_CERT_SIZE 768u -#define ROT_OWNER_AUTH_SLOT0_SIZE 328u #define ROT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 160u #define ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 160u #define ROT_OWNER_AUTH_SLOT1_SIZE 328u @@ -427,49 +384,24 @@ REG32(LC_STATE, 16344u) #define PLAT_OWNER_AUTH_SLOT3_SIZE 328u #define PLAT_OWNER_AUTH_SLOT3_KEYMANIFEST_KEY_SIZE 160u #define PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY_SIZE 160u -#define EXT_NVM_SIZE 1024u #define EXT_NVM_ANTIREPLAY_FRESHNESS_CNT_SIZE 1024u -#define ROM_PATCH_SIZE 9864u #define ROM_PATCH_DATA_SIZE 9192u -#define HW_CFG0_SIZE 72u #define HW_CFG0_DEVICE_ID_SIZE 32u #define HW_CFG0_MANUF_STATE_SIZE 32u -#define HW_CFG1_SIZE 16u #define HW_CFG1_SOC_DBG_STATE_SIZE 4u #define HW_CFG1_EN_CSRNG_SW_APP_READ_SIZE 1u #define HW_CFG1_EN_SRAM_IFETCH_SIZE 1u -#define SECRET0_SIZE 48u #define SECRET0_TEST_UNLOCK_TOKEN_SIZE 16u #define SECRET0_TEST_EXIT_TOKEN_SIZE 16u -#define SECRET1_SIZE 32u #define SECRET1_SRAM_DATA_KEY_SEED_SIZE 16u -#define SECRET2_SIZE 128u #define SECRET2_RMA_TOKEN_SIZE 16u #define SECRET2_CREATOR_ROOT_KEY_SHARE0_SIZE 32u #define SECRET2_CREATOR_ROOT_KEY_SHARE1_SIZE 32u #define SECRET2_CREATOR_SEED_SIZE 32u -#define SECRET3_SIZE 48u #define SECRET3_OWNER_SEED_SIZE 32u -#define LIFE_CYCLE_SIZE 88u -#define LC_TRANSITION_CNT_SIZE 48u -#define LC_STATE_SIZE 40u - -#define INTR_WMASK (INTR_OTP_OPERATION_DONE_MASK | INTR_OTP_ERROR_MASK) - -#define ALERT_WMASK \ - (ALERT_FATAL_MACRO_ERROR_MASK | ALERT_FATAL_CHECK_ERROR_MASK | \ - ALERT_FATAL_BUS_INTEG_ERROR_MASK | ALERT_FATAL_PRIM_OTP_ALERT_MASK | \ - ALERT_RECOV_PRIM_OTP_ALERT_MASK) -#define DIRECT_ACCESS_CMD_WMASK \ - (R_DIRECT_ACCESS_CMD_RD_MASK | R_DIRECT_ACCESS_CMD_WR_MASK | \ - R_DIRECT_ACCESS_CMD_DIGEST_MASK | R_DIRECT_ACCESS_CMD_ZEROIZE_MASK) - -#define CHECK_TRIGGER_WMASK \ - (R_CHECK_TRIGGER_INTEGRITY_MASK | R_CHECK_TRIGGER_CONSISTENCY_MASK) - -#define SW_CFG_WINDOW 0x4000u -#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) +#define SW_CFG_WINDOW_OFFSET 0x4000u +#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) @@ -479,44 +411,9 @@ REG32(LC_STATE, 16344u) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") -/* - * The OTP may be used before any CPU is started, This may cause the default - * virtual clock to stall, as the hart does not execute. OTP nevertheless may - * be active, updating the OTP content where write delays are still needed. - * Use the alternative clock source which counts even when the CPU is stalled. - */ -#define OT_OTP_HW_CLOCK QEMU_CLOCK_VIRTUAL_RT - -/* the following delays are arbitrary for now */ -#define DAI_DIGEST_DELAY_NS 50000u /* 50us */ -#define LCI_PROG_SCHED_NS 1000u /* 1us*/ - -/* The size of keys used for OTP scrambling */ -#define OTP_SCRAMBLING_KEY_WIDTH 128u -#define OTP_SCRAMBLING_KEY_BYTES ((OTP_SCRAMBLING_KEY_WIDTH) / 8u) - -/* Sizes of constants used for deriving scrambling keys (e.g. SRAM, OTBN) */ -#define SRAM_KEY_SEED_WIDTH (SRAM_DATA_KEY_SEED_SIZE * 8u) -#define KEY_MGR_KEY_WIDTH 256u -#define SRAM_KEY_WIDTH 128u -#define SRAM_NONCE_WIDTH 128u -#define OTBN_KEY_WIDTH 128u -#define OTBN_NONCE_WIDTH 64u - -#define SRAM_KEY_BYTES ((SRAM_KEY_WIDTH) / 8u) -#define SRAM_NONCE_BYTES ((SRAM_NONCE_WIDTH) / 8u) -#define OTBN_KEY_BYTES ((OTBN_KEY_WIDTH) / 8u) -#define OTBN_NONCE_BYTES ((OTBN_NONCE_WIDTH) / 8u) - -/* Need 128 bits of entropy to compute each 64-bit key part */ -#define OTP_ENTROPY_PRESENT_BITS \ - (((NUM_SRAM_KEY_REQ_SLOTS * SRAM_KEY_WIDTH) + OTBN_KEY_WIDTH) * 128u / 64u) -#define OTP_ENTROPY_PRESENT_WORDS (OTP_ENTROPY_PRESENT_BITS / 32u) -#define OTP_ENTROPY_NONCE_BITS \ - (NUM_SRAM_KEY_REQ_SLOTS * SRAM_NONCE_WIDTH + OTBN_NONCE_WIDTH) -#define OTP_ENTROPY_NONCE_WORDS (OTP_ENTROPY_NONCE_BITS / 32u) -#define OTP_ENTROPY_BUF_COUNT \ - (OTP_ENTROPY_PRESENT_WORDS + OTP_ENTROPY_NONCE_WORDS) +/* note: useless casts are required for GCC linter */ +static_assert((unsigned)R_STATUS == (unsigned)R_OTP_FIRST_IMPL_REG, + "Invalid register address"); typedef enum { OTP_PART_VENDOR_TEST, @@ -548,186 +445,19 @@ typedef enum { } OtOTPPartitionType; static_assert(OTP_PART_OTP_COUNT == NUM_PART, "Invalid partition count"); - -/* Error code (compliant with ERR_CODE registers) */ -typedef enum { - OTP_NO_ERROR, - OTP_MACRO_ERROR, - OTP_MACRO_ECC_CORR_ERROR, /* This is NOT an error */ - OTP_MACRO_ECC_UNCORR_ERROR, - OTP_MACRO_WRITE_BLANK_ERROR, - OTP_ACCESS_ERROR, - OTP_CHECK_FAIL_ERROR, /* Digest error */ - OTP_FSM_STATE_ERROR, -} OtOTPError; - -/* States of an unbuffered partition FSM */ -typedef enum { - OTP_UNBUF_RESET, - OTP_UNBUF_INIT, - OTP_UNBUF_INIT_WAIT, - OTP_UNBUF_IDLE, - OTP_UNBUF_READ, - OTP_UNBUF_READ_WAIT, - OTP_UNBUF_ERROR, -} OtOTPUnbufState; - -/* States of a buffered partition FSM */ -typedef enum { - OTP_BUF_RESET, - OTP_BUF_INIT, - OTP_BUF_INIT_WAIT, - OTP_BUF_INIT_DESCR, - OTP_BUF_INIT_DESCR_WAIT, - OTP_BUF_IDLE, - OTP_BUF_INTEG_SCR, - OTP_BUF_INTEG_SCR_WAIT, - OTP_BUF_INTEG_DIG_CLR, - OTP_BUF_INTEG_DIG, - OTP_BUF_INTEG_DIG_PAD, - OTP_BUF_INTEG_DIG_FIN, - OTP_BUF_INTEG_DIG_WAIT, - OTP_BUF_CNSTY_READ, - OTP_BUF_CNSTY_READ_WAIT, - OTP_BUF_ERROR, -} OtOTPBufState; - -typedef enum { - OTP_DAI_RESET, - OTP_DAI_INIT_OTP, - OTP_DAI_INIT_PART, - OTP_DAI_IDLE, - OTP_DAI_ERROR, - OTP_DAI_READ, - OTP_DAI_READ_WAIT, - OTP_DAI_DESCR, - OTP_DAI_DESCR_WAIT, - OTP_DAI_WRITE, - OTP_DAI_WRITE_WAIT, - OTP_DAI_SCR, - OTP_DAI_SCR_WAIT, - OTP_DAI_DIG_CLR, - OTP_DAI_DIG_READ, - OTP_DAI_DIG_READ_WAIT, - OTP_DAI_DIG, - OTP_DAI_DIG_PAD, - OTP_DAI_DIG_FIN, - OTP_DAI_DIG_WAIT, -} OtOTPDAIState; - -typedef enum { - OTP_LCI_RESET, - OTP_LCI_IDLE, - OTP_LCI_WRITE, - OTP_LCI_WRITE_WAIT, - OTP_LCI_ERROR, -} OtOTPLCIState; - -/* TODO: wr and rd lock need to be rewritten (not simple boolean) */ - -typedef struct { - uint16_t size; - uint16_t offset; - uint16_t digest_offset; - uint16_t zer_offset; - uint16_t hw_digest:1; - uint16_t sw_digest:1; - uint16_t secret:1; - uint16_t buffered:1; - uint16_t write_lock:1; - uint16_t read_lock:1; - uint16_t read_lock_csr:1; - uint16_t integrity:1; - uint16_t zeroizable:1; - uint16_t iskeymgr_creator:1; - uint16_t iskeymgr_owner:1; -} OtOTPPartDesc; - #define OT_OTP_DJ_PARTS -#define OTP_PART_LIFE_CYCLE_SIZE 88u - /* NOLINTNEXTLINE */ #include "ot_otp_dj_parts.c" -static_assert(OTP_PART_COUNT <= 64, "Maximum part count reached"); static_assert(OTP_PART_OTP_COUNT == OTP_PART_COUNT, "Invalid partition count"); - -#define OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) -#define OTP_ZER_ADDR_MASK (sizeof(uint64_t) - 1u) +static_assert(OTP_PART_COUNT <= 64, "Maximum part count reached"); static_assert(OTP_BYTE_ADDR_WIDTH == R_DIRECT_ACCESS_ADDRESS_ADDRESS_LENGTH, "OTP byte address width mismatch"); -typedef struct { - union { - OtOTPBufState b; - OtOTPUnbufState u; - } state; - struct { - uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ - uint64_t next_digest; /* computed HW digest to store into OTP cell */ - } buffer; /* only meaningful for buffered partitions */ - uint64_t digest; /* digest as read from OTP back end at init time */ - bool locked; - bool failed; - bool read_lock; - bool write_lock; -} OtOTPPartController; - -typedef struct { - QEMUTimer *delay; /* simulate delayed access completion */ - QEMUBH *digest_bh; /* write computed digest to OTP cell */ - OtOTPDAIState state; - int partition; /* current partition being worked on or -1 */ -} OtOTPDAIController; - -typedef struct { - QEMUTimer *prog_delay; /* OTP cell prog delay (use OT_OTP_HW_CLOCK) */ - OtOTPLCIState state; - OtOTPError error; - ot_otp_program_ack_fn ack_fn; - void *ack_data; - uint16_t data[OTP_PART_LIFE_CYCLE_SIZE / sizeof(uint16_t)]; - unsigned hpos; /* current offset in data */ -} OtOTPLCIController; - -typedef struct { - uint32_t *storage; /* overall buffer for the storage backend */ - uint32_t *data; /* data buffer (all partitions) */ - uint32_t *ecc; /* ecc buffer for date */ - unsigned size; /* overall storage size in bytes */ - unsigned data_size; /* data buffer size in bytes */ - unsigned ecc_size; /* ecc buffer size in bytes */ - unsigned ecc_bit_count; /* count of ECC bit for each data granule */ - unsigned ecc_granule; /* size of a granule in bytes */ -} OtOTPStorage; - -typedef struct { - QEMUBH *bh; - uint16_t signal; /* each bit tells if signal needs to be handled */ - uint16_t level; /* level of the matching signal */ - uint16_t current_level; /* current level of all signals */ -} OtOTPLcBroadcast; - -static_assert(OT_OTP_LC_BROADCAST_COUNT < 8 * sizeof(uint16_t), - "Invalid OT_OTP_LC_BROADCAST_COUNT"); - -typedef struct { - QEMUBH *entropy_bh; - OtPresentState *present; - OtPrngState *prng; - OtFifo32 entropy_buf; - bool edn_sched; -} OtOTPKeyGen; - -typedef struct { - uint8_t key[SRAM_KEY_BYTES]; - uint8_t nonce[SRAM_NONCE_BYTES]; -} OtOTPScrmblKeyInit; - struct OtOTPDjState { - OtOTPState parent_obj; + OtOTPEngineState parent_obj; struct { MemoryRegion ctrl; @@ -736,47 +466,11 @@ struct OtOTPDjState { MemoryRegion swcfg; } sub; } mmio; - QEMUBH *pwr_otp_bh; - IbexIRQ irqs[NUM_IRQS]; - IbexIRQ alerts[NUM_ALERTS]; - IbexIRQ pwc_otp_rsp; - - uint32_t *regs; - uint32_t alert_bm; - - OtOTPLcBroadcast lc_broadcast; - OtOTPDAIController *dai; - OtOTPLCIController *lci; - OtOTPPartController *partctrls; - OtOTPKeyGen *keygen; - OtOTPScrmblKeyInit *scrmbl_key_init; - OtOtpBeCharacteristics be_chars; - uint64_t digest_iv; - uint8_t digest_const[16u]; - uint64_t sram_iv; - uint8_t sram_const[16u]; - /* OTP scrambling key constants, not constants for deriving other keys */ - uint8_t *otp_scramble_keys[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - uint8_t *inv_default_parts[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - - OtOTPStorage *otp; - OtOTPHWCfg *hw_cfg; - OtOTPTokens *tokens; - char *hexstr; +}; - char *ot_id; - BlockBackend *blk; /* OTP host backend */ - OtOtpBeIf *otp_backend; - OtEDNState *edn; - char *scrmbl_key_xstr; - char *digest_const_xstr; - char *digest_iv_xstr; - char *sram_const_xstr; - char *sram_iv_xstr; - char *otp_scramble_key_xstrs[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - char *inv_default_part_xstrs[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - uint8_t edn_ep; - bool fatal_escalate; +struct OtOTPDjClass { + OtOTPEngineClass parent_class; + ResettablePhases parent_phases; }; #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) @@ -881,1777 +575,202 @@ static const char *REG_NAMES[REGS_COUNT] = { }; #undef REG_NAME_ENTRY -#define OTP_NAME_ENTRY(_st_) [_st_] = stringify(_st_) - -static const char *DAI_STATE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_DAI_RESET), - OTP_NAME_ENTRY(OTP_DAI_INIT_OTP), - OTP_NAME_ENTRY(OTP_DAI_INIT_PART), - OTP_NAME_ENTRY(OTP_DAI_IDLE), - OTP_NAME_ENTRY(OTP_DAI_ERROR), - OTP_NAME_ENTRY(OTP_DAI_READ), - OTP_NAME_ENTRY(OTP_DAI_READ_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DESCR), - OTP_NAME_ENTRY(OTP_DAI_DESCR_WAIT), - OTP_NAME_ENTRY(OTP_DAI_WRITE), - OTP_NAME_ENTRY(OTP_DAI_WRITE_WAIT), - OTP_NAME_ENTRY(OTP_DAI_SCR), - OTP_NAME_ENTRY(OTP_DAI_SCR_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DIG_CLR), - OTP_NAME_ENTRY(OTP_DAI_DIG_READ), - OTP_NAME_ENTRY(OTP_DAI_DIG_READ_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DIG), - OTP_NAME_ENTRY(OTP_DAI_DIG_PAD), - OTP_NAME_ENTRY(OTP_DAI_DIG_FIN), - OTP_NAME_ENTRY(OTP_DAI_DIG_WAIT), - /* clang-format on */ -}; - -static const char *LCI_STATE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_LCI_RESET), - OTP_NAME_ENTRY(OTP_LCI_IDLE), - OTP_NAME_ENTRY(OTP_LCI_WRITE), - OTP_NAME_ENTRY(OTP_LCI_WRITE_WAIT), - OTP_NAME_ENTRY(OTP_LCI_ERROR), - /* clang-format on */ -}; +#define OT_OTP_NAME_ENTRY(_st_) [OT_OTP_##_st_] = stringify(OT_OTP_##_st_) static const char *OTP_TOKEN_NAMES[] = { /* clang-format off */ - OTP_NAME_ENTRY(OTP_TOKEN_TEST_UNLOCK), - OTP_NAME_ENTRY(OTP_TOKEN_TEST_EXIT), - OTP_NAME_ENTRY(OTP_TOKEN_RMA), - /* clang-format on */ -}; - -static const char *PART_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_PART_VENDOR_TEST), - OTP_NAME_ENTRY(OTP_PART_CREATOR_SW_CFG), - OTP_NAME_ENTRY(OTP_PART_OWNER_SW_CFG), - OTP_NAME_ENTRY(OTP_PART_OWNERSHIP_SLOT_STATE), - OTP_NAME_ENTRY(OTP_PART_ROT_CREATOR_AUTH), - OTP_NAME_ENTRY(OTP_PART_ROT_OWNER_AUTH_SLOT0), - OTP_NAME_ENTRY(OTP_PART_ROT_OWNER_AUTH_SLOT1), - OTP_NAME_ENTRY(OTP_PART_PLAT_INTEG_AUTH_SLOT0), - OTP_NAME_ENTRY(OTP_PART_PLAT_INTEG_AUTH_SLOT1), - OTP_NAME_ENTRY(OTP_PART_PLAT_OWNER_AUTH_SLOT0), - OTP_NAME_ENTRY(OTP_PART_PLAT_OWNER_AUTH_SLOT1), - OTP_NAME_ENTRY(OTP_PART_PLAT_OWNER_AUTH_SLOT2), - OTP_NAME_ENTRY(OTP_PART_PLAT_OWNER_AUTH_SLOT3), - OTP_NAME_ENTRY(OTP_PART_EXT_NVM), - OTP_NAME_ENTRY(OTP_PART_ROM_PATCH), - OTP_NAME_ENTRY(OTP_PART_HW_CFG0), - OTP_NAME_ENTRY(OTP_PART_HW_CFG1), - OTP_NAME_ENTRY(OTP_PART_SECRET0), - OTP_NAME_ENTRY(OTP_PART_SECRET1), - OTP_NAME_ENTRY(OTP_PART_SECRET2), - OTP_NAME_ENTRY(OTP_PART_SECRET3), - OTP_NAME_ENTRY(OTP_PART_LIFE_CYCLE), - /* fake partitions */ - OTP_NAME_ENTRY(OTP_ENTRY_DAI), - OTP_NAME_ENTRY(OTP_ENTRY_KDI), - /* clang-format on */ -}; - -static const char *ERR_CODE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_NO_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ECC_CORR_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ECC_UNCORR_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_WRITE_BLANK_ERROR), - OTP_NAME_ENTRY(OTP_ACCESS_ERROR), - OTP_NAME_ENTRY(OTP_CHECK_FAIL_ERROR), - OTP_NAME_ENTRY(OTP_FSM_STATE_ERROR), + OT_OTP_NAME_ENTRY(TOKEN_TEST_UNLOCK), + OT_OTP_NAME_ENTRY(TOKEN_TEST_EXIT), + OT_OTP_NAME_ENTRY(TOKEN_RMA), /* clang-format on */ }; -#undef OTP_NAME_ENTRY - -#define BUF_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(BUF_STATE_NAMES) ? \ - BUF_STATE_NAMES[(_st_)] : \ - "?") -#define UNBUF_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(UNBUF_STATE_NAMES) ? \ - UNBUF_STATE_NAMES[(_st_)] : \ - "?") -#define DAI_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(DAI_STATE_NAMES) ? \ - DAI_STATE_NAMES[(_st_)] : \ - "?") -#define LCI_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(LCI_STATE_NAMES) ? \ - LCI_STATE_NAMES[(_st_)] : \ - "?") #define OTP_TOKEN_NAME(_tk_) \ ((unsigned)(_tk_) < ARRAY_SIZE(OTP_TOKEN_NAMES) ? \ OTP_TOKEN_NAMES[(_tk_)] : \ "?") -#define PART_NAME(_pt_) \ - (((unsigned)(_pt_)) < ARRAY_SIZE(PART_NAMES) ? PART_NAMES[(_pt_)] : "?") -#define ERR_CODE_NAME(_err_) \ - (((unsigned)(_err_)) < ARRAY_SIZE(ERR_CODE_NAMES) ? \ - ERR_CODE_NAMES[(_err_)] : \ - "?") - -static void ot_otp_dj_dai_set_error(OtOTPDjState *s, OtOTPError err); - -static void -ot_otp_dj_dai_change_state_line(OtOTPDjState *s, OtOTPDAIState state, int line); - -#define DAI_CHANGE_STATE(_s_, _st_) \ - ot_otp_dj_dai_change_state_line(_s_, _st_, __LINE__) - -static void -ot_otp_dj_lci_change_state_line(OtOTPDjState *s, OtOTPLCIState state, int line); - -#define LCI_CHANGE_STATE(_s_, _st_) \ - ot_otp_dj_lci_change_state_line(_s_, _st_, __LINE__) - -#ifdef OT_OTP_DEBUG -#define OT_OTP_HEXSTR_SIZE 256u -#define TRACE_OTP(msg, ...) qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); -#define ot_otp_hexdump(_s_, _b_, _l_) \ - ot_common_lhexdump((const uint8_t *)_b_, _l_, false, (_s_)->hexstr, \ - OT_OTP_HEXSTR_SIZE) -#else -#define TRACE_OTP(msg, ...) -#define ot_otp_hexdump(_s_, _b_, _l_) -#endif - -static inline unsigned ot_otp_dj_part_data_offset(unsigned pix) -{ - return (unsigned)(OtOTPPartDescs[pix].offset); -} - -static inline unsigned ot_otp_dj_part_data_byte_size(unsigned pix) -{ - size_t size = OtOTPPartDescs[pix].size; - - if (OtOTPPartDescs[pix].hw_digest || OtOTPPartDescs[pix].sw_digest) { - size -= sizeof(uint32_t) * NUM_DIGEST_WORDS; - } - - if (OtOTPPartDescs[pix].zeroizable) { - size -= sizeof(uint32_t) * NUM_ZER_WORDS; - } - - return (unsigned)size; -} - -static void ot_otp_dj_update_irqs(OtOTPDjState *s) -{ - uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - int level = (int)((levels >> ix) & 0x1u); - if (level != ibex_irq_get_level(&s->irqs[ix])) { - trace_ot_otp_update_irq(s->ot_id, ibex_irq_get_level(&s->irqs[ix]), - level); - } - ibex_irq_set(&s->irqs[ix], level); - } -} - -static void ot_otp_dj_update_alerts(OtOTPDjState *s) -{ - uint32_t levels = s->regs[R_ALERT_TEST]; - - levels |= s->alert_bm; - - 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_otp_update_alert(s->ot_id, - ibex_irq_get_level(&s->alerts[ix]), - level); - } - ibex_irq_set(&s->alerts[ix], level); - } - /* alert test is transient */ - if (s->regs[R_ALERT_TEST]) { - s->regs[R_ALERT_TEST] = 0; - - levels = s->alert_bm; - 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_otp_update_alert(s->ot_id, - ibex_irq_get_level(&s->alerts[ix]), - level); - } - ibex_irq_set(&s->alerts[ix], level); - } - } -} - -static bool ot_otp_dj_is_wide_granule(unsigned part_ix, unsigned address) -{ - if (part_ix < OTP_PART_COUNT) { - if (OtOTPPartDescs[part_ix].secret) { - return true; - } - - if (OtOTPPartDescs[part_ix].digest_offset == - (address & OTP_DIGEST_ADDR_MASK)) { - return true; - } - } - - return false; -} - -static bool ot_otp_dj_is_buffered(unsigned part_ix) -{ - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].buffered; - } - - return false; -} - -static bool ot_otp_dj_is_secret(unsigned part_ix) -{ - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].secret; - } - - return false; -} - -static bool ot_otp_dj_is_backend_ecc_enabled(const OtOTPDjState *s) -{ - OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); - if (!bec->is_ecc_enabled) { - return true; - } - - return bec->is_ecc_enabled(s->otp_backend); -} - -static bool ot_otp_dj_is_ecc_enabled(const OtOTPDjState *s) +static uint32_t ot_otp_dj_get_status(const OtOTPEngineState *s) { - return s->otp->ecc_granule == sizeof(uint16_t) && - ot_otp_dj_is_backend_ecc_enabled(s); -} - -static bool ot_otp_dj_has_digest(unsigned partition) -{ - return OtOTPPartDescs[partition].hw_digest || - OtOTPPartDescs[partition].sw_digest; -} + uint32_t status; -static void ot_otp_dj_disable_all_partitions(OtOTPDjState *s) -{ - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); + status = FIELD_DP32(s->regs[R_STATUS], STATUS, DAI_IDLE, + !ot_otp_engine_dai_is_busy(s)); - for (unsigned pix = 0; pix < OTP_PART_COUNT; pix++) { - OtOTPPartController *pctrl = &s->partctrls[pix]; - pctrl->failed = true; - } + return status; } -static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part, OtOTPError err) +static uint64_t ot_otp_dj_reg_read(void *opaque, hwaddr addr, unsigned size) { - g_assert(part < OTP_ENTRY_COUNT); - - uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; - if (errval || errval != s->regs[R_ERR_CODE_0 + part]) { - trace_ot_otp_set_error(s->ot_id, PART_NAME(part), part, - ERR_CODE_NAME(err), err); - } - s->regs[R_ERR_CODE_0 + part] = errval; + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; + uint32_t val32; - switch (err) { - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: - s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; - ot_otp_dj_update_alerts(s); + hwaddr reg = R32_OFF(addr); + switch (reg) { + case R_INTR_STATE: + case R_INTR_ENABLE: + case R_ERR_CODE_0 ... R_ERR_CODE_23: + case R_DIRECT_ACCESS_WDATA_0: + case R_DIRECT_ACCESS_WDATA_1: + case R_DIRECT_ACCESS_RDATA_0: + case R_DIRECT_ACCESS_RDATA_1: + case R_DIRECT_ACCESS_ADDRESS: + case R_VENDOR_TEST_READ_LOCK ... R_ROM_PATCH_READ_LOCK: + case R_CHECK_TRIGGER_REGWEN: + case R_CHECK_REGWEN: + val32 = s->regs[reg]; + break; + case R_STATUS: + val32 = ot_otp_dj_get_status(s); + break; + case R_DIRECT_ACCESS_REGWEN: + /* disabled either if SW locked, or if DAI is busy. */ + val32 = s->regs[reg]; + val32 &= FIELD_DP32(0u, DIRECT_ACCESS_REGWEN, REGWEN, + (uint32_t)!ot_otp_engine_dai_is_busy(s)); break; /* NOLINTNEXTLINE */ - case OTP_MACRO_ECC_CORR_ERROR: - /* - * "The corresponding controller automatically recovers from this error - * when issuing a new command." - */ + case R_DIRECT_ACCESS_CMD: + case R_CHECK_TRIGGER: + val32 = 0; /* R0W1C */ break; - case OTP_MACRO_WRITE_BLANK_ERROR: + case R_CHECK_TIMEOUT: + case R_INTEGRITY_CHECK_PERIOD: + case R_CONSISTENCY_CHECK_PERIOD: + /* @todo: not yet implemented, but these are R/W registers */ + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(reg)); + val32 = s->regs[reg]; break; - case OTP_ACCESS_ERROR: - s->regs[R_STATUS] |= R_STATUS_DAI_ERROR_MASK; + case R_VENDOR_TEST_DIGEST_0 ... R_SECRET3_DIGEST_1: + /* + * In all partitions with a digest, the digest itself is ALWAYS + * readable. + */ + val32 = c->get_part_digest_reg(s, reg - R_VENDOR_TEST_DIGEST_0); break; - case OTP_CHECK_FAIL_ERROR: - case OTP_FSM_STATE_ERROR: - s->alert_bm |= ALERT_FATAL_CHECK_ERROR_MASK; - ot_otp_dj_update_alerts(s); + case R_INTR_TEST: + case R_ALERT_TEST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: W/O register 0x02%" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + val32 = 0; break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); + val32 = 0; break; } - if (s->alert_bm & ALERT_FATAL_CHECK_ERROR_MASK) { - ot_otp_dj_disable_all_partitions(s); - s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; - error_report("%s: %s: OTP disabled on fatal error", __func__, s->ot_id); - } + uint32_t pc = ibex_get_current_pc(); + trace_ot_otp_io_reg_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); - if (err != OTP_NO_ERROR) { - s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; - ot_otp_dj_update_irqs(s); - } + return (uint64_t)val32; } -static uint32_t ot_otp_dj_dai_is_busy(const OtOTPDjState *s) +static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { - return s->dai->state != OTP_DAI_IDLE; -} + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; + uint32_t val32 = (uint32_t)value; -static uint32_t ot_otp_dj_get_status(const OtOTPDjState *s) -{ - uint32_t status; + hwaddr reg = R32_OFF(addr); - status = FIELD_DP32(s->regs[R_STATUS], STATUS, DAI_IDLE, - !ot_otp_dj_dai_is_busy(s)); + uint32_t pc = ibex_get_current_pc(); - return status; -} + trace_ot_otp_io_reg_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); -static int ot_otp_dj_get_part_from_address(const OtOTPDjState *s, hwaddr addr) -{ - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; - if ((addr >= part->offset) && - ((addr + sizeof(uint32_t)) <= (part->offset + part->size))) { - trace_ot_otp_addr_to_part(s->ot_id, (uint32_t)addr, PART_NAME(ix), - ix); - return (OtOTPPartitionType)ix; + switch (reg) { + case R_DIRECT_ACCESS_CMD: + case R_DIRECT_ACCESS_ADDRESS: + case R_DIRECT_ACCESS_WDATA_0: + case R_DIRECT_ACCESS_WDATA_1: + case R_VENDOR_TEST_READ_LOCK ... R_ROM_PATCH_READ_LOCK: + if (!(s->regs[R_DIRECT_ACCESS_REGWEN] & + R_DIRECT_ACCESS_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_DIRECT_ACCESS_REGWEN), + REG_NAME(reg)); + return; + } + break; + case R_CHECK_TRIGGER: + if (!(s->regs[R_CHECK_TRIGGER_REGWEN] & + R_CHECK_TRIGGER_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_CHECK_TRIGGER_REGWEN), + REG_NAME(reg)); + return; + } + break; + case R_CHECK_TIMEOUT: + case R_INTEGRITY_CHECK_PERIOD: + case R_CONSISTENCY_CHECK_PERIOD: + if (!(s->regs[R_CHECK_REGWEN] & R_CHECK_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_CHECK_REGWEN), + REG_NAME(reg)); + return; } - } - - return -1; -} - -static uint8_t ot_otp_dj_compute_ecc_u16(uint16_t data) -{ - uint32_t data_o = (uint32_t)data; - - data_o |= __builtin_parity(data_o & 0x00ad5bu) << 16u; - data_o |= __builtin_parity(data_o & 0x00366du) << 17u; - data_o |= __builtin_parity(data_o & 0x00c78eu) << 18u; - data_o |= __builtin_parity(data_o & 0x0007f0u) << 19u; - data_o |= __builtin_parity(data_o & 0x00f800u) << 20u; - data_o |= __builtin_parity(data_o & 0x1fffffu) << 21u; - - return (uint8_t)(data_o >> 16u); -} - -static uint16_t ot_otp_dj_compute_ecc_u32(uint32_t data) -{ - uint16_t data_lo = (uint16_t)(data & UINT16_MAX); - uint16_t data_hi = (uint16_t)(data >> 16u); - - uint16_t ecc_lo = (uint16_t)ot_otp_dj_compute_ecc_u16(data_lo); - uint16_t ecc_hi = (uint16_t)ot_otp_dj_compute_ecc_u16(data_hi); - - return (ecc_hi << 8u) | ecc_lo; -} - -static uint32_t ot_otp_dj_compute_ecc_u64(uint64_t data) -{ - uint32_t data_lo = (uint32_t)(data & UINT32_MAX); - uint32_t data_hi = (uint32_t)(data >> 32u); - - uint32_t ecc_lo = (uint32_t)ot_otp_dj_compute_ecc_u32(data_lo); - uint32_t ecc_hi = (uint32_t)ot_otp_dj_compute_ecc_u32(data_hi); - - return (ecc_hi << 16u) | ecc_lo; -} - -static uint32_t ot_otp_dj_verify_ecc_22_16_u16(const OtOTPDjState *s, - uint32_t data_i, unsigned *err_o) -{ - unsigned syndrome = 0u; - - syndrome |= __builtin_parity(data_i & 0x01ad5bu) << 0u; - syndrome |= __builtin_parity(data_i & 0x02366du) << 1u; - syndrome |= __builtin_parity(data_i & 0x04c78eu) << 2u; - syndrome |= __builtin_parity(data_i & 0x0807f0u) << 3u; - syndrome |= __builtin_parity(data_i & 0x10f800u) << 4u; - syndrome |= __builtin_parity(data_i & 0x3fffffu) << 5u; - - unsigned err = (syndrome >> 5u) & 1u; - if (!err && (syndrome & 0x1fu)) { - err = 2u; - } - - *err_o = err; - - if (!err) { - return data_i & UINT16_MAX; - } - - uint32_t data_o = 0; - -#define OTP_ECC_RECOVER(_sy_, _di_, _ix_) \ - ((unsigned)((syndrome == (_sy_)) ^ (bool)((_di_) & (1u << (_ix_)))) \ - << (_ix_)) - - data_o |= OTP_ECC_RECOVER(0x23u, data_i, 0u); - data_o |= OTP_ECC_RECOVER(0x25u, data_i, 1u); - data_o |= OTP_ECC_RECOVER(0x26u, data_i, 2u); - data_o |= OTP_ECC_RECOVER(0x27u, data_i, 3u); - data_o |= OTP_ECC_RECOVER(0x29u, data_i, 4u); - data_o |= OTP_ECC_RECOVER(0x2au, data_i, 5u); - data_o |= OTP_ECC_RECOVER(0x2bu, data_i, 6u); - data_o |= OTP_ECC_RECOVER(0x2cu, data_i, 7u); - data_o |= OTP_ECC_RECOVER(0x2du, data_i, 8u); - data_o |= OTP_ECC_RECOVER(0x2eu, data_i, 9u); - data_o |= OTP_ECC_RECOVER(0x2fu, data_i, 10u); - data_o |= OTP_ECC_RECOVER(0x31u, data_i, 11u); - data_o |= OTP_ECC_RECOVER(0x32u, data_i, 12u); - data_o |= OTP_ECC_RECOVER(0x33u, data_i, 13u); - data_o |= OTP_ECC_RECOVER(0x34u, data_i, 14u); - data_o |= OTP_ECC_RECOVER(0x35u, data_i, 15u); - -#undef OTP_ECC_RECOVER - - if (err > 1u) { - trace_ot_otp_ecc_unrecoverable_error(s->ot_id, data_i & UINT16_MAX); - } else { - if ((data_i & UINT16_MAX) != data_o) { - trace_ot_otp_ecc_recovered_error(s->ot_id, data_i & UINT16_MAX, - data_o); - } else { - /* ECC bit is corrupted */ - trace_ot_otp_ecc_parity_error(s->ot_id, data_i & UINT16_MAX, - data_i >> 16u); - } - } - - return data_o; -} - -static uint32_t ot_otp_dj_verify_ecc(const OtOTPDjState *s, uint32_t data, - uint32_t ecc, unsigned *err) -{ - uint32_t data_lo_i, data_lo_o, data_hi_i, data_hi_o; - unsigned err_lo, err_hi; - - data_lo_i = (data & 0xffffu) | ((ecc & 0xffu) << 16u); - data_lo_o = ot_otp_dj_verify_ecc_22_16_u16(s, data_lo_i, &err_lo); - - data_hi_i = (data >> 16u) | (((ecc >> 8u) & 0xffu) << 16u); - data_hi_o = ot_otp_dj_verify_ecc_22_16_u16(s, data_hi_i, &err_hi); - - *err |= err_lo | err_hi; - - return (data_hi_o << 16u) | data_lo_o; -} - -static uint64_t ot_otp_dj_apply_digest_ecc(OtOTPDjState *s, unsigned partition, - uint64_t digest, uint32_t ecc) -{ - uint32_t dig_lo = (uint32_t)(digest & UINT32_MAX); - uint32_t dig_hi = (uint32_t)(digest >> 32u); - - unsigned err = 0; - dig_lo = ot_otp_dj_verify_ecc(s, dig_lo, ecc & 0xffffu, &err); - dig_hi = ot_otp_dj_verify_ecc(s, dig_hi, ecc >> 16u, &err); - digest = (((uint64_t)dig_hi) << 32u) | ((uint64_t)dig_lo); - - if (err) { - OtOTPError otp_err = - (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : OTP_MACRO_ECC_CORR_ERROR; - /* - * Note: need to check if any caller could override the error/state - * in this case - */ - ot_otp_dj_set_error(s, partition, otp_err); - } - - return digest; -} - -static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned part_ix) -{ - g_assert(ot_otp_dj_is_ecc_enabled(s)); - - unsigned start = OtOTPPartDescs[part_ix].offset >> 2u; - unsigned end = - (ot_otp_dj_is_buffered((int)part_ix) && ot_otp_dj_has_digest(part_ix)) ? - (unsigned)(OtOTPPartDescs[part_ix].digest_offset >> 2u) : - start + (unsigned)(OtOTPPartDescs[part_ix].size >> 2u); - - g_assert(start < end && (end / sizeof(uint32_t)) < s->otp->data_size); - for (unsigned ix = start; ix < end; ix++) { - unsigned err = 0; - uint32_t *word = &s->otp->data[ix]; - uint16_t ecc = ((const uint16_t *)s->otp->ecc)[ix]; - *word = ot_otp_dj_verify_ecc(s, *word, (uint32_t)ecc, &err); - if (err) { - OtOTPError otp_err = (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : - OTP_MACRO_ECC_CORR_ERROR; - /* - * Note: need to check if any caller could override the error/state - * in this case - */ - ot_otp_dj_set_error(s, part_ix, otp_err); - if (err > 1) { - trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(part_ix), - part_ix, ix << 2u, *word, ecc); - s->partctrls[part_ix].failed = true; - return -1; - } - } - } - - return 0; -} - -static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) -{ - g_assert(!ot_otp_dj_is_buffered(part_ix)); - - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; - - if (offset == UINT16_MAX) { - return 0u; - } - - const uint8_t *data = (const uint8_t *)s->otp->data; - uint64_t digest = ldq_le_p(data + offset); - - if (OtOTPPartDescs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s)) { - unsigned waddr = offset >> 2u; - unsigned ewaddr = waddr >> 1u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otp_dj_apply_digest_ecc(s, part_ix, digest, ecc); - } - - return digest; -} - -static bool ot_otp_dj_is_part_digest_offset(unsigned part_ix, hwaddr addr) -{ - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); -} - -static bool ot_otp_dj_is_part_zer_offset(unsigned part_ix, hwaddr addr) -{ - uint16_t offset = OtOTPPartDescs[part_ix].zer_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_ZER_ADDR_MASK) == offset); -} - -static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) -{ - /* - * Offset is the register offset from the first read 32-bit digest register. - * All digests are 64-bits, which means each partition have two 32-bit - * registers to expose their digest. - * - * Not all partitions have digests, which means the offset argument is the - * relative partition offset for those partitions that features a digest, as - * there is no defined digest access registers defined for partitions that - * do not have a digest. - * - * Need to traverse the partition table to only account for those partitions - * with a digest to match the proper offset. - */ - - /* - * part_look_ix is the index of the partition in the contiguous array of - * digest registers, which would be equivalent as the index of the partition - * that would exist in a virtual partition-with-digest array. - */ - unsigned part_look_ix = (unsigned)(offset / NUM_DIGEST_WORDS); - /* whether to retrieve the top most 32-bit of the digest or not */ - bool hi = (bool)(offset & 0x1u); - - /* part_ix: the partition number in the global partition array */ - unsigned part_ix = 0; - /* traverse the partition array and count each partition with a digest */ - for (unsigned part_dig_ix = 0; part_ix < OTP_PART_COUNT; part_ix++) { - if (ot_otp_dj_has_digest(part_ix)) { - /* - * stop searching if we've found the part-with-digest defined from - * the offset argument. Otherwise, increment the part-with-digest - * index and continue. - */ - if (part_dig_ix == part_look_ix) { - break; - } - part_dig_ix++; - } - } - - /* - * If the part_ix as reached the latest partition, there is something wrong - * with the partition table or the register definitions, as it is assumed - * that LifeCycle partition is the last partition. - */ - static_assert(OTP_PART_LIFE_CYCLE == NUM_PART - 1, - "LC is expected to be the last partition"); - g_assert(part_ix < OTP_PART_COUNT); - - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - uint64_t digest = pctrl->digest; - - if (hi) { - digest >>= 32u; - } - - return (uint32_t)digest; -} - -static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) -{ - g_assert(part_ix < OTP_PART_COUNT); - - const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - if (pdesc->secret) { - /* secret partitions are only readable if digest is not yet set. */ - return pctrl->digest == 0u; - } - - if (!pdesc->read_lock_csr) { - if (!pdesc->read_lock) { - /* read lock is not supported for this partition */ - return true; - } - - /* hw read lock, not locked */ - return !pctrl->read_lock; - } - - unsigned roffset = 0; - unsigned pix; - for (pix = 0; pix < OTP_PART_COUNT; pix++) { - if (pix == part_ix) { - break; - } - if (pdesc->read_lock_csr) { - roffset++; - } - } - /* - * know for sure last partition is the life cycle one, which never - * support a read_lock_csr. Ideally this g_assert should be a - * static_assert, but C being C, constants are not defined as such - * at build time... - */ - g_assert(!OtOTPPartDescs[OTP_PART_LIFE_CYCLE].read_lock_csr); - - /* - * If the previous loop reached the last partition, something - * seriously wrong occurred. Use this feature as a sanity check - */ - g_assert(pix < OTP_PART_LIFE_CYCLE); - - /* - * now that the count of read_lock_csr is known, use it to access - * the register for the selected partition - */ - uint32_t reg = R_VENDOR_TEST_READ_LOCK + roffset; - - return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); -} - -static void -ot_otp_dj_dai_change_state_line(OtOTPDjState *s, OtOTPDAIState state, int line) -{ - trace_ot_otp_dai_change_state(s->ot_id, line, DAI_STATE_NAME(s->dai->state), - s->dai->state, DAI_STATE_NAME(state), state); - - s->dai->state = state; -} - -static void -ot_otp_dj_lci_change_state_line(OtOTPDjState *s, OtOTPLCIState state, int line) -{ - trace_ot_otp_lci_change_state(s->ot_id, line, LCI_STATE_NAME(s->lci->state), - s->lci->state, LCI_STATE_NAME(state), state); - - s->lci->state = state; -} - -static void ot_otp_dj_lc_broadcast_recv(void *opaque, int n, int level) -{ - OtOTPDjState *s = opaque; - OtOTPLcBroadcast *bcast = &s->lc_broadcast; - - g_assert((unsigned)n < OT_OTP_LC_BROADCAST_COUNT); - - uint16_t bit = 1u << (unsigned)n; - bcast->signal |= 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->level |= bit; - } else { - bcast->level &= ~bit; - } - - /* use a BH to decouple IRQ signaling from actual handling */ - qemu_bh_schedule(s->lc_broadcast.bh); -} - -static void ot_otp_dj_lc_broadcast_bh(void *opaque) -{ - OtOTPDjState *s = opaque; - OtOTPLcBroadcast *bcast = &s->lc_broadcast; - - /* handle all flagged signals */ - while (bcast->signal) { - /* pick and clear */ - unsigned sig = ctz16(bcast->signal); - uint16_t bit = 1u << (unsigned)sig; - bcast->signal &= ~bit; - bcast->current_level = - (bcast->current_level & ~bit) | (bcast->level & bit); - bool level = (bool)(bcast->current_level & bit); - - trace_ot_otp_lc_broadcast(s->ot_id, sig, level); - - switch ((int)sig) { - case OT_OTP_LC_DFT_EN: - qemu_log_mask(LOG_UNIMP, "%s: %s: DFT feature not supported\n", - __func__, s->ot_id); - break; - case OT_OTP_LC_ESCALATE_EN: - if (level) { - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - /* TODO: manage other FSMs */ - qemu_log_mask(LOG_UNIMP, - "%s: %s: ESCALATE partially implemented\n", - __func__, s->ot_id); - if (s->fatal_escalate) { - error_setg(&error_fatal, "%s: OTP LC escalate", s->ot_id); - } - } - break; - case OT_OTP_LC_CHECK_BYP_EN: - qemu_log_mask(LOG_UNIMP, "%s: %s: bypass is ignored\n", __func__, - s->ot_id); - break; - case OT_OTP_LC_CREATOR_SEED_SW_RW_EN: - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_creator) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; - } - } - break; - case OT_OTP_LC_OWNER_SEED_SW_RW_EN: - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; - } - } - break; - case OT_OTP_LC_SEED_HW_RD_EN: - /* nothing to do here, SEED_HW_RD_EN flag is in current_level */ - break; - default: - error_setg(&error_fatal, "%s: %s: unexpected LC broadcast %d\n", - __func__, s->ot_id, sig); - g_assert_not_reached(); - break; - } - } -} - -static uint64_t ot_otp_dj_compute_partition_digest( - OtOTPDjState *s, const uint8_t *base, unsigned size) -{ - OtPresentState *ps = ot_present_new(); - - g_assert((size & (sizeof(uint64_t) - 1u)) == 0); - - uint8_t buf[sizeof(uint64_t) * 2u]; - uint64_t state = s->digest_iv; - uint64_t out; - for (unsigned off = 0; off < size; off += sizeof(buf)) { - memcpy(buf, base + off, sizeof(uint64_t)); - if (off + sizeof(uint64_t) != size) { - memcpy(&buf[sizeof(uint64_t)], base + off + sizeof(uint64_t), - sizeof(uint64_t)); - } else { - /* special case, duplicate last block if block number is odd */ - memcpy(&buf[sizeof(uint64_t)], base + off, sizeof(uint64_t)); - } - - ot_present_init(ps, buf); - ot_present_encrypt(ps, state, &out); - state ^= out; - } - - ot_present_init(ps, s->digest_const); - ot_present_encrypt(ps, state, &out); - state ^= out; - - ot_present_free(ps); - - return state; -} - -static uint64_t -ot_otp_dj_load_partition_digest(OtOTPDjState *s, unsigned partition) -{ - unsigned digoff = (unsigned)OtOTPPartDescs[partition].digest_offset; - - if ((digoff + sizeof(uint64_t)) > s->otp->data_size) { - error_setg(&error_fatal, "%s: partition located outside storage?", - s->ot_id); - /* linter doest not know the above call never returns */ - return 0u; - } - - const uint8_t *data = (const uint8_t *)s->otp->data; - uint64_t digest = ldq_le_p(data + digoff); - - if (ot_otp_dj_is_ecc_enabled(s)) { - unsigned ewaddr = (digoff >> 3u); - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otp_dj_apply_digest_ecc(s, partition, digest, ecc); - } - - return digest; -} - -static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); - - /* part_size should be a multiple of PRESENT block size */ - g_assert((part_size & (sizeof(uint64_t) - 1u)) == 0u); - unsigned dword_count = part_size / sizeof(uint64_t); - - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; - - /* source address should be aligned to 64-bit boundary */ - g_assert(((uintptr_t)base & (sizeof(uint64_t) - 1u)) == 0u); - const uint64_t *scrambled = (const uint64_t *)base; - - /* destination address should be aligned to 64-bit boundary */ - g_assert(pctrl->buffer.data != NULL); - uint64_t *clear = (uint64_t *)pctrl->buffer.data; - - const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; - g_assert(scrambling_key); - - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - - trace_ot_otp_unscramble_partition(s->ot_id, PART_NAME(ix), ix, part_size); - /* neither the digest block nor the zeroizable block are scrambled */ - for (unsigned dix = 0u; dix < dword_count; dix++) { - ot_present_decrypt(ps, scrambled[dix], &clear[dix]); - } - - ot_present_free(ps); -} - -static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - g_assert(pctrl->buffer.data != NULL); - - if (OtOTPPartDescs[ix].hw_digest) { - pctrl->digest = ot_otp_dj_load_partition_digest(s, ix); - } else { - pctrl->digest = 0; - } - - if (OtOTPPartDescs[ix].secret) { - /* secret partitions need to be unscrambled */ - if (s->blk) { - /* - * nothing to unscramble if no OTP data is loaded - * scrambling keys in this case may not be known - */ - ot_otp_dj_unscramble_partition(s, ix); - } - } else { - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); - - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; - - memcpy(pctrl->buffer.data, base, part_size); - } -} - -static void -ot_otp_dj_check_buffered_partition_integrity(OtOTPDjState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - if (pctrl->digest == 0) { - trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); - pctrl->locked = false; - return; - } - - pctrl->locked = true; - - /* - * digests are always calculated over the original data (scrambled or not) - */ - const uint8_t *part_data = - ((const uint8_t *)s->otp->data) + ot_otp_dj_part_data_offset(ix); - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); - - uint64_t digest = - ot_otp_dj_compute_partition_digest(s, part_data, part_size); - - if (digest != pctrl->digest) { - trace_ot_otp_mismatch_digest(s->ot_id, PART_NAME(ix), ix, digest, - pctrl->digest); - - TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - PART_NAME(ix), digest, - ot_otp_hexdump(s, part_data, part_size)); - - pctrl->failed = true; - /* this is a fatal error */ - ot_otp_dj_set_error(s, ix, OTP_CHECK_FAIL_ERROR); - /* TODO: revert buffered part to default */ - } else { - trace_ot_otp_integrity_report(s->ot_id, PART_NAME(ix), ix, "digest OK"); - } -} - -static bool ot_otp_dj_is_backend_writable(OtOTPDjState *s) -{ - return (s->blk != NULL) && blk_is_writable(s->blk); -} - -static inline int ot_otp_dj_write_backend(OtOTPDjState *s, const void *buffer, - unsigned offset, size_t size) -{ - /* - * the blk_pwrite API is awful, isolate it so that linter exceptions are - * are not repeated over and over - */ - g_assert(offset + size <= s->otp->size); - - /* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */ - return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer, - /* a bitfield of enum is not an enum item */ - (BdrvRequestFlags)0); - /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ -} - -static void ot_otp_dj_dai_init(OtOTPDjState *s) -{ - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); -} - -static void ot_otp_dj_dai_set_error(OtOTPDjState *s, OtOTPError err) -{ - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, err); - - switch (err) { - case OTP_FSM_STATE_ERROR: - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - break; - default: - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - } -} - -static void ot_otp_dj_dai_clear_error(OtOTPDjState *s) -{ - s->regs[R_STATUS] &= ~R_STATUS_DAI_ERROR_MASK; - s->regs[R_ERR_CODE_0 + OTP_ENTRY_DAI] = 0; -} - -static void ot_otp_dj_dai_read(OtOTPDjState *s) -{ - if (ot_otp_dj_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - ot_otp_dj_dai_clear_error(s); - - DAI_CHANGE_STATE(s, OTP_DAI_READ); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_dj_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (partition >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - const OtOTPPartController *pctrl = &s->partctrls[partition]; - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); - return; - } - - unsigned part_ix = (unsigned)partition; - bool is_readable = ot_otp_dj_is_readable(s, part_ix); - bool is_wide = ot_otp_dj_is_wide_granule(part_ix, address); - bool is_buffered = ot_otp_dj_is_buffered(part_ix); - bool is_secret = ot_otp_dj_is_secret(part_ix); - bool is_digest = ot_otp_dj_is_part_digest_offset(part_ix, address); - bool is_zer = ot_otp_dj_is_part_zer_offset(part_ix, address); - - /* "in all partitions, the digest itself is ALWAYS readable." */ - if (!is_digest && !is_zer && !is_readable) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: partition %s @ 0x%04x not readable\n", __func__, - s->ot_id, PART_NAME(partition), address); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_offset = address - ot_otp_dj_part_data_offset(partition); - unsigned part_waddr = part_offset >> 2u; - bool do_ecc = - OtOTPPartDescs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s); - - DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); - - uint32_t data_lo, data_hi; - unsigned err = 0; - unsigned cell_count = sizeof(uint32_t) + (do_ecc ? sizeof(uint16_t) : 0); - - const uint32_t *data = (const uint32_t *)s->otp->data; - /* parenthesis inform the C linter sizeof() call is valid with 'data */ - data += (ot_otp_dj_part_data_offset(partition) / sizeof(uint32_t)); - - if (is_wide || is_digest) { - /* 64-bit requests */ - part_waddr &= ~0b1u; - - g_assert((part_waddr + 1u) * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); - - data_lo = data[part_waddr]; - data_hi = data[part_waddr + 1u]; - - if (do_ecc) { - unsigned ewaddr = address >> 3u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - if (ot_otp_dj_is_ecc_enabled(s)) { - data_lo = ot_otp_dj_verify_ecc(s, data_lo, ecc & 0xffffu, &err); - data_hi = ot_otp_dj_verify_ecc(s, data_hi, ecc >> 16u, &err); - } - } - - cell_count *= 2u; - } else { - /* 32-bit request */ - g_assert(part_waddr * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); - - data_lo = data[part_waddr]; - data_hi = 0u; - - if (do_ecc) { - unsigned ewaddr = address >> 3u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - if ((address >> 2u) & 1u) { - ecc >>= 16u; - } - if (ot_otp_dj_is_ecc_enabled(s)) { - data_lo = ot_otp_dj_verify_ecc(s, data_lo, ecc & 0xffffu, &err); - } - cell_count = 4u + 2u; - } else { - cell_count = 4u; - } - } - - if (is_secret && !(is_digest || is_zer)) { - /* - * if the partition is a secret partition, OTP storage is scrambled - * except the digest and the zeroification fields - */ - const uint8_t *scrambling_key = s->otp_scramble_keys[partition]; - g_assert(scrambling_key); - uint64_t tmp_data = ((uint64_t)data_hi << 32u) | data_lo; - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - ot_present_decrypt(ps, tmp_data, &tmp_data); - ot_present_free(ps); - data_lo = (uint32_t)tmp_data; - data_hi = (uint32_t)(tmp_data >> 32u); - } - - s->regs[R_DIRECT_ACCESS_RDATA_0] = data_lo; - s->regs[R_DIRECT_ACCESS_RDATA_1] = data_hi; - - if (err) { - OtOTPError otp_err = - (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : OTP_MACRO_ECC_CORR_ERROR; - ot_otp_dj_dai_set_error(s, otp_err); - return; - } - - s->dai->partition = partition; - - if (!is_buffered) { - /* fake slow access to OTP cell */ - unsigned access_time = s->be_chars.timings.read_ns * cell_count; - timer_mod(s->dai->delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + access_time); - } else { - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - } -} - -static int ot_otp_dj_dai_write_u64(OtOTPDjState *s, unsigned address) -{ - unsigned waddr = (address / sizeof(uint32_t)) & ~1u; - uint32_t *dst = &s->otp->data[waddr]; - - uint32_t dst_lo = dst[0u]; - uint32_t dst_hi = dst[1u]; - - uint32_t lo = s->regs[R_DIRECT_ACCESS_WDATA_0]; - uint32_t hi = s->regs[R_DIRECT_ACCESS_WDATA_1]; - - unsigned part_ix = (unsigned)s->dai->partition; - bool is_secret = ot_otp_dj_is_secret(part_ix); - bool is_zer = ot_otp_dj_is_part_zer_offset(part_ix, address); - - if (is_secret && !is_zer) { - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; - uint64_t data = ((uint64_t)hi << 32u) | lo; - g_assert(scrambling_key); - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - ot_present_encrypt(ps, data, &data); - lo = (uint32_t)data; - hi = (uint32_t)(data >> 32u); - } - - if ((dst_lo & ~lo) || (dst_hi & ~hi)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Cannot clear OTP bits\n", - __func__, s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - - dst[0u] |= lo; - dst[1u] |= hi; - - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, dst, - (unsigned)(offset + waddr * sizeof(uint32_t)), - sizeof(uint64_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - if (ot_otp_dj_is_ecc_enabled(s)) { - unsigned ewaddr = waddr >> 1u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t *edst = &s->otp->ecc[ewaddr]; - - uint32_t ecc_lo = (uint32_t)ot_otp_dj_compute_ecc_u32(lo); - uint32_t ecc_hi = (uint32_t)ot_otp_dj_compute_ecc_u32(hi); - uint32_t ecc = (ecc_hi << 16u) | ecc_lo; - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Cannot clear OTP ECC bits\n", __func__, - s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, edst, (unsigned)(offset + (waddr << 1u)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - trace_ot_otp_dai_new_dword_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - } - - return 0; -} - -static int ot_otp_dj_dai_write_u32(OtOTPDjState *s, unsigned address) -{ - unsigned waddr = address / sizeof(uint32_t); - uint32_t *dst = &s->otp->data[waddr]; - uint32_t data = s->regs[R_DIRECT_ACCESS_WDATA_0]; - - if (*dst & ~data) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP bits\n", - __func__, s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - - *dst |= data; - - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, dst, - (unsigned)(offset + waddr * sizeof(uint32_t)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - if (ot_otp_dj_is_ecc_enabled(s)) { - g_assert((waddr >> 1u) < s->otp->ecc_size); - uint16_t *edst = &((uint16_t *)s->otp->ecc)[waddr]; - uint16_t ecc = ot_otp_dj_compute_ecc_u32(*dst); - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP ECC bits\n", __func__, - s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, edst, - (unsigned)(offset + (address >> 1u)), - sizeof(uint16_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - trace_ot_otp_dai_new_word_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - } - - return 0; -} - -static void ot_otp_dj_dai_write(OtOTPDjState *s) -{ - if (ot_otp_dj_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - if (!ot_otp_dj_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); - - ot_otp_dj_dai_clear_error(s); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_dj_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_ix = (unsigned)partition; - - if (part_ix >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: Life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(part_ix)); - return; - } - - if (pctrl->locked) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (pctrl->write_lock) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: artition %s (%u) is write locked\n", __func__, - s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - bool is_digest = ot_otp_dj_is_part_digest_offset(part_ix, address); - bool is_wide = ot_otp_dj_is_wide_granule(part_ix, address); - - if (is_digest) { - if (OtOTPPartDescs[partition].hw_digest) { - /* should have been a Digest command, not a Write command */ - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: partition %s (%u) HW digest cannot be directly " - "written\n", - __func__, s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - } - - s->dai->partition = partition; - - bool do_ecc = ot_otp_dj_is_ecc_enabled(s); - unsigned cell_count = sizeof(uint32_t); - - if (is_wide || is_digest) { - if (ot_otp_dj_dai_write_u64(s, address)) { - return; - } - cell_count *= 2u; - } else { - if (ot_otp_dj_dai_write_u32(s, address)) { - return; - } - } - - if (do_ecc) { - cell_count += cell_count / 2u; - }; - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); - - /* fake slow update of OTP cell */ - unsigned update_time = s->be_chars.timings.write_ns * cell_count; - timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); -} - -static void ot_otp_dj_dai_digest(OtOTPDjState *s) -{ - if (ot_otp_dj_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - if (!ot_otp_dj_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_CLR); - - ot_otp_dj_dai_clear_error(s); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_dj_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (partition >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: Life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (!OtOTPPartDescs[partition].hw_digest) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Invalid partition, no HW digest on %s (#%u)\n", - __func__, s->ot_id, PART_NAME(partition), partition); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - OtOTPPartController *pctrl = &s->partctrls[partition]; - - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); - return; - } - - if (pctrl->locked) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(partition), partition); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (pctrl->write_lock) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Partition %s (%u) is write locked\n", __func__, - s->ot_id, PART_NAME(partition), partition); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); - - const uint8_t *data = - ((const uint8_t *)s->otp->data) + ot_otp_dj_part_data_offset(partition); - unsigned part_size = ot_otp_dj_part_data_byte_size(partition); - - DAI_CHANGE_STATE(s, OTP_DAI_DIG); - - pctrl->buffer.next_digest = - ot_otp_dj_compute_partition_digest(s, data, part_size); - s->dai->partition = partition; - - TRACE_OTP("%s: %s: next digest %016" PRIx64 " from %s\n", __func__, - s->ot_id, pctrl->buffer.next_digest, - ot_otp_hexdump(s, data, part_size)); - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_WAIT); - - /* fake slow update of OTP cell */ - timer_mod(s->dai->delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_DIGEST_DELAY_NS); -} - -static void ot_otp_dj_dai_write_digest(void *opaque) -{ - OtOTPDjState *s = OT_OTP_DJ(opaque); - - g_assert((s->dai->partition >= 0) && (s->dai->partition < OTP_PART_COUNT)); - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); - - OtOTPPartController *pctrl = &s->partctrls[s->dai->partition]; - unsigned address = OtOTPPartDescs[s->dai->partition].digest_offset; - unsigned dwaddr = address / sizeof(uint64_t); - uint64_t *dst = &((uint64_t *)s->otp->data)[dwaddr]; - uint64_t data = pctrl->buffer.next_digest; - pctrl->buffer.next_digest = 0; - - if (*dst & ~data) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP data bits\n", - __func__, s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *dst |= data; - - uintptr_t offset; - offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, dst, (unsigned)(offset + address), - sizeof(uint64_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - uint32_t ecc = ot_otp_dj_compute_ecc_u64(data); - - /* dwaddr is 64-bit based, convert it to 32-bit base for ECC */ - unsigned ewaddr = (dwaddr << 1u) / s->otp->ecc_granule; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t *edst = &s->otp->ecc[ewaddr]; - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP ECC bits\n", - __func__, s->ot_id); - ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, edst, (unsigned)(offset + (ewaddr << 2u)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - trace_ot_otp_dai_new_digest_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); - - /* fake slow update of OTP cell */ - unsigned cell_count = sizeof(uint64_t) + sizeof(uint32_t); - unsigned update_time = s->be_chars.timings.write_ns * cell_count; - timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); -} - -static void ot_otp_dj_dai_complete(void *opaque) -{ - OtOTPDjState *s = opaque; - - switch (s->dai->state) { - case OTP_DAI_READ_WAIT: - g_assert(s->dai->partition >= 0); - trace_ot_otp_dai_read(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, - s->regs[R_DIRECT_ACCESS_RDATA_1], - s->regs[R_DIRECT_ACCESS_RDATA_1]); - s->dai->partition = -1; - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - case OTP_DAI_WRITE_WAIT: - g_assert(s->dai->partition >= 0); - s->regs[R_INTR_STATE] |= INTR_OTP_OPERATION_DONE_MASK; - s->dai->partition = -1; - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - case OTP_DAI_DIG_WAIT: - g_assert(s->dai->partition >= 0); - qemu_bh_schedule(s->dai->digest_bh); - break; - case OTP_DAI_ERROR: - break; - default: - g_assert_not_reached(); - break; - }; -} - -static void ot_otp_dj_lci_init(OtOTPDjState *s) -{ - LCI_CHANGE_STATE(s, OTP_LCI_IDLE); -} - -static uint64_t ot_otp_dj_reg_read(void *opaque, hwaddr addr, unsigned size) -{ - OtOTPDjState *s = OT_OTP_DJ(opaque); - (void)size; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - switch (reg) { - case R_INTR_STATE: - case R_INTR_ENABLE: - case R_ERR_CODE_0 ... R_ERR_CODE_23: - case R_DIRECT_ACCESS_WDATA_0: - case R_DIRECT_ACCESS_WDATA_1: - case R_DIRECT_ACCESS_RDATA_0: - case R_DIRECT_ACCESS_RDATA_1: - case R_DIRECT_ACCESS_ADDRESS: - case R_VENDOR_TEST_READ_LOCK ... R_ROM_PATCH_READ_LOCK: - case R_CHECK_TRIGGER_REGWEN: - case R_CHECK_REGWEN: - val32 = s->regs[reg]; - break; - case R_STATUS: - val32 = ot_otp_dj_get_status(s); - break; - case R_DIRECT_ACCESS_REGWEN: - /* disabled either if SW locked, or if DAI is busy. */ - val32 = s->regs[reg]; - val32 &= FIELD_DP32(0u, DIRECT_ACCESS_REGWEN, REGWEN, - (uint32_t)!ot_otp_dj_dai_is_busy(s)); - break; - /* NOLINTNEXTLINE */ - case R_DIRECT_ACCESS_CMD: - case R_CHECK_TRIGGER: - val32 = 0; /* R0W1C */ - break; - case R_CHECK_TIMEOUT: - case R_INTEGRITY_CHECK_PERIOD: - case R_CONSISTENCY_CHECK_PERIOD: - /* @todo: not yet implemented, but these are R/W registers */ - qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, - s->ot_id, REG_NAME(reg)); - val32 = s->regs[reg]; - break; - case R_VENDOR_TEST_DIGEST_0 ... R_SECRET3_DIGEST_1: - /* - * In all partitions with a digest, the digest itself is ALWAYS - * readable. - */ - val32 = ot_otp_dj_get_part_digest_reg(s, reg - R_VENDOR_TEST_DIGEST_0); - break; - case R_INTR_TEST: - case R_ALERT_TEST: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: W/O register 0x02%" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(reg)); - val32 = 0; - break; - default: - 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_otp_io_reg_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, - pc); - - return (uint64_t)val32; -} - -static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - OtOTPDjState *s = OT_OTP_DJ(opaque); - (void)size; - uint32_t val32 = (uint32_t)value; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - - trace_ot_otp_io_reg_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, - pc); - - switch (reg) { - case R_DIRECT_ACCESS_CMD: - case R_DIRECT_ACCESS_ADDRESS: - case R_DIRECT_ACCESS_WDATA_0: - case R_DIRECT_ACCESS_WDATA_1: - case R_VENDOR_TEST_READ_LOCK ... R_ROM_PATCH_READ_LOCK: - if (!(s->regs[R_DIRECT_ACCESS_REGWEN] & - R_DIRECT_ACCESS_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_DIRECT_ACCESS_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_CHECK_TRIGGER: - if (!(s->regs[R_CHECK_TRIGGER_REGWEN] & - R_CHECK_TRIGGER_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_CHECK_TRIGGER_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_CHECK_TIMEOUT: - case R_INTEGRITY_CHECK_PERIOD: - case R_CONSISTENCY_CHECK_PERIOD: - if (!(s->regs[R_CHECK_REGWEN] & R_CHECK_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_CHECK_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_STATUS: - case R_ERR_CODE_0 ... R_ERR_CODE_23: - case R_DIRECT_ACCESS_RDATA_0: - case R_DIRECT_ACCESS_RDATA_1: - case R_VENDOR_TEST_DIGEST_0 ... R_SECRET3_DIGEST_1: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(reg)); - return; - default: - break; + break; + case R_STATUS: + case R_ERR_CODE_0 ... R_ERR_CODE_23: + case R_DIRECT_ACCESS_RDATA_0: + case R_DIRECT_ACCESS_RDATA_1: + case R_VENDOR_TEST_DIGEST_0 ... R_SECRET3_DIGEST_1: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + return; + default: + break; } switch (reg) { case R_INTR_STATE: val32 &= INTR_WMASK; s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ - ot_otp_dj_update_irqs(s); + c->update_irqs(s); break; case R_INTR_ENABLE: val32 &= INTR_WMASK; s->regs[R_INTR_ENABLE] = val32; - ot_otp_dj_update_irqs(s); + c->update_irqs(s); break; case R_INTR_TEST: val32 &= INTR_WMASK; s->regs[R_INTR_STATE] = val32; - ot_otp_dj_update_irqs(s); + c->update_irqs(s); break; case R_ALERT_TEST: val32 &= ALERT_WMASK; s->regs[reg] = val32; - ot_otp_dj_update_alerts(s); + c->update_alerts(s); break; case R_DIRECT_ACCESS_REGWEN: val32 &= R_DIRECT_ACCESS_REGWEN_REGWEN_MASK; s->regs[reg] &= val32; /* RW0C */ break; case R_DIRECT_ACCESS_CMD: - if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { - ot_otp_dj_dai_read(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { - ot_otp_dj_dai_write(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { - ot_otp_dj_dai_digest(s); + if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, RD)) { + c->dai_read(s); + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, WR)) { + c->dai_write(s); + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, DIGEST)) { + c->dai_digest(s); } + /* @todo implement ZEROIZE command */ break; case R_DIRECT_ACCESS_ADDRESS: val32 &= R_DIRECT_ACCESS_ADDRESS_ADDRESS_MASK; @@ -2662,7 +781,7 @@ static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, s->regs[reg] = val32; break; case R_VENDOR_TEST_READ_LOCK ... R_ROM_PATCH_READ_LOCK: - val32 &= READ_LOCK_MASK; + val32 &= OT_OTP_READ_LOCK_MASK; s->regs[reg] &= val32; /* RW0C */ break; case R_CHECK_TRIGGER_REGWEN: @@ -2838,13 +957,13 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) static MemTxResult ot_otp_dj_swcfg_read_with_attrs( void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - OtOTPDjState *s = OT_OTP_DJ(opaque); + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; (void)attrs; - g_assert(addr + size <= SW_CFG_WINDOW_SIZE); - hwaddr reg = R32_OFF(addr); - int partition = ot_otp_dj_get_part_from_address(s, addr); + int partition = c->get_part_from_address(s, addr); if (partition < 0) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "invalid"); @@ -2855,701 +974,179 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( unsigned part_ix = (unsigned)partition; - if (ot_otp_dj_is_buffered(part_ix)) { + if (ot_otp_engine_is_buffered(s, part_ix)) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "buffered"); - ot_otp_dj_set_error(s, part_ix, OTP_ACCESS_ERROR); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); /* real HW seems to stall the Tile Link bus in this case */ return MEMTX_ACCESS_ERROR; } - bool is_readable = ot_otp_dj_is_readable(s, part_ix); - bool is_digest = ot_otp_dj_is_part_digest_offset(part_ix, addr); - bool is_zer = ot_otp_dj_is_part_zer_offset(part_ix, addr); + bool is_readable = c->is_readable(s, part_ix); + bool is_digest = ot_otp_engine_is_part_digest_offset(s, part_ix, addr); + bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, addr); if (!is_readable && !(is_digest || is_zer)) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); - ot_otp_dj_set_error(s, part_ix, OTP_ACCESS_ERROR); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); return MEMTX_DECODE_ERROR; } uint32_t val32 = s->otp->data[reg]; - ot_otp_dj_set_error(s, part_ix, OTP_NO_ERROR); - - uint64_t pc; - - pc = ibex_get_current_pc(); - trace_ot_otp_io_swcfg_read_out(s->ot_id, (uint32_t)addr, - ot_otp_dj_swcfg_reg_name(addr), val32, pc); - - *data = (uint64_t)val32; - - return MEMTX_OK; -} - -static void ot_otp_dj_get_lc_info( - const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, - uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) -{ - const OtOTPDjState *ds = OT_OTP_DJ(s); - const OtOTPStorage *otp = ds->otp; - - if (lc_tcount) { - memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], - LC_TRANSITION_CNT_SIZE); - } - - if (lc_state) { - memcpy(lc_state, &otp->data[R_LC_STATE], LC_STATE_SIZE); - } - - if (lc_valid) { - *lc_valid = !(ds->partctrls[OTP_PART_SECRET0].failed || - ds->partctrls[OTP_PART_SECRET2].failed || - ds->partctrls[OTP_PART_LIFE_CYCLE].failed) ? - OT_MULTIBITBOOL_LC4_TRUE : - OT_MULTIBITBOOL_LC4_FALSE; - } - if (secret_valid) { - *secret_valid = (!ds->partctrls[OTP_PART_SECRET2].failed && - ds->partctrls[OTP_PART_SECRET2].locked) ? - OT_MULTIBITBOOL_LC4_TRUE : - OT_MULTIBITBOOL_LC4_FALSE; - } - if (tokens) { - *tokens = ds->tokens; - } -} - -static const OtOTPHWCfg *ot_otp_dj_get_hw_cfg(const OtOTPState *s) -{ - const OtOTPDjState *ds = OT_OTP_DJ(s); - - return (const OtOTPHWCfg *)ds->hw_cfg; -} - -static void ot_otp_dj_request_entropy_bh(void *opaque) -{ - OtOTPDjState *s = opaque; - - /* - * Use a BH as entropy should be filled in as soon as possible after reset. - * However, as the EDN / OTP reset order is unknown, this initial request - * can only be performed once the reset sequence is over. - */ - if (!s->keygen->edn_sched) { - int rc = ot_edn_request_entropy(s->edn, s->edn_ep); - g_assert(rc == 0); - s->keygen->edn_sched = true; - } -} - -static void -ot_otp_dj_keygen_push_entropy(void *opaque, uint32_t bits, bool fips) -{ - OtOTPDjState *s = opaque; - (void)fips; - - s->keygen->edn_sched = false; - - if (!ot_fifo32_is_full(&s->keygen->entropy_buf)) { - ot_fifo32_push(&s->keygen->entropy_buf, bits); - } - - bool resched = !ot_fifo32_is_full(&s->keygen->entropy_buf); - - trace_ot_otp_keygen_entropy(s->ot_id, - ot_fifo32_num_used(&s->keygen->entropy_buf), - resched); - - if (resched && !s->keygen->edn_sched) { - qemu_bh_schedule(s->keygen->entropy_bh); - } -} - -static void ot_otp_dj_fake_entropy(OtOTPDjState *s, unsigned count) -{ - /* - * This part departs from real HW: OTP needs to have bufferized enough - * entropy for any SRAM OTP key request to be successfully completed. - * On real HW, entropy is requested on demand, but in QEMU this very API - * (#get_otp_key) needs to be synchronous, as it should be able to complete - * on SRAM controller I/O request, which is itself fully synchronous. - * When not enough entropy has been initiatially collected, this function - * adds some fake entropy to entropy buffer. The main use case is to enable - * SRAM initialization with random values and does not need to be truly - * secure, while limiting emulation code size and complexity. - */ - - OtOTPKeyGen *kgen = s->keygen; - while (count-- && !ot_fifo32_is_full(&kgen->entropy_buf)) { - ot_fifo32_push(&kgen->entropy_buf, ot_prng_random_u32(kgen->prng)); - } -} - -/* - * See - * https://opentitan.org/book/hw/top_darjeeling/ip_autogen/otp_ctrl/doc/ - * theory_of_operation.html#scrambling-datapath - * - * The `fetch_nonce_entropy` field refers to the fetching of additional - * entropy for the nonce output. - * - * The `ingest_entropy` field indicates whether an additional 128 bit entropy - * block should be ingested after the seed. That is, `true` will - * derive an ephemeral scrambling key (path C) and `false` will derive a static - * scrambling key (path D). - * - * Will fake entropy if there is not enough available, rather than waiting. - */ -static void ot_otp_dj_generate_scrambling_key( - OtOTPDjState *s, OtOTPKey *key, OtOTPKeyType type, hwaddr key_reg, - uint64_t k_iv, const uint8_t *k_const, bool fetch_nonce_entropy, - bool ingest_entropy) -{ - g_assert(key->seed_size < OT_OTP_SEED_MAX_SIZE); - g_assert(key->nonce_size < OT_OTP_NONCE_MAX_SIZE); - - g_assert(key->seed_size % sizeof(uint32_t) == 0u); - g_assert(key->nonce_size % sizeof(uint32_t) == 0u); - unsigned seed_words = key->seed_size / sizeof(uint32_t); - unsigned nonce_words = key->nonce_size / sizeof(uint32_t); - unsigned scramble_blocks = key->seed_size / sizeof(uint64_t); - - OtFifo32 *entropy = &s->keygen->entropy_buf; - - /* for QEMU emulation, fake entropy instead of waiting */ - unsigned avail_entropy = ot_fifo32_num_used(entropy); - unsigned needed_entropy = 0u; - needed_entropy += fetch_nonce_entropy ? nonce_words : 0u; - needed_entropy += ingest_entropy ? (seed_words * scramble_blocks) : 0u; - if (avail_entropy < needed_entropy) { - unsigned count = needed_entropy - avail_entropy; - error_report("%s: %s: not enough entropy for key %d, fake %u words", - __func__, s->ot_id, type, count); - ot_otp_dj_fake_entropy(s, count); - } - - if (fetch_nonce_entropy) { - /* fill in the nonce using entropy */ - g_assert(ot_fifo32_num_used(entropy) >= nonce_words); - for (unsigned ix = 0; ix < nonce_words; ix++) { - stl_le_p(&key->nonce[ix * sizeof(uint32_t)], - ot_fifo32_pop(entropy)); - } - } - - OtPresentState *ps = s->keygen->present; - - /* read the key seed from the OTP SECRET1 partition */ - OtOTPPartController *pctrl = &s->partctrls[OTP_PART_SECRET1]; - g_assert(ot_otp_dj_is_buffered(OTP_PART_SECRET1)); - uint32_t poffset = - OtOTPPartDescs[OTP_PART_SECRET1].offset / sizeof(uint32_t); - const uint32_t *key_seed = &pctrl->buffer.data[key_reg - poffset]; - - /* check the key seed's validity */ - key->seed_valid = pctrl->locked && !pctrl->failed; - - uint32_t *ephemeral_entropy = g_new0(uint32_t, seed_words); - for (unsigned rix = 0; rix < scramble_blocks; rix++) { - /* compress the IV state with the OTP key seed */ - uint64_t data = k_iv; - ot_present_init(ps, (const uint8_t *)key_seed); - ot_present_encrypt(ps, data, &data); - - if (ingest_entropy) { - /* ephemeral keys ingest different entropy each round */ - g_assert(ot_fifo32_num_used(entropy) >= seed_words); - for (unsigned ix = 0; ix < seed_words; ix++) { - ephemeral_entropy[ix] = ot_fifo32_pop(entropy); - } - - ot_present_init(ps, (uint8_t *)&ephemeral_entropy[0]); - ot_present_encrypt(ps, data, &data); - } - - /* compress with the finalization constant*/ - ot_present_init(ps, k_const); - ot_present_encrypt(ps, data, &data); - - /* write back to the key */ - for (unsigned ix = 0; ix < sizeof(uint64_t); ix++) { - unsigned seed_byte = rix * sizeof(uint64_t) + ix; - key->seed[seed_byte] = (uint8_t)(data >> (ix * 8u)); - } - } - g_free(ephemeral_entropy); - - trace_ot_otp_key_generated(s->ot_id, type); - - if (needed_entropy) { - /* some entropy bits have been used, refill the buffer */ - qemu_bh_schedule(s->keygen->entropy_bh); - } -} - -static void ot_otp_dj_get_otp_key(OtOTPState *s, OtOTPKeyType type, - OtOTPKey *key) -{ - OtOTPDjState *ds = OT_OTP_DJ(s); - - hwaddr key_offset; - - trace_ot_otp_get_otp_key(ds->ot_id, type); - - /* reference: req_bundles in OpenTitan rtl/otp_ctrl_kdi.sv */ - switch (type) { - case OTP_KEY_FLASH_DATA: - case OTP_KEY_FLASH_ADDR: - /* there is no flash key on Darjeeling */ - qemu_log_mask(LOG_UNIMP, "%s: %s: flash key is not supported\n", - __func__, ds->ot_id); - break; - case OTP_KEY_OTBN: - memcpy(key->seed, ds->scrmbl_key_init->key, OTBN_KEY_BYTES); - memcpy(key->nonce, ds->scrmbl_key_init->nonce, OTBN_NONCE_BYTES); - key->seed_size = OTBN_KEY_BYTES; - key->nonce_size = OTBN_NONCE_BYTES; - key->seed_valid = false; - /* The OTBN scrambling key is derived from the SRAM scrambling key */ - key_offset = R_SECRET1_SRAM_DATA_KEY_SEED; - ot_otp_dj_generate_scrambling_key(ds, key, type, key_offset, - ds->sram_iv, ds->sram_const, true, - true); - break; - case OTP_KEY_SRAM: - memcpy(key->seed, ds->scrmbl_key_init->key, SRAM_KEY_BYTES); - memcpy(key->nonce, ds->scrmbl_key_init->nonce, SRAM_NONCE_BYTES); - key->seed_size = SRAM_KEY_BYTES; - key->nonce_size = SRAM_NONCE_BYTES; - key->seed_valid = false; - key_offset = R_SECRET1_SRAM_DATA_KEY_SEED; - ot_otp_dj_generate_scrambling_key(ds, key, type, key_offset, - ds->sram_iv, ds->sram_const, true, - true); - break; - default: - error_report("%s: %s: invalid OTP key type: %d", __func__, ds->ot_id, - type); - break; - } -} - -static void ot_otp_dj_get_keymgr_secret( - OtOTPState *s, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) -{ - OtOTPDjState *ds = OT_OTP_DJ(s); - int partition; - size_t offset; - - switch (type) { - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - - OtOTPPartDescs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - - OtOTPPartDescs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_SEED: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_SEED - OtOTPPartDescs[partition].offset; - break; - case OTP_KEYMGR_SECRET_OWNER_SEED: - partition = OTP_PART_SECRET3; - offset = A_SECRET3_OWNER_SEED - OtOTPPartDescs[partition].offset; - break; - default: - error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, - ds->ot_id, type); - secret->valid = false; - memset(secret->secret, 0, OT_OTP_KEYMGR_SECRET_SIZE); - return; - } - - unsigned part_ix = (unsigned)partition; - g_assert(ot_otp_dj_is_buffered(part_ix)); - - const uint8_t *data_ptr; - if (ds->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { - data_ptr = (const uint8_t *)ds->partctrls[part_ix].buffer.data; - } else { - /* source data from PartInvDefault instead of real buffer */ - data_ptr = ds->inv_default_parts[part_ix]; - } - - secret->valid = ds->partctrls[part_ix].digest != 0; - memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); -} - -static bool ot_otp_dj_program_req(OtOTPState *s, const uint16_t *lc_tcount, - const uint16_t *lc_state, - ot_otp_program_ack_fn ack, void *opaque) -{ - OtOTPDjState *ds = OT_OTP_DJ(s); - OtOTPLCIController *lci = ds->lci; - - switch (lci->state) { - case OTP_LCI_IDLE: - case OTP_LCI_ERROR: - /* error case is handled asynchronously */ - g_assert(!(lci->ack_fn || lci->ack_data)); - break; - case OTP_LCI_WRITE: - case OTP_LCI_WRITE_WAIT: - /* another LC programming request is on-going */ - return false; - case OTP_LCI_RESET: - /* cannot reach this point if PwrMgr init has been executed */ - default: - g_assert_not_reached(); - break; - } - - lci->ack_fn = ack; - lci->ack_data = opaque; - - if (lci->state == OTP_LCI_IDLE) { - unsigned hpos = 0; - memcpy(&lci->data[hpos], lc_tcount, LC_TRANSITION_CNT_SIZE); - hpos += LC_TRANSITION_CNT_SIZE / sizeof(uint16_t); - memcpy(&lci->data[hpos], lc_state, LC_STATE_SIZE); - hpos += LC_STATE_SIZE / sizeof(uint16_t); - g_assert(hpos == - OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size / sizeof(uint16_t)); - - /* current position in LC buffer to write to backend */ - lci->hpos = 0u; - } - - /* - * schedule even if LCI FSM is already in error to report the issue - * asynchronously - */ - timer_mod(lci->prog_delay, - qemu_clock_get_ns(OT_OTP_HW_CLOCK) + LCI_PROG_SCHED_NS); - - return true; -} - -static void ot_otp_dj_lci_write_complete(OtOTPDjState *s, bool success) -{ - OtOTPLCIController *lci = s->lci; - - if (lci->hpos) { - /* - * if the LC partition has been modified somehow, even if the request - * has failed, update the backend file - */ - const OtOTPPartDesc *lcdesc = &OtOTPPartDescs[OTP_PART_LIFE_CYCLE]; - unsigned lc_off = lcdesc->offset / sizeof(uint32_t); - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, &s->otp->data[lc_off], - (unsigned)(offset + lcdesc->offset), - lcdesc->size)) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - } - } - if (ot_otp_dj_is_ecc_enabled(s)) { - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, &((uint16_t *)s->otp->ecc)[lc_off], - (unsigned)(offset + - (lcdesc->offset >> 1u)), - lcdesc->size >> 1u)) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - } - } - } - } - - g_assert(lci->ack_fn); - ot_otp_program_ack_fn ack_fn = lci->ack_fn; - void *ack_data = lci->ack_data; - lci->ack_fn = NULL; - lci->ack_data = NULL; - lci->hpos = 0u; - - if (!success && lci->error != OTP_NO_ERROR) { - ot_otp_dj_set_error(s, OTP_PART_LIFE_CYCLE, lci->error); - } - - (*ack_fn)(ack_data, success); -} - -static void ot_otp_dj_lci_write_word(void *opaque) -{ - OtOTPDjState *s = OT_OTP_DJ(opaque); - OtOTPLCIController *lci = s->lci; - const OtOTPPartDesc *lcdesc = &OtOTPPartDescs[OTP_PART_LIFE_CYCLE]; - - /* should not be called if already in error */ - if (lci->state == OTP_LCI_ERROR) { - lci->error = OTP_FSM_STATE_ERROR; - ot_otp_dj_lci_write_complete(s, false); - return; - } - - if (!ot_otp_dj_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - ot_otp_dj_lci_write_complete(s, false); - /* abort immediately */ - return; - } - - if (lci->hpos >= lcdesc->size / sizeof(uint16_t)) { - /* the whole LC partition has been updated */ - if (lci->error == OTP_NO_ERROR) { - LCI_CHANGE_STATE(s, OTP_LCI_IDLE); - ot_otp_dj_lci_write_complete(s, true); - } else { - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - ot_otp_dj_lci_write_complete(s, false); - } - return; - } - - LCI_CHANGE_STATE(s, OTP_LCI_WRITE); - - uint16_t *lc_dst = - (uint16_t *)&s->otp->data[lcdesc->offset / sizeof(uint32_t)]; - - uint16_t cur_val = lc_dst[lci->hpos]; - uint16_t new_val = lci->data[lci->hpos]; - - trace_ot_otp_lci_write(s->ot_id, lci->hpos, cur_val, new_val); - - if (cur_val & ~new_val) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP bits @ %u: 0x%04x / 0x%04x\n", - __func__, s->ot_id, lci->hpos, cur_val, new_val); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_WRITE_BLANK_ERROR; - } - /* - * "Note that if errors occur, we aggregate the error code but still - * attempt to program all remaining words. This is done to ensure that - * a life cycle state with ECC correctable errors in some words can - * still be scrapped." - */ - } - - lc_dst[lci->hpos] |= new_val; - - if (ot_otp_dj_is_ecc_enabled(s)) { - uint8_t *lc_edst = - (uint8_t *)&s->otp->ecc[lcdesc->offset / (2u * sizeof(uint32_t))]; - uint8_t cur_ecc = lc_edst[lci->hpos]; - uint8_t new_ecc = ot_otp_dj_compute_ecc_u16(lc_dst[lci->hpos]); - - trace_ot_otp_lci_write_ecc(s->ot_id, lci->hpos, cur_ecc, new_ecc); - - if (cur_ecc & ~new_ecc) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP ECC @ %u: 0x%02x / 0x%02x\n", - __func__, s->ot_id, lci->hpos, cur_ecc, new_ecc); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_WRITE_BLANK_ERROR; - } - } - - lc_edst[lci->hpos] |= new_ecc; - } - - lci->hpos += 1u; - - unsigned update_time = s->be_chars.timings.write_ns * sizeof(uint16_t); - timer_mod(lci->prog_delay, - qemu_clock_get_ns(OT_OTP_HW_CLOCK) + update_time); - - LCI_CHANGE_STATE(s, OTP_LCI_WRITE_WAIT); -} - -static void ot_otp_dj_pwr_otp_req(void *opaque, int n, int level) -{ - OtOTPDjState *s = opaque; - - g_assert(n == 0); - - if (level) { - trace_ot_otp_pwr_otp_req(s->ot_id, "signaled"); - qemu_bh_schedule(s->pwr_otp_bh); - } -} - -static void ot_otp_dj_pwr_load(OtOTPDjState *s) -{ - /* - * HEADER_FORMAT - * - * | magic | 4 char | "vOFTP" | - * | hlength | uint32_t | count of header bytes after this point | - * | version | uint32_t | version of the header (v2) | - * | eccbits | uint16_t | ECC size in bits | - * | eccgran | uint16_t | ECC granule | - * | dlength | uint32_t | count of data bytes (% uint64_t) | - * | elength | uint32_t | count of ecc bytes (% uint64_t) | - * | -------- | ---------- | only in V2 | - * | dig_iv | 8 uint8_t | Present digest initialization vector | - * | dig_iv | 16 uint8_t | Present digest initialization vector | - */ - - struct otp_header { - char magic[4]; - uint32_t hlength; - uint32_t version; - uint16_t eccbits; - uint16_t eccgran; - uint32_t data_len; - uint32_t ecc_len; - /* added in V2 */ - uint8_t digest_iv[8u]; - uint8_t digest_constant[16u]; - }; - - static_assert(sizeof(struct otp_header) == 48u, "Invalid header size"); + c->set_error(s, part_ix, OT_OTP_NO_ERROR); - /* data following header should always be 64-bit aligned */ - static_assert((sizeof(struct otp_header) % sizeof(uint64_t)) == 0, - "invalid header definition"); + uint64_t pc; - size_t header_size = sizeof(struct otp_header); - size_t data_size = 0u; - size_t ecc_size = 0u; + pc = ibex_get_current_pc(); + trace_ot_otp_io_swcfg_read_out(s->ot_id, (uint32_t)addr, + ot_otp_dj_swcfg_reg_name(addr), val32, pc); - for (unsigned ix = 0u; ix < OTP_PART_COUNT; ix++) { - size_t psize = (size_t)OtOTPPartDescs[ix].size; - size_t dsize = ROUND_UP(psize, sizeof(uint64_t)); - data_size += dsize; - /* up to 1 ECC byte for 2 data bytes */ - ecc_size += DIV_ROUND_UP(dsize, 2u); - } - size_t otp_size = header_size + data_size + ecc_size; + *data = (uint64_t)val32; - otp_size = ROUND_UP(otp_size, 4096u); + return MEMTX_OK; +} - OtOTPStorage *otp = s->otp; +static void ot_otp_dj_get_lc_info( + const OtOTPIf *dev, uint16_t *lc_tcount, uint16_t *lc_state, + uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) +{ + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); + const OtOTPStorage *otp = s->otp; - /* always allocates the requested size even if blk is NULL */ - if (!otp->storage) { - /* only allocated once on PoR */ - otp->storage = blk_blockalign(s->blk, otp_size); + if (lc_tcount) { + memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], + LC_TRANSITION_CNT_SIZE); } - uintptr_t base = (uintptr_t)otp->storage; - g_assert(!(base & (sizeof(uint64_t) - 1u))); - - memset(otp->storage, 0, otp_size); - - otp->data = (uint32_t *)(base + sizeof(struct otp_header)); - otp->ecc = (uint32_t *)(base + sizeof(struct otp_header) + data_size); - otp->ecc_bit_count = 0u; - otp->ecc_granule = 0u; + if (lc_state) { + memcpy(lc_state, &otp->data[R_LC_STATE], LC_STATE_SIZE); + } - if (s->blk) { - int rc = blk_pread(s->blk, 0, (int64_t)otp_size, otp->storage, 0); - if (rc < 0) { - error_setg(&error_fatal, - "%s: failed to read the initial OTP content %zu bytes: " - "%d", - s->ot_id, otp_size, rc); - return; - } + if (lc_valid) { + *lc_valid = !(s->part_ctrls[OTP_PART_SECRET0].failed || + s->part_ctrls[OTP_PART_SECRET2].failed || + s->part_ctrls[s->part_lc_num].failed) ? + OT_MULTIBITBOOL_LC4_TRUE : + OT_MULTIBITBOOL_LC4_FALSE; + } + if (secret_valid) { + *secret_valid = (!s->part_ctrls[OTP_PART_SECRET2].failed && + s->part_ctrls[OTP_PART_SECRET2].locked) ? + OT_MULTIBITBOOL_LC4_TRUE : + OT_MULTIBITBOOL_LC4_FALSE; + } + if (tokens) { + *tokens = s->tokens; + } +} - const struct otp_header *otp_hdr = (const struct otp_header *)base; +static void ot_otp_dj_get_keymgr_secret( + OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + int partition; + size_t offset; - if (memcmp(otp_hdr->magic, "vOTP", sizeof(otp_hdr->magic)) != 0) { - error_setg(&error_fatal, "%s: OTP file is not a valid OTP backend", - s->ot_id); - return; - } - if (otp_hdr->version != 1u && otp_hdr->version != 2u) { - error_setg(&error_fatal, "%s: OTP file version %u is not supported", - s->ot_id, otp_hdr->version); - return; - } + switch (type) { + case OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: + partition = OTP_PART_SECRET2; + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - s->part_descs[partition].offset; + break; + case OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: + partition = OTP_PART_SECRET2; + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - s->part_descs[partition].offset; + break; + case OT_OTP_KEYMGR_SECRET_CREATOR_SEED: + partition = OTP_PART_SECRET2; + offset = A_SECRET2_CREATOR_SEED - s->part_descs[partition].offset; + break; + case OT_OTP_KEYMGR_SECRET_OWNER_SEED: + partition = OTP_PART_SECRET3; + offset = A_SECRET3_OWNER_SEED - s->part_descs[partition].offset; + break; + default: + error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, + s->ot_id, type); + secret->valid = false; + memset(secret->secret, 0, OT_OTP_KEYMGR_SECRET_SIZE); + return; + } - uintptr_t data_offset = otp_hdr->hlength + 8u; /* magic & length */ - uintptr_t ecc_offset = data_offset + otp_hdr->data_len; + unsigned part_ix = (unsigned)partition; + g_assert(ot_otp_engine_is_buffered(s, part_ix)); - otp->data = (uint32_t *)(base + data_offset); - otp->ecc = (uint32_t *)(base + ecc_offset); - otp->ecc_bit_count = otp_hdr->eccbits; - otp->ecc_granule = otp_hdr->eccgran; + const uint8_t *data_ptr; + if (s->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { + data_ptr = (const uint8_t *)s->part_ctrls[part_ix].buffer.data; + } else { + /* source data from PartInvDefault instead of real buffer */ + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + data_ptr = pctrl->inv_default_data; + } - if (otp->ecc_bit_count != 6u || !ot_otp_dj_is_ecc_enabled(s)) { - qemu_log_mask(LOG_UNIMP, - "%s: %s: support for ECC %u/%u not implemented\n", - __func__, s->ot_id, otp->ecc_granule, - otp->ecc_bit_count); - } + secret->valid = s->part_ctrls[part_ix].digest != 0; + memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); +} - bool write = blk_supports_write_perm(s->blk); - trace_ot_otp_load_backend(s->ot_id, otp_hdr->version, - write ? "R/W" : "R/O", otp->ecc_bit_count, - otp->ecc_granule); +static void +ot_otp_dj_update_status_error(OtOTPImplIf *dev, OtOTPStatus error, bool set) +{ + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); - if (otp_hdr->version == 2u) { - /* - * Version 2 is deprecated and digest const/IV are now ignored. - * Nonetheless, keep checking for inconsistencies. - */ - if (s->digest_iv != ldq_le_p(otp_hdr->digest_iv)) { - error_report("%s: %s: OTP file digest IV mismatch", __func__, - s->ot_id); - } - if (memcmp(s->digest_const, otp_hdr->digest_constant, - sizeof(s->digest_const)) != 0) { - error_report("%s: %s: OTP file digest const mismatch", __func__, - s->ot_id); - } - } + uint32_t mask; + switch (error) { + case OT_OTP_STATUS_DAI: + mask = R_STATUS_DAI_ERROR_MASK; + break; + case OT_OTP_STATUS_LCI: + mask = R_STATUS_LCI_ERROR_MASK; + break; + default: + g_assert_not_reached(); + return; } - otp->data_size = data_size; - otp->ecc_size = ecc_size; - otp->size = otp_size; + if (set) { + s->regs[R_STATUS] |= mask; + } else { + s->regs[R_STATUS] &= ~mask; + } } -static void ot_otp_dj_pwr_load_hw_cfg(OtOTPDjState *s) +static void ot_otp_dj_pwr_load_hw_cfg(OtOTPEngineState *s) { - OtOTPStorage *otp = s->otp; + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + const OtOTPPartDesc *pdesc0 = &ic->part_descs[OTP_PART_HW_CFG0]; + const OtOTPPartDesc *pdesc1 = &ic->part_descs[OTP_PART_HW_CFG1]; + const OtOTPPartController *pctrl0 = &s->part_ctrls[OTP_PART_HW_CFG0]; + const OtOTPPartController *pctrl1 = &s->part_ctrls[OTP_PART_HW_CFG1]; + const uint8_t *pdata0 = (const uint8_t *)pctrl0->buffer.data; + const uint8_t *pdata1 = (const uint8_t *)pctrl1->buffer.data; + OtOTPHWCfg *hw_cfg = s->hw_cfg; - memcpy(hw_cfg->device_id, &otp->data[R_HW_CFG0_DEVICE_ID], + memcpy(hw_cfg->device_id, &pdata0[A_HW_CFG0_DEVICE_ID - pdesc0->offset], sizeof(hw_cfg->device_id)); - memcpy(hw_cfg->manuf_state, &otp->data[R_HW_CFG0_MANUF_STATE], + memcpy(hw_cfg->manuf_state, &pdata0[A_HW_CFG0_MANUF_STATE - pdesc0->offset], sizeof(hw_cfg->manuf_state)); - memcpy(hw_cfg->soc_dbg_state, &otp->data[R_HW_CFG1_SOC_DBG_STATE], + memcpy(hw_cfg->soc_dbg_state, + &pdata1[A_HW_CFG1_SOC_DBG_STATE - pdesc1->offset], sizeof(hw_cfg->soc_dbg_state)); /* do not prevent execution from SRAM if no OTP configuration is loaded */ hw_cfg->en_sram_ifetch_mb8 = - s->blk ? (uint8_t)otp->data[R_HW_CFG1_EN_SRAM_IFETCH] : + s->blk ? pdata1[A_HW_CFG1_EN_SRAM_IFETCH - pdesc1->offset] : OT_MULTIBITBOOL8_TRUE; /* do not prevent CSRNG app reads if no OTP configuration is loaded */ hw_cfg->en_csrng_sw_app_read_mb8 = - s->blk ? (uint8_t)otp->data[R_HW_CFG1_EN_CSRNG_SW_APP_READ] : + s->blk ? pdata1[A_HW_CFG1_EN_CSRNG_SW_APP_READ - pdesc1->offset] : OT_MULTIBITBOOL8_TRUE; } -static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) +static void ot_otp_dj_pwr_load_tokens(OtOTPEngineState *s) { memset(s->tokens, 0, sizeof(*s->tokens)); @@ -3557,21 +1154,21 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) static_assert(sizeof(OtOTPTokenValue) == 16u, "Invalid token size"); - for (unsigned tkx = 0; tkx < OTP_TOKEN_COUNT; tkx++) { + for (unsigned tkx = 0; tkx < OT_OTP_TOKEN_COUNT; tkx++) { unsigned partition; uint32_t secret_addr; switch (tkx) { - case OTP_TOKEN_TEST_UNLOCK: - partition = OTP_PART_SECRET0; + case OT_OTP_TOKEN_TEST_UNLOCK: + partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; break; - case OTP_TOKEN_TEST_EXIT: - partition = OTP_PART_SECRET0; + case OT_OTP_TOKEN_TEST_EXIT: + partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_EXIT_TOKEN; break; - case OTP_TOKEN_RMA: - partition = OTP_PART_SECRET2; + case OT_OTP_TOKEN_RMA: + partition = (unsigned)OTP_PART_SECRET2; secret_addr = A_SECRET2_RMA_TOKEN; break; default: @@ -3579,20 +1176,20 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) break; } - OtOTPPartController *pctrl = &s->partctrls[partition]; + OtOTPPartController *pctrl = &s->part_ctrls[partition]; g_assert(pctrl->buffer.data != NULL); /* byte offset of the secret within the partition */ unsigned secret_offset = - secret_addr - ot_otp_dj_part_data_offset(partition); + secret_addr - ot_otp_engine_part_data_offset(s, partition); g_assert(secret_offset + sizeof(OtOTPTokenValue) <= - OtOTPPartDescs[partition].size); + OT_OTP_PART_DESCS[partition].size); OtOTPTokenValue value; memcpy(&value, &pctrl->buffer.data[secret_offset / sizeof(uint32_t)], sizeof(OtOTPTokenValue)); - if (s->partctrls[partition].locked) { + if (s->part_ctrls[partition].locked) { tokens->values[tkx] = value; tokens->valid_bm |= 1u << tkx; } @@ -3603,342 +1200,14 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) } } -static void ot_otp_dj_pwr_initialize_partitions(OtOTPDjState *s) -{ - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - /* sanity check: all secret partitions are also buffered */ - g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); - - if (ot_otp_dj_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { - if (ot_otp_dj_apply_ecc(s, ix)) { - continue; - } - } - - if (OtOTPPartDescs[ix].sw_digest) { - s->partctrls[ix].digest = ot_otp_dj_get_part_digest(s, ix); - s->partctrls[ix].locked = s->partctrls[ix].digest != 0; - continue; - } - - if (OtOTPPartDescs[ix].buffered) { - ot_otp_dj_bufferize_partition(s, ix); - if (OtOTPPartDescs[ix].hw_digest) { - ot_otp_dj_check_buffered_partition_integrity(s, ix); - } - continue; - } - } -} - -static void ot_otp_dj_pwr_otp_bh(void *opaque) +static void ot_otp_dj_signal_pwr_sequence(OtOTPImplIf *dev) { - OtOTPDjState *s = opaque; - - /* - * This sequence is triggered from the Power Manager, in the early boot - * sequence while the OT IPs are maintained in reset. - * This means that all ot_otp_dj_pwr_* functions are called before the OTP - * IP is released from reset. - * - * The QEMU reset is not a 1:1 mapping to the actual HW. - */ - trace_ot_otp_pwr_otp_req(s->ot_id, "initialize"); + OtOTPEngineState *s = OT_OTP_ENGINE(dev); - /* load OTP data from OTP back-end file */ - ot_otp_dj_pwr_load(s); - /* check ECC, digests, configure locks and bufferize partitions */ - ot_otp_dj_pwr_initialize_partitions(s); - /* load HW configuration, that is HW "broadcasted signals" */ ot_otp_dj_pwr_load_hw_cfg(s); - /* load LC controller tokens */ ot_otp_dj_pwr_load_tokens(s); - - /* initialize direct access interface */ - ot_otp_dj_dai_init(s); - /* initialize LC controller interface */ - ot_otp_dj_lci_init(s); - - trace_ot_otp_pwr_otp_req(s->ot_id, "done"); - - /* toggle OTP completion to signal the power manager OTP init is complete */ - ibex_irq_set(&s->pwc_otp_rsp, 1); - ibex_irq_set(&s->pwc_otp_rsp, 0); -} - -static void ot_otp_dj_configure_scrmbl_key(OtOTPDjState *s) -{ - if (!s->scrmbl_key_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "scrmbl_key"); - return; - } - - size_t len = strlen(s->scrmbl_key_xstr); - if (len != (size_t)(SRAM_KEY_BYTES + SRAM_NONCE_BYTES) * 2u) { - error_setg(&error_fatal, "%s: %s invalid scrmbl_key length\n", __func__, - s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(s->scrmbl_key_init->key, - &s->scrmbl_key_xstr[0], SRAM_KEY_BYTES, false, - false)) { - error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", - __func__, s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(s->scrmbl_key_init->nonce, - &s->scrmbl_key_xstr[SRAM_KEY_BYTES * 2u], - SRAM_NONCE_BYTES, false, true)) { - error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", - __func__, s->ot_id); - return; - } -} - -static void ot_otp_dj_configure_digest(OtOTPDjState *s) -{ - memset(s->digest_const, 0, sizeof(s->digest_const)); - s->digest_iv = 0ull; - - if (!s->digest_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "digest_const"); - return; - } - - if (!s->digest_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "digest_iv"); - return; - } - - size_t len; - - len = strlen(s->digest_const_xstr); - if (len != sizeof(s->digest_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid digest_const length\n", - __func__, s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(s->digest_const, s->digest_const_xstr, - sizeof(s->digest_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse digest_const\n", - __func__, s->ot_id); - return; - } - - uint8_t digest_iv[sizeof(uint64_t)]; - - len = strlen(s->digest_iv_xstr); - if (len != sizeof(digest_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid digest_iv length\n", __func__, - s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(digest_iv, s->digest_iv_xstr, - sizeof(digest_iv), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse digest_iv\n", __func__, - s->ot_id); - return; - } - - s->digest_iv = ldq_le_p(digest_iv); -} - -static void ot_otp_dj_configure_sram(OtOTPDjState *s) -{ - memset(s->sram_const, 0, sizeof(s->sram_const)); - s->sram_iv = 0ull; - - if (!s->sram_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "sram_const"); - return; - } - - if (!s->sram_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "sram_iv"); - return; - } - - size_t len; - - len = strlen(s->sram_const_xstr); - if (len != sizeof(s->sram_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid sram_const length\n", __func__, - s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(s->sram_const, s->sram_const_xstr, - sizeof(s->sram_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse sram_const\n", - __func__, s->ot_id); - return; - } - - uint8_t sram_iv[sizeof(uint64_t)]; - - len = strlen(s->sram_iv_xstr); - if (len != sizeof(sram_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid sram_iv length\n", __func__, - s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(sram_iv, s->sram_iv_xstr, sizeof(sram_iv), - true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse sram_iv\n", __func__, - s->ot_id); - return; - } - - s->sram_iv = ldq_le_p(sram_iv); -} - -static void ot_otp_dj_configure_part_scramble_keys(OtOTPDjState *s) -{ - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { - continue; - } - - if (!s->otp_scramble_key_xstrs[ix]) { - /* if OTP data is loaded, unscrambling keys are mandatory */ - if (s->blk) { - error_setg(&error_fatal, - "%s: %s Missing OTP scrambling key for part %s (%u)", - __func__, s->ot_id, PART_NAME(ix), ix); - return; - } - continue; - } - - size_t len = strlen(s->otp_scramble_key_xstrs[ix]); - if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { - error_setg( - &error_fatal, - "%s: %s Invalid OTP scrambling key length %zu for part %s (%u)", - __func__, s->ot_id, len, PART_NAME(ix), ix); - return; - } - - g_assert(!s->otp_scramble_keys[ix]); - - s->otp_scramble_keys[ix] = g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - if (ot_common_parse_hexa_str(s->otp_scramble_keys[ix], - s->otp_scramble_key_xstrs[ix], - OTP_SCRAMBLING_KEY_BYTES, true, true)) { - error_setg(&error_fatal, - "%s: %s unable to parse otp_scramble_keys[%u] for %s", - __func__, s->ot_id, ix, PART_NAME(ix)); - return; - } - - TRACE_OTP("otp_scramble_keys[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->otp_scramble_keys[ix], - OTP_SCRAMBLING_KEY_BYTES)); - } -} - -static void ot_otp_dj_class_add_scramble_key_props(OtOTPClass *odc) -{ - unsigned secret_ix = 0u; - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { - continue; - } - - Property *prop = g_new0(Property, 1u); - - /* - * Assumes secret partitions are sequentially ordered and named - * SECRET0, SECRET1, SECRET2, SECRET3 etc. - */ - prop->name = g_strdup_printf("secret%u_scramble_key", secret_ix++); - prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPDjState, otp_scramble_key_xstrs) + - sizeof(char *) * ix; - - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); - } -} - -static void ot_otp_dj_configure_inv_default_parts(OtOTPDjState *s) -{ - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!s->inv_default_part_xstrs[ix]) { - continue; - } - - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; - - size_t len; - - len = strlen(s->inv_default_part_xstrs[ix]); - if (len != part->size * 2u) { - error_setg(&error_fatal, - "%s: %s invalid inv_default_part[%u] length\n", __func__, - s->ot_id, ix); - return; - } - - g_assert(!s->inv_default_parts[ix]); - - s->inv_default_parts[ix] = g_new0(uint8_t, part->size + 1u); - if (ot_common_parse_hexa_str(s->inv_default_parts[ix], - s->inv_default_part_xstrs[ix], part->size, - false, true)) { - error_setg(&error_fatal, - "%s: %s unable to parse inv_default_part[%u]\n", - __func__, s->ot_id, ix); - return; - } - - TRACE_OTP("inv_default_part[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->inv_default_parts[ix], part->size)); - } -} - -static void ot_otp_dj_class_add_inv_def_props(OtOTPClass *odc) -{ - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].buffered) { - continue; - } - - Property *prop = g_new0(Property, 1u); - - prop->name = g_strdup_printf("inv_default_part_%u", ix); - prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPDjState, inv_default_part_xstrs) + - sizeof(char *) * ix; - - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); - } } -static Property ot_otp_dj_properties[] = { - DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOTPDjState, ot_id), - DEFINE_PROP_DRIVE("drive", OtOTPDjState, blk), - DEFINE_PROP_LINK("backend", OtOTPDjState, otp_backend, TYPE_OT_OTP_BE_IF, - OtOtpBeIf *), - DEFINE_PROP_LINK("edn", OtOTPDjState, edn, TYPE_OT_EDN, OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtOTPDjState, edn_ep, UINT8_MAX), - DEFINE_PROP_STRING("scrmbl_key", OtOTPDjState, scrmbl_key_xstr), - DEFINE_PROP_STRING("digest_const", OtOTPDjState, digest_const_xstr), - DEFINE_PROP_STRING("digest_iv", OtOTPDjState, digest_iv_xstr), - DEFINE_PROP_STRING("sram_const", OtOTPDjState, sram_const_xstr), - DEFINE_PROP_STRING("sram_iv", OtOTPDjState, sram_iv_xstr), - DEFINE_PROP_BOOL("fatal_escalate", OtOTPDjState, fatal_escalate, false), - DEFINE_PROP_END_OF_LIST(), -}; - static const MemoryRegionOps ot_otp_dj_reg_ops = { .read = &ot_otp_dj_reg_read, .write = &ot_otp_dj_reg_write, @@ -3956,47 +1225,10 @@ static const MemoryRegionOps ot_otp_dj_swcfg_ops = { static void ot_otp_dj_reset_enter(Object *obj, ResetType type) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); - OtOTPDjState *s = OT_OTP_DJ(obj); - - /* - * Note: beware of the special reset sequence for the OTP controller, - * see comments from ot_otp_dj_pwr_otp_bh, as this very QEMU reset may be - * called after ot_otp_dj_pwr_otp_bh is invoked, hereby changing the usual - * realize-reset sequence. - * - * File back-end storage (loading) is processed from - * the ot_otp_dj_pwr_otp_bh handler, to ensure data is reloaded from the - * backend on each reset, prior to this very reset function. This reset - * function should not alter the storage content. - * - * Ideally the OTP reset functions should be decoupled from the regular - * IP reset, which are exercised automatically from the SoC, since all the - * OT SysBysDevice IPs are connected to the private system bus of the Ibex. - * This is by-design in QEMU. The reset management is already far too - * complex to create a special case for the OTP. Kind in mind that the OTP - * reset_enter/reset_exit functions are QEMU regular reset functions called - * as part of the private bus reset and do not represent the actual OTP HW - * reset. Part of this reset is handled in the Power Manager handler. - */ - trace_ot_otp_reset(s->ot_id, "enter"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - qemu_bh_cancel(s->dai->digest_bh); - qemu_bh_cancel(s->lc_broadcast.bh); - qemu_bh_cancel(s->pwr_otp_bh); - - timer_del(s->dai->delay); - timer_del(s->lci->prog_delay); - qemu_bh_cancel(s->keygen->entropy_bh); - s->keygen->edn_sched = false; - - memset(s->regs, 0, REGS_COUNT * sizeof(uint32_t)); - memset(s->hw_cfg, 0, sizeof(*s->hw_cfg)); + OtOTPEngineState *s = OT_OTP_ENGINE(obj); + OtOTPDjClass *dc = OT_OTP_DJ_GET_CLASS(obj); + memset(s->regs, 0, REGS_SIZE); s->regs[R_DIRECT_ACCESS_REGWEN] = 0x00000001u; s->regs[R_CHECK_TRIGGER_REGWEN] = 0x00000001u; s->regs[R_CHECK_REGWEN] = 0x00000001u; @@ -4016,200 +1248,93 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) s->regs[R_EXT_NVM_READ_LOCK] = 0x00000001u; s->regs[R_ROM_PATCH_READ_LOCK] = 0x00000001u; - s->alert_bm = 0u; - - s->lc_broadcast.current_level = 0u; - s->lc_broadcast.level = 0u; - s->lc_broadcast.signal = 0u; - - ot_otp_dj_update_irqs(s); - ot_otp_dj_update_alerts(s); - ibex_irq_set(&s->pwc_otp_rsp, 0); - - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - /* TODO: initialize with actual default partition data once known */ - if (OtOTPPartDescs[ix].buffered) { - s->partctrls[ix].state.b = OTP_BUF_IDLE; - } else { - s->partctrls[ix].state.u = OTP_UNBUF_IDLE; - continue; - } - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); - memset(s->partctrls[ix].buffer.data, 0, part_size); - s->partctrls[ix].digest = 0; - if (OtOTPPartDescs[ix].iskeymgr_creator || - OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = true; - s->partctrls[ix].write_lock = true; - } + if (dc->parent_phases.enter) { + /* OtOTPEngineState cleanup */ + dc->parent_phases.enter(obj, type); } - DAI_CHANGE_STATE(s, OTP_DAI_RESET); - LCI_CHANGE_STATE(s, OTP_LCI_RESET); } -static void ot_otp_dj_reset_exit(Object *obj, ResetType type) +static void ot_otp_dj_init(Object *obj) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); OtOTPDjState *s = OT_OTP_DJ(obj); + OtOTPEngineState *es = OT_OTP_ENGINE(obj); - trace_ot_otp_reset(s->ot_id, "exit"); - - if (c->parent_phases.exit) { - c->parent_phases.exit(obj, type); - } - - OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); - memcpy(&s->be_chars, bec->get_characteristics(s->otp_backend), - sizeof(OtOtpBeCharacteristics)); - - ot_edn_connect_endpoint(s->edn, s->edn_ep, &ot_otp_dj_keygen_push_entropy, - s); - - qemu_bh_schedule(s->keygen->entropy_bh); -} - -static void ot_otp_dj_realize(DeviceState *dev, Error **errp) -{ - OtOTPDjState *s = OT_OTP_DJ(dev); - (void)errp; - - g_assert(s->ot_id); - g_assert(s->otp_backend); - - /* - * Set the OTP drive's permissions now during realization. We can't leave it - * until reset because QEMU might have `-daemonize`d and changed directory, - * invalidating the filesystem path to the OTP image. - */ - if (s->blk) { - bool write = blk_supports_write_perm(s->blk); - uint64_t perm = BLK_PERM_CONSISTENT_READ | (write ? BLK_PERM_WRITE : 0); - if (blk_set_perm(s->blk, perm, perm, &error_fatal)) { - warn_report("%s: %s: OTP backend is R/O", __func__, s->ot_id); - } - } - - ot_otp_dj_configure_scrmbl_key(s); - ot_otp_dj_configure_digest(s); - ot_otp_dj_configure_sram(s); - ot_otp_dj_configure_part_scramble_keys(s); - ot_otp_dj_configure_inv_default_parts(s); -} + /* note: device realization is implemented in OtOTPEngineState */ -static void ot_otp_dj_init(Object *obj) -{ - OtOTPDjState *s = OT_OTP_DJ(obj); + es->regs = g_new0(uint32_t, REGS_COUNT); + es->reg_offset.dai_base = R_DIRECT_ACCESS_REGWEN; + es->reg_offset.err_code_base = R_ERR_CODE_0; + es->reg_offset.read_lock_base = R_VENDOR_TEST_READ_LOCK; /* * "ctrl" region covers two sub-regions: * - "regs", registers: * offset 0, size REGS_SIZE * - "swcfg", software config window - * offset SW_CFG_WINDOW, size SW_CFG_WINDOW_SIZE + * offset SW_CFG_WINDOW_OFFSET, size SW_CFG_WINDOW_SIZE */ - memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP "-ctrl", - SW_CFG_WINDOW + SW_CFG_WINDOW_SIZE); + memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP_DJ "-ctrl", + SW_CFG_WINDOW_OFFSET + SW_CFG_WINDOW_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio.ctrl); memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_dj_reg_ops, s, - TYPE_OT_OTP "-regs", REGS_SIZE); + TYPE_OT_OTP_DJ "-regs", REGS_SIZE); memory_region_add_subregion(&s->mmio.ctrl, 0u, &s->mmio.sub.regs); - /* TODO: it might be worthwhile to use a ROM-kind here */ + /* @todo it might be worthwhile to use a ROM-kind here */ memory_region_init_io(&s->mmio.sub.swcfg, obj, &ot_otp_dj_swcfg_ops, s, - TYPE_OT_OTP "-swcfg", SW_CFG_WINDOW_SIZE); - memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, + TYPE_OT_OTP_DJ "-swcfg", SW_CFG_WINDOW_SIZE); + memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW_OFFSET, &s->mmio.sub.swcfg); - - ibex_qdev_init_irq(obj, &s->pwc_otp_rsp, OT_PWRMGR_OTP_RSP); - - qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_dj_pwr_otp_req, - OT_PWRMGR_OTP_REQ, 1); - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_sysbus_init_irq(obj, &s->irqs[ix]); - } - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); - } - - qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_dj_lc_broadcast_recv, - OT_LC_BROADCAST, OT_OTP_LC_BROADCAST_COUNT); - - s->hw_cfg = g_new0(OtOTPHWCfg, 1u); - s->tokens = g_new0(OtOTPTokens, 1u); - s->regs = g_new0(uint32_t, REGS_COUNT); - s->dai = g_new0(OtOTPDAIController, 1u); - s->lci = g_new0(OtOTPLCIController, 1u); - s->partctrls = g_new0(OtOTPPartController, OTP_PART_COUNT); - s->keygen = g_new0(OtOTPKeyGen, 1u); - s->otp = g_new0(OtOTPStorage, 1u); - s->scrmbl_key_init = g_new0(OtOTPScrmblKeyInit, 1u); - - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (!OtOTPPartDescs[ix].buffered) { - continue; - } - size_t part_words = - ot_otp_dj_part_data_byte_size(ix) / sizeof(uint32_t); - s->partctrls[ix].buffer.data = g_new0(uint32_t, part_words); - } - - ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT); - s->keygen->present = ot_present_new(); - s->keygen->prng = ot_prng_allocate(); - - s->dai->delay = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_otp_dj_dai_complete, s); - s->dai->digest_bh = qemu_bh_new(&ot_otp_dj_dai_write_digest, s); - s->lci->prog_delay = - timer_new_ns(OT_OTP_HW_CLOCK, &ot_otp_dj_lci_write_word, s); - s->pwr_otp_bh = qemu_bh_new(&ot_otp_dj_pwr_otp_bh, s); - s->lc_broadcast.bh = qemu_bh_new(&ot_otp_dj_lc_broadcast_bh, s); - s->keygen->entropy_bh = qemu_bh_new(&ot_otp_dj_request_entropy_bh, s); - - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - ot_prng_reseed(s->keygen->prng, (uint32_t)now); - -#ifdef OT_OTP_DEBUG - s->hexstr = g_new0(char, OT_OTP_HEXSTR_SIZE); -#endif } static void ot_otp_dj_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - g_assert(OTP_PART_LIFE_CYCLE_SIZE == - OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size); - - dc->realize = &ot_otp_dj_realize; - device_class_set_props(dc, ot_otp_dj_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - ResettableClass *rc = RESETTABLE_CLASS(klass); - OtOTPClass *oc = OT_OTP_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_otp_dj_reset_enter, NULL, - &ot_otp_dj_reset_exit, - &oc->parent_phases); + OtOTPDjClass *dc = OT_OTP_DJ_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_dj_reset_enter, NULL, NULL, + &dc->parent_phases); + OtOTPEngineClass *ec = OT_OTP_ENGINE_CLASS(klass); + OtOTPIfClass *oc = OT_OTP_IF_CLASS(klass); oc->get_lc_info = &ot_otp_dj_get_lc_info; - oc->get_hw_cfg = &ot_otp_dj_get_hw_cfg; - oc->get_otp_key = &ot_otp_dj_get_otp_key; + oc->get_hw_cfg = ec->get_hw_cfg; + oc->get_otp_key = ec->get_otp_key; oc->get_keymgr_secret = &ot_otp_dj_get_keymgr_secret; - oc->program_req = &ot_otp_dj_program_req; + oc->program_req = ec->program_req; + + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_CLASS(klass); + ic->signal_pwr_sequence = &ot_otp_dj_signal_pwr_sequence; + ic->update_status_error = &ot_otp_dj_update_status_error; + + ic->part_descs = OT_OTP_PART_DESCS; + ic->part_count = (unsigned)OTP_PART_COUNT; + ic->part_lc_num = (unsigned)OTP_PART_LIFE_CYCLE; + ic->sram_key_req_slot_count = NUM_SRAM_KEY_REQ_SLOTS; + + ic->key_seeds = OT_OTP_KEY_SEEDS; + ic->has_flash_support = false; + ic->has_zer_support = true; - ot_otp_dj_class_add_scramble_key_props(oc); - ot_otp_dj_class_add_inv_def_props(oc); + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_SRAM].size == + SECRET1_SRAM_DATA_KEY_SEED_SIZE); } static const TypeInfo ot_otp_dj_info = { .name = TYPE_OT_OTP_DJ, - .parent = TYPE_OT_OTP, + .parent = TYPE_OT_OTP_ENGINE, .instance_size = sizeof(OtOTPDjState), .instance_init = &ot_otp_dj_init, - .class_size = sizeof(OtOTPClass), + .class_size = sizeof(OtOTPDjClass), .class_init = &ot_otp_dj_class_init, + .interfaces = + (InterfaceInfo[]){ + { TYPE_OT_OTP_IF }, /* public OTP API */ + { TYPE_OT_OTP_IMPL_IF }, /* private OTP API for OTP engine */ + {}, + }, }; static void ot_otp_dj_register_types(void) diff --git a/hw/opentitan/ot_otp_dj_parts.c b/hw/opentitan/ot_otp_dj_parts.c index 411802ead16f5..dda4c81752dff 100644 --- a/hw/opentitan/ot_otp_dj_parts.c +++ b/hw/opentitan/ot_otp_dj_parts.c @@ -8,8 +8,9 @@ /* clang-format off */ /* NOLINTBEGIN */ -static const OtOTPPartDesc OtOTPPartDescs[] = { +static const OtOTPPartDesc OT_OTP_PART_DESCS[] = { [OTP_PART_VENDOR_TEST] = { + .name = "VENDOR_TEST", .size = 64u, .offset = 0u, .zer_offset = UINT16_MAX, @@ -25,6 +26,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_CREATOR_SW_CFG] = { + .name = "CREATOR_SW_CFG", .size = 304u, .offset = 64u, .zer_offset = UINT16_MAX, @@ -40,6 +42,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_OWNER_SW_CFG] = { + .name = "OWNER_SW_CFG", .size = 600u, .offset = 368u, .zer_offset = UINT16_MAX, @@ -55,6 +58,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_OWNERSHIP_SLOT_STATE] = { + .name = "OWNERSHIP_SLOT_STATE", .size = 48u, .offset = 968u, .zer_offset = UINT16_MAX, @@ -70,6 +74,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_ROT_CREATOR_AUTH] = { + .name = "ROT_CREATOR_AUTH", .size = 1424u, .offset = 1016u, .zer_offset = UINT16_MAX, @@ -85,6 +90,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_ROT_OWNER_AUTH_SLOT0] = { + .name = "ROT_OWNER_AUTH_SLOT0", .size = 328u, .offset = 2440u, .zer_offset = UINT16_MAX, @@ -100,6 +106,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_ROT_OWNER_AUTH_SLOT1] = { + .name = "ROT_OWNER_AUTH_SLOT1", .size = 328u, .offset = 2768u, .zer_offset = UINT16_MAX, @@ -115,6 +122,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_INTEG_AUTH_SLOT0] = { + .name = "PLAT_INTEG_AUTH_SLOT0", .size = 328u, .offset = 3096u, .zer_offset = UINT16_MAX, @@ -130,6 +138,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_INTEG_AUTH_SLOT1] = { + .name = "PLAT_INTEG_AUTH_SLOT1", .size = 328u, .offset = 3424u, .zer_offset = UINT16_MAX, @@ -145,6 +154,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_OWNER_AUTH_SLOT0] = { + .name = "PLAT_OWNER_AUTH_SLOT0", .size = 328u, .offset = 3752u, .zer_offset = UINT16_MAX, @@ -160,6 +170,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_OWNER_AUTH_SLOT1] = { + .name = "PLAT_OWNER_AUTH_SLOT1", .size = 328u, .offset = 4080u, .zer_offset = UINT16_MAX, @@ -175,6 +186,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_OWNER_AUTH_SLOT2] = { + .name = "PLAT_OWNER_AUTH_SLOT2", .size = 328u, .offset = 4408u, .zer_offset = UINT16_MAX, @@ -190,6 +202,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_PLAT_OWNER_AUTH_SLOT3] = { + .name = "PLAT_OWNER_AUTH_SLOT3", .size = 328u, .offset = 4736u, .zer_offset = UINT16_MAX, @@ -205,6 +218,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_EXT_NVM] = { + .name = "EXT_NVM", .size = 1024u, .offset = 5064u, .zer_offset = UINT16_MAX, @@ -220,6 +234,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_ROM_PATCH] = { + .name = "ROM_PATCH", .size = 9864u, .offset = 6088u, .zer_offset = UINT16_MAX, @@ -235,6 +250,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_HW_CFG0] = { + .name = "HW_CFG0", .size = 72u, .offset = 15952u, .zer_offset = UINT16_MAX, @@ -249,6 +265,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_HW_CFG1] = { + .name = "HW_CFG1", .size = 16u, .offset = 16024u, .zer_offset = UINT16_MAX, @@ -263,6 +280,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = false, }, [OTP_PART_SECRET0] = { + .name = "SECRET0", .size = 48u, .offset = 16040u, .zer_offset = 16080u, @@ -277,6 +295,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = true, }, [OTP_PART_SECRET1] = { + .name = "SECRET1", .size = 32u, .offset = 16088u, .zer_offset = 16112u, @@ -291,6 +310,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .zeroizable = true, }, [OTP_PART_SECRET2] = { + .name = "SECRET2", .size = 128u, .offset = 16120u, .zer_offset = 16240u, @@ -306,6 +326,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .iskeymgr_creator = true, }, [OTP_PART_SECRET3] = { + .name = "SECRET3", .size = 48u, .offset = 16248u, .zer_offset = 16288u, @@ -321,6 +342,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .iskeymgr_owner = true, }, [OTP_PART_LIFE_CYCLE] = { + .name = "LIFE_CYCLE", .size = 88u, .offset = 16296u, .zer_offset = UINT16_MAX, @@ -335,7 +357,20 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { }, }; -#define OTP_PART_COUNT ARRAY_SIZE(OtOTPPartDescs) +#define OTP_PART_COUNT ARRAY_SIZE(OT_OTP_PART_DESCS) + +static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OT_OTP_KEY_COUNT] = { + [OT_OTP_KEY_OTBN] = { + .partition = OTP_PART_SECRET1, + .offset = 0, + .size = 16, + }, + [OT_OTP_KEY_SRAM] = { + .partition = OTP_PART_SECRET1, + .offset = 0, + .size = 16, + }, +}; /* NOLINTEND */ /* clang-format on */ diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 068cc8e933c3b..97370bb2abc1f 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1,5 +1,5 @@ /* - * QEMU OpenTitan EarlGrey One Time Programmable (OTP) memory controller + * QEMU OpenTitan EarlGrey One Time Programmable (OTP) implementation * * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. @@ -28,37 +28,16 @@ * THE SOFTWARE. */ +#define OT_OTP_COMPORTABLE_REGS + #include "qemu/osdep.h" -#include "qemu/bswap.h" #include "qemu/log.h" -#include "qemu/timer.h" -#include "qemu/typedefs.h" -#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_fifo32.h" -#include "hw/opentitan/ot_lc_ctrl.h" -#include "hw/opentitan/ot_otp_be_if.h" +#include "qom/object.h" #include "hw/opentitan/ot_otp_eg.h" -#include "hw/opentitan/ot_present.h" -#include "hw/opentitan/ot_prng.h" -#include "hw/opentitan/ot_pwrmgr.h" -#include "hw/qdev-properties-system.h" -#include "hw/qdev-properties.h" -#include "hw/registerfields.h" -#include "hw/riscv/ibex_common.h" -#include "hw/riscv/ibex_irq.h" -#include "hw/sysbus.h" -#include "sysemu/block-backend.h" +#include "hw/opentitan/ot_otp_engine.h" +#include "hw/opentitan/ot_otp_impl_if.h" #include "trace.h" -#undef OT_OTP_DEBUG - -#define NUM_IRQS 2u -#define NUM_ALERTS 5u -#define NUM_DAI_WORDS 2u -#define NUM_DIGEST_WORDS 2u #define NUM_ERROR_ENTRIES 13u #define NUM_PART 11u #define NUM_PART_BUF 6u @@ -70,17 +49,6 @@ /* clang-format off */ /* Core registers */ -REG32(INTR_STATE, 0x00u) - SHARED_FIELD(INTR_OTP_OPERATION_DONE, 0u, 1u) - SHARED_FIELD(INTR_OTP_ERROR, 1u, 1u) -REG32(INTR_ENABLE, 0x04u) -REG32(INTR_TEST, 0x08u) -REG32(ALERT_TEST, 0x0cu) - SHARED_FIELD(ALERT_FATAL_MACRO_ERROR, 0u, 1u) - SHARED_FIELD(ALERT_FATAL_CHECK_ERROR, 1u, 1u) - SHARED_FIELD(ALERT_FATAL_BUS_INTEG_ERROR, 2u, 1u) - SHARED_FIELD(ALERT_FATAL_PRIM_OTP_ALERT, 3u, 1u) - SHARED_FIELD(ALERT_RECOV_PRIM_OTP_ALERT, 4u, 1u) REG32(STATUS, 0x10u) FIELD(STATUS, VENDOR_TEST_ERROR, 0u, 1u) FIELD(STATUS, CREATOR_SW_CFG_ERROR, 1u, 1u) @@ -103,7 +71,6 @@ REG32(STATUS, 0x10u) FIELD(STATUS, DAI_IDLE, 18u, 1u) FIELD(STATUS, CHECK_PENDING, 19u, 1u) REG32(ERR_CODE_0, 0x14u) - SHARED_FIELD(ERR_CODE, 0u, 3u) REG32(ERR_CODE_1, 0x18u) REG32(ERR_CODE_2, 0x1cu) REG32(ERR_CODE_3, 0x20u) @@ -119,9 +86,6 @@ REG32(ERR_CODE_12, 0x44u) REG32(DIRECT_ACCESS_REGWEN, 0x48u) FIELD(DIRECT_ACCESS_REGWEN, REGWEN, 0u, 1u) REG32(DIRECT_ACCESS_CMD, 0x4cu) - FIELD(DIRECT_ACCESS_CMD, RD, 0u, 1u) - FIELD(DIRECT_ACCESS_CMD, WR, 1u, 1u) - FIELD(DIRECT_ACCESS_CMD, DIGEST, 2u, 1u) REG32(DIRECT_ACCESS_ADDRESS, 0x50u) FIELD(DIRECT_ACCESS_ADDRESS, ADDRESS, 0, 11u) REG32(DIRECT_ACCESS_WDATA_0, 0x54u) @@ -139,7 +103,6 @@ REG32(CHECK_TIMEOUT, 0x70u) REG32(INTEGRITY_CHECK_PERIOD, 0x74u) REG32(CONSISTENCY_CHECK_PERIOD, 0x78u) REG32(VENDOR_TEST_READ_LOCK, 0x7cu) - SHARED_FIELD(READ_LOCK, 0u, 1u) REG32(CREATOR_SW_CFG_READ_LOCK, 0x80u) REG32(OWNER_SW_CFG_READ_LOCK, 0x84u) REG32(ROT_CREATOR_AUTH_CODESIGN_READ_LOCK, 0x88u) @@ -165,7 +128,7 @@ REG32(SECRET1_DIGEST_1, 0xd4u) REG32(SECRET2_DIGEST_0, 0xd8u) REG32(SECRET2_DIGEST_1, 0xdcu) -/* Software Config Window registers (at offset SW_CFG_WINDOW = +0x800) */ +/* Software Config Window registers (at offset SW_CFG_WINDOW_OFFSET) */ REG32(VENDOR_TEST_SCRATCH, 0u) REG32(VENDOR_TEST_DIGEST, 56u) REG32(CREATOR_SW_CFG_AST_CFG, 64u) @@ -382,29 +345,12 @@ REG32(LC_STATE, 2008u) #define SECRET1_FLASH_ADDR_KEY_SEED_SIZE 32u #define SECRET1_FLASH_DATA_KEY_SEED_SIZE 32u #define SECRET1_SRAM_DATA_KEY_SEED_SIZE 16u -#define SECRET2_SIZE 88u #define SECRET2_RMA_TOKEN_SIZE 16u #define SECRET2_CREATOR_ROOT_KEY_SHARE0_SIZE 32u #define SECRET2_CREATOR_ROOT_KEY_SHARE1_SIZE 32u -#define LC_TRANSITION_CNT_SIZE 48u -#define LC_STATE_SIZE 40u - -#define INTR_WMASK (INTR_OTP_OPERATION_DONE_MASK | INTR_OTP_ERROR_MASK) - -#define ALERT_WMASK \ - (ALERT_FATAL_MACRO_ERROR_MASK | ALERT_FATAL_CHECK_ERROR_MASK | \ - ALERT_FATAL_BUS_INTEG_ERROR_MASK | ALERT_FATAL_PRIM_OTP_ALERT_MASK | \ - ALERT_RECOV_PRIM_OTP_ALERT_MASK) -#define DIRECT_ACCESS_CMD_WMASK \ - (R_DIRECT_ACCESS_CMD_RD_MASK | R_DIRECT_ACCESS_CMD_WR_MASK | \ - R_DIRECT_ACCESS_CMD_DIGEST_MASK) - -#define CHECK_TRIGGER_WMASK \ - (R_CHECK_TRIGGER_INTEGRITY_MASK | R_CHECK_TRIGGER_CONSISTENCY_MASK) - -#define SW_CFG_WINDOW 0x800u -#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) +#define SW_CFG_WINDOW_OFFSET 0x800u +#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) @@ -414,57 +360,9 @@ REG32(LC_STATE, 2008u) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") -/* - * The OTP may be used before any CPU is started, This may cause the default - * virtual clock to stall, as the hart does not execute. OTP nevertheless may - * be active, updating the OTP content where write delays are still needed. - * Use the alternative clock source which counts even when the CPU is stalled. - */ -#define OT_OTP_HW_CLOCK QEMU_CLOCK_VIRTUAL_RT - -/* the following delays are arbitrary for now */ -#define DAI_DIGEST_DELAY_NS 50000u /* 50us */ -#define LCI_PROG_SCHED_NS 1000u /* 1us*/ - -/* The size of keys used for OTP scrambling */ -#define OTP_SCRAMBLING_KEY_WIDTH 128u -#define OTP_SCRAMBLING_KEY_BYTES ((OTP_SCRAMBLING_KEY_WIDTH) / 8u) - -/* Sizes of constants used for deriving scrambling keys (e.g. flash, SRAM) */ -#define FLASH_KEY_SEED_WIDTH 256u -#define SRAM_KEY_SEED_WIDTH 128u -#define KEY_MGR_KEY_WIDTH 256u -#define FLASH_KEY_WIDTH 128u -/* nonce is same size as key: see req_bundles[0] in rtl/otp_ctrl_kdi.sv */ -#define FLASH_NONCE_WIDTH (FLASH_KEY_WIDTH) -#define SRAM_KEY_WIDTH 128u -#define SRAM_NONCE_WIDTH 128u -#define OTBN_KEY_WIDTH 128u -#define OTBN_NONCE_WIDTH 64u - -static_assert(FLASH_KEY_SEED_WIDTH == SECRET1_FLASH_ADDR_KEY_SEED_SIZE * 8u, - "Flash key seed size does not match flash address field size"); -static_assert(FLASH_KEY_SEED_WIDTH == SECRET1_FLASH_DATA_KEY_SEED_SIZE * 8u, - "Flash key seed size does not match flash data field size"); -static_assert(SRAM_KEY_SEED_WIDTH == SECRET1_SRAM_DATA_KEY_SEED_SIZE * 8u, - "SRAM key seed size does not match OTP field size"); - -#define FLASH_KEY_BYTES ((FLASH_KEY_WIDTH) / 8u) -#define FLASH_NONCE_BYTES ((FLASH_NONCE_WIDTH) / 8u) -#define SRAM_KEY_BYTES ((SRAM_KEY_WIDTH) / 8u) -#define SRAM_NONCE_BYTES ((SRAM_NONCE_WIDTH) / 8u) -#define OTBN_KEY_BYTES ((OTBN_KEY_WIDTH) / 8u) -#define OTBN_NONCE_BYTES ((OTBN_NONCE_WIDTH) / 8u) - -/* Need 128 bits of entropy to compute each 64-bit key part */ -#define OTP_ENTROPY_PRESENT_BITS \ - (((NUM_SRAM_KEY_REQ_SLOTS * SRAM_KEY_WIDTH) + OTBN_KEY_WIDTH) * 128u / 64u) -#define OTP_ENTROPY_PRESENT_WORDS (OTP_ENTROPY_PRESENT_BITS / 32u) -#define OTP_ENTROPY_NONCE_BITS \ - (NUM_SRAM_KEY_REQ_SLOTS * SRAM_NONCE_WIDTH + OTBN_NONCE_WIDTH) -#define OTP_ENTROPY_NONCE_WORDS (OTP_ENTROPY_NONCE_BITS / 32u) -#define OTP_ENTROPY_BUF_COUNT \ - (OTP_ENTROPY_PRESENT_WORDS + OTP_ENTROPY_NONCE_WORDS) +/* note: useless casts are required for GCC linter */ +static_assert((unsigned)R_STATUS == (unsigned)R_OTP_FIRST_IMPL_REG, + "Invalid register address"); typedef enum { OTP_PART_VENDOR_TEST, @@ -486,182 +384,18 @@ typedef enum { static_assert(OTP_PART_OTP_COUNT == NUM_PART, "Invalid partition count"); -/* Error code (compliant with ERR_CODE registers) */ -typedef enum { - OTP_NO_ERROR, - OTP_MACRO_ERROR, - OTP_MACRO_ECC_CORR_ERROR, /* This is NOT an error */ - OTP_MACRO_ECC_UNCORR_ERROR, - OTP_MACRO_WRITE_BLANK_ERROR, - OTP_ACCESS_ERROR, - OTP_CHECK_FAIL_ERROR, /* Digest error */ - OTP_FSM_STATE_ERROR, -} OtOTPError; - -/* States of an unbuffered partition FSM */ -typedef enum { - OTP_UNBUF_RESET, - OTP_UNBUF_INIT, - OTP_UNBUF_INIT_WAIT, - OTP_UNBUF_IDLE, - OTP_UNBUF_READ, - OTP_UNBUF_READ_WAIT, - OTP_UNBUF_ERROR, -} OtOTPUnbufState; - -/* States of a buffered partition FSM */ -typedef enum { - OTP_BUF_RESET, - OTP_BUF_INIT, - OTP_BUF_INIT_WAIT, - OTP_BUF_INIT_DESCR, - OTP_BUF_INIT_DESCR_WAIT, - OTP_BUF_IDLE, - OTP_BUF_INTEG_SCR, - OTP_BUF_INTEG_SCR_WAIT, - OTP_BUF_INTEG_DIG_CLR, - OTP_BUF_INTEG_DIG, - OTP_BUF_INTEG_DIG_PAD, - OTP_BUF_INTEG_DIG_FIN, - OTP_BUF_INTEG_DIG_WAIT, - OTP_BUF_CNSTY_READ, - OTP_BUF_CNSTY_READ_WAIT, - OTP_BUF_ERROR, -} OtOTPBufState; - -typedef enum { - OTP_DAI_RESET, - OTP_DAI_INIT_OTP, - OTP_DAI_INIT_PART, - OTP_DAI_IDLE, - OTP_DAI_ERROR, - OTP_DAI_READ, - OTP_DAI_READ_WAIT, - OTP_DAI_DESCR, - OTP_DAI_DESCR_WAIT, - OTP_DAI_WRITE, - OTP_DAI_WRITE_WAIT, - OTP_DAI_SCR, - OTP_DAI_SCR_WAIT, - OTP_DAI_DIG_CLR, - OTP_DAI_DIG_READ, - OTP_DAI_DIG_READ_WAIT, - OTP_DAI_DIG, - OTP_DAI_DIG_PAD, - OTP_DAI_DIG_FIN, - OTP_DAI_DIG_WAIT, -} OtOTPDAIState; - -typedef enum { - OTP_LCI_RESET, - OTP_LCI_IDLE, - OTP_LCI_WRITE, - OTP_LCI_WRITE_WAIT, - OTP_LCI_ERROR, -} OtOTPLCIState; - -/* TODO: wr and rd lock need to be rewritten (not simple boolean) */ - -typedef struct { - uint16_t size; - uint16_t offset; - uint16_t digest_offset; - uint16_t hw_digest:1; - uint16_t sw_digest:1; - uint16_t secret:1; - uint16_t buffered:1; - uint16_t write_lock:1; - uint16_t read_lock:1; - uint16_t read_lock_csr:1; - uint16_t integrity:1; - uint16_t iskeymgr_creator:1; - uint16_t iskeymgr_owner:1; -} OtOTPPartDesc; - #define OT_OTP_EG_PARTS -#define OTP_PART_LIFE_CYCLE_SIZE 88u - /* NOLINTNEXTLINE */ #include "ot_otp_eg_parts.c" -static_assert(OTP_PART_COUNT <= 64, "Maximum part count reached"); static_assert(OTP_PART_OTP_COUNT == OTP_PART_COUNT, "Invalid partition count"); - -#define OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) +static_assert(OTP_PART_COUNT <= 64, "Maximum part count reached"); static_assert(OTP_BYTE_ADDR_WIDTH == R_DIRECT_ACCESS_ADDRESS_ADDRESS_LENGTH, "OTP byte address width mismatch"); - -typedef struct { - union { - OtOTPBufState b; - OtOTPUnbufState u; - } state; - struct { - uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ - uint64_t next_digest; /* computed HW digest to store into OTP cell */ - } buffer; /* only meaningful for buffered partitions */ - uint64_t digest; /* digest as read from OTP back end at init time */ - bool locked; - bool failed; - bool read_lock; - bool write_lock; -} OtOTPPartController; - -typedef struct { - QEMUTimer *delay; /* simulate delayed access completion */ - QEMUBH *digest_bh; /* write computed digest to OTP cell */ - OtOTPDAIState state; - int partition; /* current partition being worked on or -1 */ -} OtOTPDAIController; - -typedef struct { - QEMUTimer *prog_delay; /* OTP cell prog delay (use OT_OTP_HW_CLOCK) */ - OtOTPLCIState state; - OtOTPError error; - ot_otp_program_ack_fn ack_fn; - void *ack_data; - uint16_t data[OTP_PART_LIFE_CYCLE_SIZE / sizeof(uint16_t)]; - unsigned hpos; /* current offset in data */ -} OtOTPLCIController; - -typedef struct { - uint32_t *storage; /* overall buffer for the storage backend */ - uint32_t *data; /* data buffer (all partitions) */ - uint32_t *ecc; /* ecc buffer for date */ - unsigned size; /* overall storage size in bytes */ - unsigned data_size; /* data buffer size in bytes */ - unsigned ecc_size; /* ecc buffer size in bytes */ - unsigned ecc_bit_count; /* count of ECC bit for each data granule */ - unsigned ecc_granule; /* size of a granule in bytes */ -} OtOTPStorage; - -typedef struct { - QEMUBH *bh; - uint16_t signal; /* each bit tells if signal needs to be handled */ - uint16_t level; /* level of the matching signal */ - uint16_t current_level; /* current level of all signals */ -} OtOTPLcBroadcast; - -static_assert(OT_OTP_LC_BROADCAST_COUNT < 8 * sizeof(uint16_t), - "Invalid OT_OTP_LC_BROADCAST_COUNT"); - -typedef struct { - QEMUBH *entropy_bh; - OtPresentState *present; - OtPrngState *prng; - OtFifo32 entropy_buf; - bool edn_sched; -} OtOTPKeyGen; - -typedef struct { - uint8_t key[SRAM_KEY_BYTES]; - uint8_t nonce[SRAM_NONCE_BYTES]; -} OtOTPScrmblKeyInit; - struct OtOTPEgState { - OtOTPState parent_obj; + OtOTPEngineState parent_obj; struct { MemoryRegion ctrl; @@ -670,55 +404,11 @@ struct OtOTPEgState { MemoryRegion swcfg; } sub; } mmio; - QEMUBH *pwr_otp_bh; - IbexIRQ irqs[NUM_IRQS]; - IbexIRQ alerts[NUM_ALERTS]; - IbexIRQ pwc_otp_rsp; - - uint32_t *regs; - uint32_t alert_bm; - - OtOTPLcBroadcast lc_broadcast; - OtOTPDAIController *dai; - OtOTPLCIController *lci; - OtOTPPartController *partctrls; - OtOTPKeyGen *keygen; - OtOTPScrmblKeyInit *scrmbl_key_init; - OtOtpBeCharacteristics be_chars; - uint64_t digest_iv; - uint8_t digest_const[16u]; - uint64_t sram_iv; - uint8_t sram_const[16u]; - uint64_t flash_data_iv; - uint8_t flash_data_const[16u]; - uint64_t flash_addr_iv; - uint8_t flash_addr_const[16u]; - /* OTP scrambling key constants, not constants for deriving other keys */ - uint8_t *otp_scramble_keys[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - uint8_t *inv_default_parts[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - - OtOTPStorage *otp; - OtOTPHWCfg *hw_cfg; - OtOTPTokens *tokens; - char *hexstr; +}; - char *ot_id; - BlockBackend *blk; /* OTP host backend */ - OtOtpBeIf *otp_backend; - OtEDNState *edn; - char *scrmbl_key_xstr; - char *digest_const_xstr; - char *digest_iv_xstr; - char *sram_const_xstr; - char *sram_iv_xstr; - char *flash_data_iv_xstr; - char *flash_data_const_xstr; - char *flash_addr_iv_xstr; - char *flash_addr_const_xstr; - char *otp_scramble_key_xstrs[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - char *inv_default_part_xstrs[ARRAY_SIZE(OtOTPPartDescs)]; /* may be NULL */ - uint8_t edn_ep; - bool fatal_escalate; +struct OtOTPEgClass { + OtOTPEngineClass parent_class; + ResettablePhases parent_phases; }; #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) @@ -784,3139 +474,626 @@ static const char *REG_NAMES[REGS_COUNT] = { }; #undef REG_NAME_ENTRY -#define OTP_NAME_ENTRY(_st_) [_st_] = stringify(_st_) - -static const char *DAI_STATE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_DAI_RESET), - OTP_NAME_ENTRY(OTP_DAI_INIT_OTP), - OTP_NAME_ENTRY(OTP_DAI_INIT_PART), - OTP_NAME_ENTRY(OTP_DAI_IDLE), - OTP_NAME_ENTRY(OTP_DAI_ERROR), - OTP_NAME_ENTRY(OTP_DAI_READ), - OTP_NAME_ENTRY(OTP_DAI_READ_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DESCR), - OTP_NAME_ENTRY(OTP_DAI_DESCR_WAIT), - OTP_NAME_ENTRY(OTP_DAI_WRITE), - OTP_NAME_ENTRY(OTP_DAI_WRITE_WAIT), - OTP_NAME_ENTRY(OTP_DAI_SCR), - OTP_NAME_ENTRY(OTP_DAI_SCR_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DIG_CLR), - OTP_NAME_ENTRY(OTP_DAI_DIG_READ), - OTP_NAME_ENTRY(OTP_DAI_DIG_READ_WAIT), - OTP_NAME_ENTRY(OTP_DAI_DIG), - OTP_NAME_ENTRY(OTP_DAI_DIG_PAD), - OTP_NAME_ENTRY(OTP_DAI_DIG_FIN), - OTP_NAME_ENTRY(OTP_DAI_DIG_WAIT), - /* clang-format on */ -}; - -static const char *LCI_STATE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_LCI_RESET), - OTP_NAME_ENTRY(OTP_LCI_IDLE), - OTP_NAME_ENTRY(OTP_LCI_WRITE), - OTP_NAME_ENTRY(OTP_LCI_WRITE_WAIT), - OTP_NAME_ENTRY(OTP_LCI_ERROR), - /* clang-format on */ -}; +#define OT_OTP_NAME_ENTRY(_st_) [OT_OTP_##_st_] = stringify(OT_OTP_##_st_) static const char *OTP_TOKEN_NAMES[] = { /* clang-format off */ - OTP_NAME_ENTRY(OTP_TOKEN_TEST_UNLOCK), - OTP_NAME_ENTRY(OTP_TOKEN_TEST_EXIT), - OTP_NAME_ENTRY(OTP_TOKEN_RMA), - /* clang-format on */ -}; - -static const char *PART_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_PART_VENDOR_TEST), - OTP_NAME_ENTRY(OTP_PART_CREATOR_SW_CFG), - OTP_NAME_ENTRY(OTP_PART_OWNER_SW_CFG), - OTP_NAME_ENTRY(OTP_PART_ROT_CREATOR_AUTH_CODESIGN), - OTP_NAME_ENTRY(OTP_PART_ROT_CREATOR_AUTH_STATE), - OTP_NAME_ENTRY(OTP_PART_HW_CFG0), - OTP_NAME_ENTRY(OTP_PART_HW_CFG1), - OTP_NAME_ENTRY(OTP_PART_SECRET0), - OTP_NAME_ENTRY(OTP_PART_SECRET1), - OTP_NAME_ENTRY(OTP_PART_SECRET2), - OTP_NAME_ENTRY(OTP_PART_LIFE_CYCLE), - /* fake partitions */ - OTP_NAME_ENTRY(OTP_ENTRY_DAI), - OTP_NAME_ENTRY(OTP_ENTRY_KDI), + OT_OTP_NAME_ENTRY(TOKEN_TEST_UNLOCK), + OT_OTP_NAME_ENTRY(TOKEN_TEST_EXIT), + OT_OTP_NAME_ENTRY(TOKEN_RMA), /* clang-format on */ }; -static const char *ERR_CODE_NAMES[] = { - /* clang-format off */ - OTP_NAME_ENTRY(OTP_NO_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ECC_CORR_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_ECC_UNCORR_ERROR), - OTP_NAME_ENTRY(OTP_MACRO_WRITE_BLANK_ERROR), - OTP_NAME_ENTRY(OTP_ACCESS_ERROR), - OTP_NAME_ENTRY(OTP_CHECK_FAIL_ERROR), - OTP_NAME_ENTRY(OTP_FSM_STATE_ERROR), - /* clang-format on */ -}; - -#undef OTP_NAME_ENTRY - -#define BUF_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(BUF_STATE_NAMES) ? \ - BUF_STATE_NAMES[(_st_)] : \ - "?") -#define UNBUF_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(UNBUF_STATE_NAMES) ? \ - UNBUF_STATE_NAMES[(_st_)] : \ - "?") -#define DAI_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(DAI_STATE_NAMES) ? \ - DAI_STATE_NAMES[(_st_)] : \ - "?") -#define LCI_STATE_NAME(_st_) \ - ((unsigned)(_st_) < ARRAY_SIZE(LCI_STATE_NAMES) ? \ - LCI_STATE_NAMES[(_st_)] : \ - "?") #define OTP_TOKEN_NAME(_tk_) \ ((unsigned)(_tk_) < ARRAY_SIZE(OTP_TOKEN_NAMES) ? \ OTP_TOKEN_NAMES[(_tk_)] : \ "?") -#define PART_NAME(_pt_) \ - (((unsigned)(_pt_)) < ARRAY_SIZE(PART_NAMES) ? PART_NAMES[(_pt_)] : "?") -#define ERR_CODE_NAME(_err_) \ - (((unsigned)(_err_)) < ARRAY_SIZE(ERR_CODE_NAMES) ? \ - ERR_CODE_NAMES[(_err_)] : \ - "?") - -static void ot_otp_eg_dai_set_error(OtOTPEgState *s, OtOTPError err); - -static void -ot_otp_eg_dai_change_state_line(OtOTPEgState *s, OtOTPDAIState state, int line); - -#define DAI_CHANGE_STATE(_s_, _st_) \ - ot_otp_eg_dai_change_state_line(_s_, _st_, __LINE__) - -static void -ot_otp_eg_lci_change_state_line(OtOTPEgState *s, OtOTPLCIState state, int line); - -#define LCI_CHANGE_STATE(_s_, _st_) \ - ot_otp_eg_lci_change_state_line(_s_, _st_, __LINE__) - -#ifdef OT_OTP_DEBUG -#define OT_OTP_HEXSTR_SIZE 256u -#define TRACE_OTP(msg, ...) qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); -#define ot_otp_hexdump(_s_, _b_, _l_) \ - ot_common_lhexdump((const uint8_t *)_b_, _l_, false, (_s_)->hexstr, \ - OT_OTP_HEXSTR_SIZE) -#else -#define TRACE_OTP(msg, ...) -#define ot_otp_hexdump(_s_, _b_, _l_) -#endif - -static inline unsigned ot_otp_eg_part_data_offset(unsigned pix) -{ - return (unsigned)(OtOTPPartDescs[pix].offset); -} - -static inline unsigned ot_otp_eg_part_data_byte_size(unsigned pix) -{ - size_t size = OtOTPPartDescs[pix].size; - - if (OtOTPPartDescs[pix].hw_digest || OtOTPPartDescs[pix].sw_digest) { - size -= sizeof(uint32_t) * NUM_DIGEST_WORDS; - } - - return (unsigned)size; -} - -static void ot_otp_eg_update_irqs(OtOTPEgState *s) -{ - uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - int level = (int)((levels >> ix) & 0x1u); - if (level != ibex_irq_get_level(&s->irqs[ix])) { - trace_ot_otp_update_irq(s->ot_id, ibex_irq_get_level(&s->irqs[ix]), - level); - } - ibex_irq_set(&s->irqs[ix], level); - } -} - -static void ot_otp_eg_update_alerts(OtOTPEgState *s) -{ - uint32_t levels = s->regs[R_ALERT_TEST]; - - levels |= s->alert_bm; - - 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_otp_update_alert(s->ot_id, - ibex_irq_get_level(&s->alerts[ix]), - level); - } - ibex_irq_set(&s->alerts[ix], level); - } - - /* alert test is transient */ - if (s->regs[R_ALERT_TEST]) { - s->regs[R_ALERT_TEST] = 0; - levels = s->alert_bm; - 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_otp_update_alert(s->ot_id, - ibex_irq_get_level(&s->alerts[ix]), - level); - } - ibex_irq_set(&s->alerts[ix], level); - } - } -} - -static bool ot_otp_eg_is_wide_granule(unsigned part_ix, unsigned address) -{ - if (part_ix < OTP_PART_COUNT) { - if (OtOTPPartDescs[part_ix].secret) { - return true; - } - - if (OtOTPPartDescs[part_ix].digest_offset == - (address & OTP_DIGEST_ADDR_MASK)) { - return true; - } - } - - return false; -} - -static bool ot_otp_eg_is_buffered(unsigned part_ix) -{ - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].buffered; - } - - return false; -} - -static bool ot_otp_eg_is_secret(unsigned part_ix) -{ - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].secret; - } - - return false; -} - -static bool ot_otp_eg_is_backend_ecc_enabled(const OtOTPEgState *s) -{ - OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); - if (!bec->is_ecc_enabled) { - return true; - } - - return bec->is_ecc_enabled(s->otp_backend); -} - -static bool ot_otp_eg_is_ecc_enabled(const OtOTPEgState *s) -{ - return s->otp->ecc_granule == sizeof(uint16_t) && - ot_otp_eg_is_backend_ecc_enabled(s); -} - -static bool ot_otp_eg_has_digest(unsigned partition) +static uint32_t ot_otp_eg_get_status(const OtOTPEngineState *s) { - return OtOTPPartDescs[partition].hw_digest || - OtOTPPartDescs[partition].sw_digest; -} + uint32_t status; -static void ot_otp_eg_disable_all_partitions(OtOTPEgState *s) -{ - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); + status = FIELD_DP32(s->regs[R_STATUS], STATUS, DAI_IDLE, + !ot_otp_engine_dai_is_busy(s)); - for (unsigned pix = 0; pix < OTP_PART_COUNT; pix++) { - OtOTPPartController *pctrl = &s->partctrls[pix]; - pctrl->failed = true; - } + return status; } -static void ot_otp_eg_set_error(OtOTPEgState *s, unsigned part, OtOTPError err) +static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) { - g_assert(part < OTP_ENTRY_COUNT); - - uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; - if (errval || errval != s->regs[R_ERR_CODE_0 + part]) { - trace_ot_otp_set_error(s->ot_id, PART_NAME(part), part, - ERR_CODE_NAME(err), err); - } - s->regs[R_ERR_CODE_0 + part] = errval; + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; + uint32_t val32; - switch (err) { - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: - s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; - ot_otp_eg_update_alerts(s); + hwaddr reg = R32_OFF(addr); + switch (reg) { + case R_INTR_STATE: + case R_INTR_ENABLE: + case R_ERR_CODE_0 ... R_ERR_CODE_12: + case R_DIRECT_ACCESS_WDATA_0: + case R_DIRECT_ACCESS_WDATA_1: + case R_DIRECT_ACCESS_RDATA_0: + case R_DIRECT_ACCESS_RDATA_1: + case R_DIRECT_ACCESS_ADDRESS: + case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + case R_CHECK_TRIGGER_REGWEN: + case R_CHECK_REGWEN: + val32 = s->regs[reg]; + break; + case R_STATUS: + val32 = ot_otp_eg_get_status(s); + break; + case R_DIRECT_ACCESS_REGWEN: + /* disabled either if SW locked, or if DAI is busy. */ + val32 = s->regs[reg]; + val32 &= FIELD_DP32(0u, DIRECT_ACCESS_REGWEN, REGWEN, + (uint32_t)!ot_otp_engine_dai_is_busy(s)); break; /* NOLINTNEXTLINE */ - case OTP_MACRO_ECC_CORR_ERROR: - /* - * "The corresponding controller automatically recovers from this error - * when issuing a new command." - */ + case R_DIRECT_ACCESS_CMD: + case R_CHECK_TRIGGER: + val32 = 0; /* R0W1C */ break; - case OTP_MACRO_WRITE_BLANK_ERROR: + case R_CHECK_TIMEOUT: + case R_INTEGRITY_CHECK_PERIOD: + case R_CONSISTENCY_CHECK_PERIOD: + /* @todo: not yet implemented, but these are R/W registers */ + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(reg)); + val32 = s->regs[reg]; break; - case OTP_ACCESS_ERROR: - s->regs[R_STATUS] |= R_STATUS_DAI_ERROR_MASK; + case R_VENDOR_TEST_DIGEST_0 ... R_SECRET2_DIGEST_1: + /* + * In all partitions with a digest, the digest itself is ALWAYS + * readable. + */ + val32 = c->get_part_digest_reg(s, reg - R_VENDOR_TEST_DIGEST_0); break; - case OTP_CHECK_FAIL_ERROR: - case OTP_FSM_STATE_ERROR: - s->alert_bm |= ALERT_FATAL_CHECK_ERROR_MASK; - ot_otp_eg_update_alerts(s); + case R_INTR_TEST: + case R_ALERT_TEST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: W/O register 0x02%" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + val32 = 0; break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); + val32 = 0; break; } - if (s->alert_bm & ALERT_FATAL_CHECK_ERROR_MASK) { - ot_otp_eg_disable_all_partitions(s); - s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; - error_report("%s: %s: OTP disabled on fatal error", __func__, s->ot_id); - } + uint32_t pc = ibex_get_current_pc(); + trace_ot_otp_io_reg_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); - if (err != OTP_NO_ERROR) { - s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; - ot_otp_eg_update_irqs(s); - } + return (uint64_t)val32; } -static uint32_t ot_otp_eg_dai_is_busy(const OtOTPEgState *s) +static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { - return s->dai->state != OTP_DAI_IDLE; -} + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; + uint32_t val32 = (uint32_t)value; -static uint32_t ot_otp_eg_get_status(const OtOTPEgState *s) -{ - uint32_t status; + hwaddr reg = R32_OFF(addr); - status = FIELD_DP32(s->regs[R_STATUS], STATUS, DAI_IDLE, - !ot_otp_eg_dai_is_busy(s)); + uint32_t pc = ibex_get_current_pc(); - return status; -} + trace_ot_otp_io_reg_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); -static int ot_otp_eg_get_part_from_address(const OtOTPEgState *s, hwaddr addr) -{ - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; - if ((addr >= part->offset) && - ((addr + sizeof(uint32_t)) <= (part->offset + part->size))) { - trace_ot_otp_addr_to_part(s->ot_id, (uint32_t)addr, PART_NAME(ix), - ix); - return (OtOTPPartitionType)ix; + switch (reg) { + case R_DIRECT_ACCESS_CMD: + case R_DIRECT_ACCESS_ADDRESS: + case R_DIRECT_ACCESS_WDATA_0: + case R_DIRECT_ACCESS_WDATA_1: + case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + if (!(s->regs[R_DIRECT_ACCESS_REGWEN] & + R_DIRECT_ACCESS_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_DIRECT_ACCESS_REGWEN), + REG_NAME(reg)); + return; + } + break; + case R_CHECK_TRIGGER: + if (!(s->regs[R_CHECK_TRIGGER_REGWEN] & + R_CHECK_TRIGGER_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_CHECK_TRIGGER_REGWEN), + REG_NAME(reg)); + return; + } + break; + case R_CHECK_TIMEOUT: + case R_INTEGRITY_CHECK_PERIOD: + case R_CONSISTENCY_CHECK_PERIOD: + if (!(s->regs[R_CHECK_REGWEN] & R_CHECK_REGWEN_REGWEN_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: %s is not enabled, %s is protected\n", + __func__, s->ot_id, REG_NAME(R_CHECK_REGWEN), + REG_NAME(reg)); + return; } + break; + case R_STATUS: + case R_ERR_CODE_0 ... R_ERR_CODE_12: + case R_DIRECT_ACCESS_RDATA_0: + case R_DIRECT_ACCESS_RDATA_1: + case R_VENDOR_TEST_DIGEST_0 ... R_SECRET2_DIGEST_1: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + return; + default: + break; } - return -1; -} - -static uint8_t ot_otp_eg_compute_ecc_u16(uint16_t data) -{ - uint32_t data_o = (uint32_t)data; - - data_o |= __builtin_parity(data_o & 0x00ad5bu) << 16u; - data_o |= __builtin_parity(data_o & 0x00366du) << 17u; - data_o |= __builtin_parity(data_o & 0x00c78eu) << 18u; - data_o |= __builtin_parity(data_o & 0x0007f0u) << 19u; - data_o |= __builtin_parity(data_o & 0x00f800u) << 20u; - data_o |= __builtin_parity(data_o & 0x1fffffu) << 21u; - - return (uint8_t)(data_o >> 16u); -} - -static uint16_t ot_otp_eg_compute_ecc_u32(uint32_t data) -{ - uint16_t data_lo = (uint16_t)(data & UINT16_MAX); - uint16_t data_hi = (uint16_t)(data >> 16u); - - uint16_t ecc_lo = (uint16_t)ot_otp_eg_compute_ecc_u16(data_lo); - uint16_t ecc_hi = (uint16_t)ot_otp_eg_compute_ecc_u16(data_hi); - - return (ecc_hi << 8u) | ecc_lo; -} - -static uint32_t ot_otp_eg_compute_ecc_u64(uint64_t data) -{ - uint32_t data_lo = (uint32_t)(data & UINT32_MAX); - uint32_t data_hi = (uint32_t)(data >> 32u); - - uint32_t ecc_lo = (uint32_t)ot_otp_eg_compute_ecc_u32(data_lo); - uint32_t ecc_hi = (uint32_t)ot_otp_eg_compute_ecc_u32(data_hi); - - return (ecc_hi << 16u) | ecc_lo; -} - -static uint32_t ot_otp_eg_verify_ecc_22_16_u16(const OtOTPEgState *s, - uint32_t data_i, unsigned *err_o) -{ - unsigned syndrome = 0u; - - syndrome |= __builtin_parity(data_i & 0x01ad5bu) << 0u; - syndrome |= __builtin_parity(data_i & 0x02366du) << 1u; - syndrome |= __builtin_parity(data_i & 0x04c78eu) << 2u; - syndrome |= __builtin_parity(data_i & 0x0807f0u) << 3u; - syndrome |= __builtin_parity(data_i & 0x10f800u) << 4u; - syndrome |= __builtin_parity(data_i & 0x3fffffu) << 5u; - - unsigned err = (syndrome >> 5u) & 1u; - if (!err && (syndrome & 0x1fu)) { - err = 2u; - } - - *err_o = err; - - if (!err) { - return data_i & UINT16_MAX; - } - - uint32_t data_o = 0; - -#define OTP_ECC_RECOVER(_sy_, _di_, _ix_) \ - ((unsigned)((syndrome == (_sy_)) ^ (bool)((_di_) & (1u << (_ix_)))) \ - << (_ix_)) - - data_o |= OTP_ECC_RECOVER(0x23u, data_i, 0u); - data_o |= OTP_ECC_RECOVER(0x25u, data_i, 1u); - data_o |= OTP_ECC_RECOVER(0x26u, data_i, 2u); - data_o |= OTP_ECC_RECOVER(0x27u, data_i, 3u); - data_o |= OTP_ECC_RECOVER(0x29u, data_i, 4u); - data_o |= OTP_ECC_RECOVER(0x2au, data_i, 5u); - data_o |= OTP_ECC_RECOVER(0x2bu, data_i, 6u); - data_o |= OTP_ECC_RECOVER(0x2cu, data_i, 7u); - data_o |= OTP_ECC_RECOVER(0x2du, data_i, 8u); - data_o |= OTP_ECC_RECOVER(0x2eu, data_i, 9u); - data_o |= OTP_ECC_RECOVER(0x2fu, data_i, 10u); - data_o |= OTP_ECC_RECOVER(0x31u, data_i, 11u); - data_o |= OTP_ECC_RECOVER(0x32u, data_i, 12u); - data_o |= OTP_ECC_RECOVER(0x33u, data_i, 13u); - data_o |= OTP_ECC_RECOVER(0x34u, data_i, 14u); - data_o |= OTP_ECC_RECOVER(0x35u, data_i, 15u); - -#undef OTP_ECC_RECOVER - - if (err > 1u) { - trace_ot_otp_ecc_unrecoverable_error(s->ot_id, data_i & UINT16_MAX); - } else { - if ((data_i & UINT16_MAX) != data_o) { - trace_ot_otp_ecc_recovered_error(s->ot_id, data_i & UINT16_MAX, - data_o); - } else { - /* ECC bit is corrupted */ - trace_ot_otp_ecc_parity_error(s->ot_id, data_i & UINT16_MAX, - data_i >> 16u); - } - } - - return data_o; -} - -static uint32_t ot_otp_eg_verify_ecc(const OtOTPEgState *s, uint32_t data, - uint32_t ecc, unsigned *err) -{ - uint32_t data_lo_i, data_lo_o, data_hi_i, data_hi_o; - unsigned err_lo, err_hi; - - data_lo_i = (data & 0xffffu) | ((ecc & 0xffu) << 16u); - data_lo_o = ot_otp_eg_verify_ecc_22_16_u16(s, data_lo_i, &err_lo); - - data_hi_i = (data >> 16u) | (((ecc >> 8u) & 0xffu) << 16u); - data_hi_o = ot_otp_eg_verify_ecc_22_16_u16(s, data_hi_i, &err_hi); - - *err |= err_lo | err_hi; - - return (data_hi_o << 16u) | data_lo_o; -} - -static uint64_t ot_otp_eg_apply_digest_ecc(OtOTPEgState *s, unsigned partition, - uint64_t digest, uint32_t ecc) -{ - uint32_t dig_lo = (uint32_t)(digest & UINT32_MAX); - uint32_t dig_hi = (uint32_t)(digest >> 32u); - - unsigned err = 0; - dig_lo = ot_otp_eg_verify_ecc(s, dig_lo, ecc & 0xffffu, &err); - dig_hi = ot_otp_eg_verify_ecc(s, dig_hi, ecc >> 16u, &err); - digest = (((uint64_t)dig_hi) << 32u) | ((uint64_t)dig_lo); - - if (err) { - OtOTPError otp_err = - (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : OTP_MACRO_ECC_CORR_ERROR; - /* - * Note: need to check if any caller could override the error/state - * in this case - */ - ot_otp_eg_set_error(s, partition, otp_err); - } - - return digest; -} - -static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned part_ix) -{ - g_assert(ot_otp_eg_is_ecc_enabled(s)); - - unsigned start = OtOTPPartDescs[part_ix].offset >> 2u; - unsigned end = - (ot_otp_eg_is_buffered((int)part_ix) && ot_otp_eg_has_digest(part_ix)) ? - (unsigned)(OtOTPPartDescs[part_ix].digest_offset >> 2u) : - start + (unsigned)(OtOTPPartDescs[part_ix].size >> 2u); - - g_assert(start < end && (end / sizeof(uint32_t)) < s->otp->data_size); - for (unsigned ix = start; ix < end; ix++) { - unsigned err = 0; - uint32_t *word = &s->otp->data[ix]; - uint16_t ecc = ((const uint16_t *)s->otp->ecc)[ix]; - *word = ot_otp_eg_verify_ecc(s, *word, (uint32_t)ecc, &err); - if (err) { - OtOTPError otp_err = (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : - OTP_MACRO_ECC_CORR_ERROR; - /* - * Note: need to check if any caller could override the error/state - * in this case - */ - ot_otp_eg_set_error(s, part_ix, otp_err); - if (err > 1) { - trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(part_ix), - part_ix, ix << 2u, *word, ecc); - s->partctrls[part_ix].failed = true; - return -1; - } - } - } - - return 0; -} - -static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) -{ - g_assert(!ot_otp_eg_is_buffered(part_ix)); - - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; - - if (offset == UINT16_MAX) { - return 0u; - } - - const uint8_t *data = (const uint8_t *)s->otp->data; - uint64_t digest = ldq_le_p(data + offset); - - if (OtOTPPartDescs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s)) { - unsigned waddr = offset >> 2u; - unsigned ewaddr = waddr >> 1u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otp_eg_apply_digest_ecc(s, part_ix, digest, ecc); - } - - return digest; -} - -static bool ot_otp_eg_is_part_digest_offset(unsigned part_ix, hwaddr addr) -{ - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); -} - -static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) -{ - /* - * Offset is the register offset from the first read 32-bit digest register. - * All digests are 64-bits, which means each partition have two 32-bit - * registers to expose their digest. - * - * Not all partitions have digests, which means the offset argument is the - * relative partition offset for those partitions that features a digest, as - * there is no defined digest access registers defined for partitions that - * do not have a digest. - * - * Need to traverse the partition table to only account for those partitions - * with a digest to match the proper offset. - */ - - /* - * part_look_ix is the index of the partition in the contiguous array of - * digest registers, which would be equivalent as the index of the partition - * that would exist in a virtual partition-with-digest array. - */ - unsigned part_look_ix = (unsigned)(offset / NUM_DIGEST_WORDS); - /* whether to retrieve the top most 32-bit of the digest or not */ - bool hi = (bool)(offset & 0x1u); - - /* part_ix: the partition number in the global partition array */ - unsigned part_ix = 0; - /* traverse the partition array and count each partition with a digest */ - for (unsigned part_dig_ix = 0; part_ix < OTP_PART_COUNT; part_ix++) { - if (ot_otp_eg_has_digest(part_ix)) { - /* - * stop searching if we've found the part-with-digest defined from - * the offset argument. Otherwise, increment the part-with-digest - * index and continue. - */ - if (part_dig_ix == part_look_ix) { - break; - } - part_dig_ix++; - } - } - - /* - * If the part_ix as reached the latest partition, there is something wrong - * with the partition table or the register definitions, as it is assumed - * that LifeCycle partition is the last partition. - */ - static_assert(OTP_PART_LIFE_CYCLE == NUM_PART - 1, - "LC is expected to be the last partition"); - g_assert(part_ix < OTP_PART_COUNT); - - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - uint64_t digest = pctrl->digest; - - if (hi) { - digest >>= 32u; - } - - return (uint32_t)digest; -} - -static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) -{ - g_assert(part_ix < OTP_PART_COUNT); - - const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - if (pdesc->secret) { - /* secret partitions are only readable if digest is not yet set. */ - return pctrl->digest == 0u; - } - - if (!pdesc->read_lock_csr) { - if (!pdesc->read_lock) { - /* read lock is not supported for this partition */ - return true; - } - - /* hw read lock, not locked */ - return !pctrl->read_lock; - } - - unsigned roffset = 0; - unsigned pix; - for (pix = 0; pix < OTP_PART_COUNT; pix++) { - if (pix == part_ix) { - break; - } - if (pdesc->read_lock_csr) { - roffset++; - } - } - /* - * know for sure last partition is the life cycle one, which never - * support a read_lock_csr. Ideally this g_assert should be a - * static_assert, but C being C, constants are not defined as such - * at build time... - */ - g_assert(!OtOTPPartDescs[OTP_PART_LIFE_CYCLE].read_lock_csr); - - /* - * If the previous loop reached the last partition, something - * seriously wrong occurred. Use this feature as a sanity check - */ - g_assert(pix < OTP_PART_LIFE_CYCLE); - - /* - * now that the count of read_lock_csr is known, use it to access - * the register for the selected partition - */ - uint32_t reg = R_VENDOR_TEST_READ_LOCK + roffset; - - return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); -} - -static void -ot_otp_eg_dai_change_state_line(OtOTPEgState *s, OtOTPDAIState state, int line) -{ - trace_ot_otp_dai_change_state(s->ot_id, line, DAI_STATE_NAME(s->dai->state), - s->dai->state, DAI_STATE_NAME(state), state); - - s->dai->state = state; -} - -static void -ot_otp_eg_lci_change_state_line(OtOTPEgState *s, OtOTPLCIState state, int line) -{ - trace_ot_otp_lci_change_state(s->ot_id, line, LCI_STATE_NAME(s->lci->state), - s->lci->state, LCI_STATE_NAME(state), state); - - s->lci->state = state; -} - -static void ot_otp_eg_lc_broadcast_recv(void *opaque, int n, int level) -{ - OtOTPEgState *s = opaque; - OtOTPLcBroadcast *bcast = &s->lc_broadcast; - - g_assert((unsigned)n < OT_OTP_LC_BROADCAST_COUNT); - - uint16_t bit = 1u << (unsigned)n; - bcast->signal |= 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->level |= bit; - } else { - bcast->level &= ~bit; - } - - /* use a BH to decouple IRQ signaling from actual handling */ - qemu_bh_schedule(s->lc_broadcast.bh); -} - -static void ot_otp_eg_lc_broadcast_bh(void *opaque) -{ - OtOTPEgState *s = opaque; - OtOTPLcBroadcast *bcast = &s->lc_broadcast; - - /* handle all flagged signals */ - while (bcast->signal) { - /* pick and clear */ - unsigned sig = ctz16(bcast->signal); - uint16_t bit = 1u << (unsigned)sig; - bcast->signal &= ~bit; - bcast->current_level = - (bcast->current_level & ~bit) | (bcast->level & bit); - bool level = (bool)(bcast->current_level & bit); - - trace_ot_otp_lc_broadcast(s->ot_id, sig, level); - - switch ((int)sig) { - case OT_OTP_LC_DFT_EN: - qemu_log_mask(LOG_UNIMP, "%s: %s: DFT feature not supported\n", - __func__, s->ot_id); - break; - case OT_OTP_LC_ESCALATE_EN: - if (level) { - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - /* TODO: manage other FSMs */ - qemu_log_mask(LOG_UNIMP, - "%s: %s: ESCALATE partially implemented\n", - __func__, s->ot_id); - if (s->fatal_escalate) { - error_setg(&error_fatal, "%s: OTP LC escalate", s->ot_id); - } - } - break; - case OT_OTP_LC_CHECK_BYP_EN: - qemu_log_mask(LOG_UNIMP, "%s: %s: bypass is ignored\n", __func__, - s->ot_id); - break; - case OT_OTP_LC_CREATOR_SEED_SW_RW_EN: - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_creator) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; - } - } - break; - case OT_OTP_LC_OWNER_SEED_SW_RW_EN: - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; - } - } - break; - case OT_OTP_LC_SEED_HW_RD_EN: - /* nothing to do here, SEED_HW_RD_EN flag is in current_level */ - break; - default: - error_setg(&error_fatal, "%s: %s: unexpected LC broadcast %d\n", - __func__, s->ot_id, sig); - g_assert_not_reached(); - break; - } - } -} - -static uint64_t ot_otp_eg_compute_partition_digest( - OtOTPEgState *s, const uint8_t *base, unsigned size) -{ - OtPresentState *ps = ot_present_new(); - - g_assert((size & (sizeof(uint64_t) - 1u)) == 0); - - uint8_t buf[sizeof(uint64_t) * 2u]; - uint64_t state = s->digest_iv; - uint64_t out; - for (unsigned off = 0; off < size; off += sizeof(buf)) { - memcpy(buf, base + off, sizeof(uint64_t)); - if (off + sizeof(uint64_t) != size) { - memcpy(&buf[sizeof(uint64_t)], base + off + sizeof(uint64_t), - sizeof(uint64_t)); - } else { - /* special case, duplicate last block if block number is odd */ - memcpy(&buf[sizeof(uint64_t)], base + off, sizeof(uint64_t)); - } - - ot_present_init(ps, buf); - ot_present_encrypt(ps, state, &out); - state ^= out; - } - - ot_present_init(ps, s->digest_const); - ot_present_encrypt(ps, state, &out); - state ^= out; - - ot_present_free(ps); - - return state; -} - -static uint64_t -ot_otp_eg_load_partition_digest(OtOTPEgState *s, unsigned partition) -{ - unsigned digoff = (unsigned)OtOTPPartDescs[partition].digest_offset; - - if ((digoff + sizeof(uint64_t)) > s->otp->data_size) { - error_setg(&error_fatal, "%s: partition located outside storage?", - s->ot_id); - /* linter doest not know the above call never returns */ - return 0u; - } - - const uint8_t *data = (const uint8_t *)s->otp->data; - uint64_t digest = ldq_le_p(data + digoff); - - if (ot_otp_eg_is_ecc_enabled(s)) { - unsigned ewaddr = (digoff >> 3u); - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otp_eg_apply_digest_ecc(s, partition, digest, ecc); - } - - return digest; -} - -static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); - - /* part_size should be a multiple of PRESENT block size */ - g_assert((part_size & (sizeof(uint64_t) - 1u)) == 0u); - unsigned dword_count = part_size / sizeof(uint64_t); - - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; - - /* source address should be aligned to 64-bit boundary */ - g_assert(((uintptr_t)base & (sizeof(uint64_t) - 1u)) == 0u); - const uint64_t *scrambled = (const uint64_t *)base; - - /* destination address should be aligned to 64-bit boundary */ - g_assert(pctrl->buffer.data != NULL); - uint64_t *clear = (uint64_t *)pctrl->buffer.data; - - const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; - g_assert(scrambling_key); - - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - - trace_ot_otp_unscramble_partition(s->ot_id, PART_NAME(ix), ix, part_size); - /* neither the digest block nor the zeroizable block are scrambled */ - for (unsigned dix = 0u; dix < dword_count; dix++) { - ot_present_decrypt(ps, scrambled[dix], &clear[dix]); - } - - ot_present_free(ps); -} - -static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - g_assert(pctrl->buffer.data != NULL); - - if (OtOTPPartDescs[ix].hw_digest) { - pctrl->digest = ot_otp_eg_load_partition_digest(s, ix); - } else { - pctrl->digest = 0; - } - - if (OtOTPPartDescs[ix].secret) { - /* secret partitions need to be unscrambled */ - if (s->blk) { - /* - * nothing to unscramble if no OTP data is loaded - * scrambling keys in this case may not be known - */ - ot_otp_eg_unscramble_partition(s, ix); - } - } else { - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); - - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; - - memcpy(pctrl->buffer.data, base, part_size); - } -} - -static void -ot_otp_eg_check_buffered_partition_integrity(OtOTPEgState *s, unsigned ix) -{ - OtOTPPartController *pctrl = &s->partctrls[ix]; - - if (pctrl->digest == 0) { - trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); - pctrl->locked = false; - return; - } - - pctrl->locked = true; - - /* - * digests are always calculated over the original data (scrambled or not) - */ - const uint8_t *part_data = - ((const uint8_t *)s->otp->data) + ot_otp_eg_part_data_offset(ix); - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); - - uint64_t digest = - ot_otp_eg_compute_partition_digest(s, part_data, part_size); - - if (digest != pctrl->digest) { - trace_ot_otp_mismatch_digest(s->ot_id, PART_NAME(ix), ix, digest, - pctrl->digest); - - TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - PART_NAME(ix), digest, - ot_otp_hexdump(s, part_data, part_size)); - - pctrl->failed = true; - /* this is a fatal error */ - ot_otp_eg_set_error(s, ix, OTP_CHECK_FAIL_ERROR); - /* TODO: revert buffered part to default */ - } else { - trace_ot_otp_integrity_report(s->ot_id, PART_NAME(ix), ix, "digest OK"); - } -} - -static bool ot_otp_eg_is_backend_writable(OtOTPEgState *s) -{ - return (s->blk != NULL) && blk_is_writable(s->blk); -} - -static inline int ot_otp_eg_write_backend(OtOTPEgState *s, const void *buffer, - unsigned offset, size_t size) -{ - /* - * the blk_pwrite API is awful, isolate it so that linter exceptions are - * are not repeated over and over - */ - g_assert(offset + size <= s->otp->size); - - /* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */ - return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer, - /* a bitfield of enum is not an enum item */ - (BdrvRequestFlags)0); - /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ -} - -static void ot_otp_eg_dai_init(OtOTPEgState *s) -{ - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); -} - -static void ot_otp_eg_dai_set_error(OtOTPEgState *s, OtOTPError err) -{ - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, err); - - switch (err) { - case OTP_FSM_STATE_ERROR: - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - break; - default: - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - } -} - -static void ot_otp_eg_dai_clear_error(OtOTPEgState *s) -{ - s->regs[R_STATUS] &= ~R_STATUS_DAI_ERROR_MASK; - s->regs[R_ERR_CODE_0 + OTP_ENTRY_DAI] = 0; -} - -static void ot_otp_eg_dai_read(OtOTPEgState *s) -{ - if (ot_otp_eg_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - ot_otp_eg_dai_clear_error(s); - - DAI_CHANGE_STATE(s, OTP_DAI_READ); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_eg_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (partition >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - const OtOTPPartController *pctrl = &s->partctrls[partition]; - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); - return; - } - - unsigned part_ix = (unsigned)partition; - bool is_readable = ot_otp_eg_is_readable(s, part_ix); - bool is_wide = ot_otp_eg_is_wide_granule(part_ix, address); - bool is_buffered = ot_otp_eg_is_buffered(part_ix); - bool is_secret = ot_otp_eg_is_secret(part_ix); - bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, address); - - /* "in all partitions, the digest itself is ALWAYS readable." */ - if (!is_digest && !is_readable) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: partition %s @ 0x%04x not readable\n", __func__, - s->ot_id, PART_NAME(partition), address); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_offset = address - ot_otp_eg_part_data_offset(partition); - unsigned part_waddr = part_offset >> 2u; - bool do_ecc = - OtOTPPartDescs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s); - - DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); - - uint32_t data_lo, data_hi; - unsigned err = 0; - unsigned cell_count = sizeof(uint32_t) + (do_ecc ? sizeof(uint16_t) : 0); - - const uint32_t *data = (const uint32_t *)s->otp->data; - /* parenthesis inform the C linter sizeof() call is valid with 'data */ - data += (ot_otp_eg_part_data_offset(partition) / sizeof(uint32_t)); - - if (is_wide || is_digest) { - /* 64-bit requests */ - part_waddr &= ~0b1u; - - g_assert((part_waddr + 1u) * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); - - data_lo = data[part_waddr]; - data_hi = data[part_waddr + 1u]; - - if (do_ecc) { - unsigned ewaddr = address >> 3u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - if (ot_otp_eg_is_ecc_enabled(s)) { - data_lo = ot_otp_eg_verify_ecc(s, data_lo, ecc & 0xffffu, &err); - data_hi = ot_otp_eg_verify_ecc(s, data_hi, ecc >> 16u, &err); - } - } - - cell_count *= 2u; - } else { - /* 32-bit request */ - g_assert(part_waddr * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); - - data_lo = data[part_waddr]; - data_hi = 0u; - - if (do_ecc) { - unsigned ewaddr = address >> 3u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t ecc = s->otp->ecc[ewaddr]; - if ((address >> 2u) & 1u) { - ecc >>= 16u; - } - if (ot_otp_eg_is_ecc_enabled(s)) { - data_lo = ot_otp_eg_verify_ecc(s, data_lo, ecc & 0xffffu, &err); - } - cell_count = 4u + 2u; - } else { - cell_count = 4u; - } - } - - if (is_secret && !is_digest) { - /* - * if the partition is a secret partition, OTP storage is scrambled - * except the digest and the zeroification fields - */ - const uint8_t *scrambling_key = s->otp_scramble_keys[partition]; - g_assert(scrambling_key); - uint64_t tmp_data = ((uint64_t)data_hi << 32u) | data_lo; - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - ot_present_decrypt(ps, tmp_data, &tmp_data); - ot_present_free(ps); - data_lo = (uint32_t)tmp_data; - data_hi = (uint32_t)(tmp_data >> 32u); - } - - s->regs[R_DIRECT_ACCESS_RDATA_0] = data_lo; - s->regs[R_DIRECT_ACCESS_RDATA_1] = data_hi; - - if (err) { - OtOTPError otp_err = - (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : OTP_MACRO_ECC_CORR_ERROR; - ot_otp_eg_dai_set_error(s, otp_err); - return; - } - - s->dai->partition = partition; - - if (!is_buffered) { - /* fake slow access to OTP cell */ - unsigned access_time = s->be_chars.timings.read_ns * cell_count; - timer_mod(s->dai->delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + access_time); - } else { - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - } -} - -static int ot_otp_eg_dai_write_u64(OtOTPEgState *s, unsigned address) -{ - unsigned waddr = (address / sizeof(uint32_t)) & ~1u; - uint32_t *dst = &s->otp->data[waddr]; - - uint32_t dst_lo = dst[0u]; - uint32_t dst_hi = dst[1u]; - - uint32_t lo = s->regs[R_DIRECT_ACCESS_WDATA_0]; - uint32_t hi = s->regs[R_DIRECT_ACCESS_WDATA_1]; - - unsigned part_ix = (unsigned)s->dai->partition; - bool is_secret = ot_otp_eg_is_secret(part_ix); - - if (is_secret) { - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; - uint64_t data = ((uint64_t)hi << 32u) | lo; - g_assert(scrambling_key); - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - ot_present_encrypt(ps, data, &data); - lo = (uint32_t)data; - hi = (uint32_t)(data >> 32u); - } - - if ((dst_lo & ~lo) || (dst_hi & ~hi)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Cannot clear OTP bits\n", - __func__, s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - - dst[0u] |= lo; - dst[1u] |= hi; - - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, dst, - (unsigned)(offset + waddr * sizeof(uint32_t)), - sizeof(uint64_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - if (ot_otp_eg_is_ecc_enabled(s)) { - unsigned ewaddr = waddr >> 1u; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t *edst = &s->otp->ecc[ewaddr]; - - uint32_t ecc_lo = (uint32_t)ot_otp_eg_compute_ecc_u32(lo); - uint32_t ecc_hi = (uint32_t)ot_otp_eg_compute_ecc_u32(hi); - uint32_t ecc = (ecc_hi << 16u) | ecc_lo; - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Cannot clear OTP ECC bits\n", __func__, - s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, edst, (unsigned)(offset + (waddr << 1u)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - trace_ot_otp_dai_new_dword_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - } - - return 0; -} - -static int ot_otp_eg_dai_write_u32(OtOTPEgState *s, unsigned address) -{ - unsigned waddr = address / sizeof(uint32_t); - uint32_t *dst = &s->otp->data[waddr]; - uint32_t data = s->regs[R_DIRECT_ACCESS_WDATA_0]; - - if (*dst & ~data) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP bits\n", - __func__, s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - - *dst |= data; - - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, dst, - (unsigned)(offset + waddr * sizeof(uint32_t)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - if (ot_otp_eg_is_ecc_enabled(s)) { - g_assert((waddr >> 1u) < s->otp->ecc_size); - uint16_t *edst = &((uint16_t *)s->otp->ecc)[waddr]; - uint16_t ecc = ot_otp_eg_compute_ecc_u32(*dst); - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP ECC bits\n", __func__, - s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, edst, - (unsigned)(offset + (address >> 1u)), - sizeof(uint16_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return -1; - } - - trace_ot_otp_dai_new_word_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - } - - return 0; -} - -static void ot_otp_eg_dai_write(OtOTPEgState *s) -{ - if (ot_otp_eg_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - if (!ot_otp_eg_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); - - ot_otp_eg_dai_clear_error(s); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_eg_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_ix = (unsigned)partition; - - if (part_ix >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: Life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(part_ix)); - return; - } - - if (pctrl->locked) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (pctrl->write_lock) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: artition %s (%u) is write locked\n", __func__, - s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, address); - bool is_wide = ot_otp_eg_is_wide_granule(part_ix, address); - - if (is_digest) { - if (OtOTPPartDescs[partition].hw_digest) { - /* should have been a Digest command, not a Write command */ - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: partition %s (%u) HW digest cannot be directly " - "written\n", - __func__, s->ot_id, PART_NAME(part_ix), part_ix); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - } - - s->dai->partition = partition; - - bool do_ecc = ot_otp_eg_is_ecc_enabled(s); - unsigned cell_count = sizeof(uint32_t); - - if (is_wide || is_digest) { - if (ot_otp_eg_dai_write_u64(s, address)) { - return; - } - cell_count *= 2u; - } else { - if (ot_otp_eg_dai_write_u32(s, address)) { - return; - } - } - - if (do_ecc) { - cell_count += cell_count / 2u; - }; - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); - - /* fake slow update of OTP cell */ - unsigned update_time = s->be_chars.timings.write_ns * cell_count; - timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); -} - -static void ot_otp_eg_dai_digest(OtOTPEgState *s) -{ - if (ot_otp_eg_dai_is_busy(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", - __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); - return; - } - - if (!ot_otp_eg_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_CLR); - - ot_otp_eg_dai_clear_error(s); - - unsigned address = s->regs[R_DIRECT_ACCESS_ADDRESS]; - - int partition = ot_otp_eg_get_part_from_address(s, address); - - if (partition < 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Invalid partition address 0x%x\n", __func__, - s->ot_id, address); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (partition >= OTP_PART_LIFE_CYCLE) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: Life cycle partition cannot be accessed from DAI\n", - __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (!OtOTPPartDescs[partition].hw_digest) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Invalid partition, no HW digest on %s (#%u)\n", - __func__, s->ot_id, PART_NAME(partition), partition); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - OtOTPPartController *pctrl = &s->partctrls[partition]; - - if (pctrl->failed) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); - return; - } - - if (pctrl->locked) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(partition), partition); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - if (pctrl->write_lock) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Partition %s (%u) is write locked\n", __func__, - s->ot_id, PART_NAME(partition), partition); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); - - const uint8_t *data = - ((const uint8_t *)s->otp->data) + ot_otp_eg_part_data_offset(partition); - unsigned part_size = ot_otp_eg_part_data_byte_size(partition); - - DAI_CHANGE_STATE(s, OTP_DAI_DIG); - - pctrl->buffer.next_digest = - ot_otp_eg_compute_partition_digest(s, data, part_size); - s->dai->partition = partition; - - TRACE_OTP("%s: %s: next digest %016" PRIx64 " from %s\n", __func__, - s->ot_id, pctrl->buffer.next_digest, - ot_otp_hexdump(s, data, part_size)); - - DAI_CHANGE_STATE(s, OTP_DAI_DIG_WAIT); - - /* fake slow update of OTP cell */ - timer_mod(s->dai->delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_DIGEST_DELAY_NS); -} - -static void ot_otp_eg_dai_write_digest(void *opaque) -{ - OtOTPEgState *s = OT_OTP_EG(opaque); - - g_assert((s->dai->partition >= 0) && (s->dai->partition < OTP_PART_COUNT)); - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); - - OtOTPPartController *pctrl = &s->partctrls[s->dai->partition]; - unsigned address = OtOTPPartDescs[s->dai->partition].digest_offset; - unsigned dwaddr = address / sizeof(uint64_t); - uint64_t *dst = &((uint64_t *)s->otp->data)[dwaddr]; - uint64_t data = pctrl->buffer.next_digest; - pctrl->buffer.next_digest = 0; - - if (*dst & ~data) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP data bits\n", - __func__, s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *dst |= data; - - uintptr_t offset; - offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, dst, (unsigned)(offset + address), - sizeof(uint64_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - uint32_t ecc = ot_otp_eg_compute_ecc_u64(data); - - /* dwaddr is 64-bit based, convert it to 32-bit base for ECC */ - unsigned ewaddr = (dwaddr << 1u) / s->otp->ecc_granule; - g_assert(ewaddr < s->otp->ecc_size); - uint32_t *edst = &s->otp->ecc[ewaddr]; - - if (*edst & ~ecc) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP ECC bits\n", - __func__, s->ot_id); - ot_otp_eg_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); - } - *edst |= ecc; - - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, edst, (unsigned)(offset + (ewaddr << 2u)), - sizeof(uint32_t))) { - error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_eg_dai_set_error(s, OTP_MACRO_ERROR); - return; - } - - trace_ot_otp_dai_new_digest_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); - - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); - - /* fake slow update of OTP cell */ - unsigned cell_count = sizeof(uint64_t) + sizeof(uint32_t); - unsigned update_time = s->be_chars.timings.write_ns * cell_count; - timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); -} - -static void ot_otp_eg_dai_complete(void *opaque) -{ - OtOTPEgState *s = opaque; - - switch (s->dai->state) { - case OTP_DAI_READ_WAIT: - g_assert(s->dai->partition >= 0); - trace_ot_otp_dai_read(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, - s->regs[R_DIRECT_ACCESS_RDATA_1], - s->regs[R_DIRECT_ACCESS_RDATA_1]); - s->dai->partition = -1; - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - case OTP_DAI_WRITE_WAIT: - g_assert(s->dai->partition >= 0); - s->regs[R_INTR_STATE] |= INTR_OTP_OPERATION_DONE_MASK; - s->dai->partition = -1; - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); - break; - case OTP_DAI_DIG_WAIT: - g_assert(s->dai->partition >= 0); - qemu_bh_schedule(s->dai->digest_bh); - break; - case OTP_DAI_ERROR: - break; - default: - g_assert_not_reached(); - break; - }; -} - -static void ot_otp_eg_lci_init(OtOTPEgState *s) -{ - LCI_CHANGE_STATE(s, OTP_LCI_IDLE); -} - -static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) -{ - OtOTPEgState *s = OT_OTP_EG(opaque); - (void)size; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - switch (reg) { - case R_INTR_STATE: - case R_INTR_ENABLE: - case R_ERR_CODE_0 ... R_ERR_CODE_12: - case R_DIRECT_ACCESS_WDATA_0: - case R_DIRECT_ACCESS_WDATA_1: - case R_DIRECT_ACCESS_RDATA_0: - case R_DIRECT_ACCESS_RDATA_1: - case R_DIRECT_ACCESS_ADDRESS: - case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: - case R_CHECK_TRIGGER_REGWEN: - case R_CHECK_REGWEN: - val32 = s->regs[reg]; - break; - case R_STATUS: - val32 = ot_otp_eg_get_status(s); - break; - case R_DIRECT_ACCESS_REGWEN: - /* disabled either if SW locked, or if DAI is busy. */ - val32 = s->regs[reg]; - val32 &= FIELD_DP32(0u, DIRECT_ACCESS_REGWEN, REGWEN, - (uint32_t)!ot_otp_eg_dai_is_busy(s)); - break; - /* NOLINTNEXTLINE */ - case R_DIRECT_ACCESS_CMD: - case R_CHECK_TRIGGER: - val32 = 0; /* R0W1C */ - break; - case R_CHECK_TIMEOUT: - case R_INTEGRITY_CHECK_PERIOD: - case R_CONSISTENCY_CHECK_PERIOD: - /* @todo: not yet implemented, but these are R/W registers */ - qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, - s->ot_id, REG_NAME(reg)); - val32 = s->regs[reg]; - break; - case R_VENDOR_TEST_DIGEST_0 ... R_SECRET2_DIGEST_1: - /* - * In all partitions with a digest, the digest itself is ALWAYS - * readable. - */ - val32 = ot_otp_eg_get_part_digest_reg(s, reg - R_VENDOR_TEST_DIGEST_0); - break; - case R_INTR_TEST: - case R_ALERT_TEST: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: W/O register 0x02%" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(reg)); - val32 = 0; - break; - default: - 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_otp_io_reg_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, - pc); - - return (uint64_t)val32; -} - -static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - OtOTPEgState *s = OT_OTP_EG(opaque); - (void)size; - uint32_t val32 = (uint32_t)value; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - - trace_ot_otp_io_reg_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, - pc); - - switch (reg) { - case R_DIRECT_ACCESS_CMD: - case R_DIRECT_ACCESS_ADDRESS: - case R_DIRECT_ACCESS_WDATA_0: - case R_DIRECT_ACCESS_WDATA_1: - case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: - if (!(s->regs[R_DIRECT_ACCESS_REGWEN] & - R_DIRECT_ACCESS_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_DIRECT_ACCESS_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_CHECK_TRIGGER: - if (!(s->regs[R_CHECK_TRIGGER_REGWEN] & - R_CHECK_TRIGGER_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_CHECK_TRIGGER_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_CHECK_TIMEOUT: - case R_INTEGRITY_CHECK_PERIOD: - case R_CONSISTENCY_CHECK_PERIOD: - if (!(s->regs[R_CHECK_REGWEN] & R_CHECK_REGWEN_REGWEN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: %s is not enabled, %s is protected\n", - __func__, s->ot_id, REG_NAME(R_CHECK_REGWEN), - REG_NAME(reg)); - return; - } - break; - case R_STATUS: - case R_ERR_CODE_0 ... R_ERR_CODE_12: - case R_DIRECT_ACCESS_RDATA_0: - case R_DIRECT_ACCESS_RDATA_1: - case R_VENDOR_TEST_DIGEST_0 ... R_SECRET2_DIGEST_1: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(reg)); - return; - default: - break; - } - - switch (reg) { - case R_INTR_STATE: - val32 &= INTR_WMASK; - s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ - ot_otp_eg_update_irqs(s); - break; - case R_INTR_ENABLE: - val32 &= INTR_WMASK; - s->regs[R_INTR_ENABLE] = val32; - ot_otp_eg_update_irqs(s); - break; - case R_INTR_TEST: - val32 &= INTR_WMASK; - s->regs[R_INTR_STATE] = val32; - ot_otp_eg_update_irqs(s); - break; - case R_ALERT_TEST: - val32 &= ALERT_WMASK; - s->regs[reg] = val32; - ot_otp_eg_update_alerts(s); - break; - case R_DIRECT_ACCESS_REGWEN: - val32 &= R_DIRECT_ACCESS_REGWEN_REGWEN_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case R_DIRECT_ACCESS_CMD: - if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { - ot_otp_eg_dai_read(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { - ot_otp_eg_dai_write(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { - ot_otp_eg_dai_digest(s); - } - break; - case R_DIRECT_ACCESS_ADDRESS: - val32 &= R_DIRECT_ACCESS_ADDRESS_ADDRESS_MASK; - s->regs[reg] = val32; - break; - case R_DIRECT_ACCESS_WDATA_0: - case R_DIRECT_ACCESS_WDATA_1: - s->regs[reg] = val32; - break; - case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: - val32 &= READ_LOCK_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case R_CHECK_TRIGGER_REGWEN: - val32 &= R_CHECK_TRIGGER_REGWEN_REGWEN_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case R_CHECK_REGWEN: - val32 &= R_CHECK_REGWEN_REGWEN_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case R_CHECK_TRIGGER: - case R_CHECK_TIMEOUT: - case R_INTEGRITY_CHECK_PERIOD: - case R_CONSISTENCY_CHECK_PERIOD: - qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, - s->ot_id, REG_NAME(reg)); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, - s->ot_id, addr); - break; - } -} - -static const char *ot_otp_eg_swcfg_reg_name(unsigned swreg) -{ -#define CASE_BYTE(_reg_) \ - case A_##_reg_: \ - return stringify(_reg_) -#define CASE_SUB(_reg_, _sz_) \ - case A_##_reg_...(A_##_reg_ + (_sz_) - 1u): \ - return stringify(_reg_) -#define CASE_REG(_reg_) \ - case A_##_reg_...(A_##_reg_ + 3u): \ - return stringify(_reg_) -#define CASE_WIDE(_reg_) \ - case A_##_reg_...(A_##_reg_ + 7u): \ - return stringify(_reg_) -#define CASE_RANGE(_reg_) \ - case A_##_reg_...(A_##_reg_ + (_reg_##_SIZE) - 1u): \ - return stringify(_reg_) - - switch (swreg) { - CASE_RANGE(VENDOR_TEST_SCRATCH); - CASE_WIDE(VENDOR_TEST_DIGEST); - CASE_RANGE(CREATOR_SW_CFG_AST_CFG); - CASE_REG(CREATOR_SW_CFG_AST_INIT_EN); - CASE_REG(CREATOR_SW_CFG_ROM_EXT_SKU); - CASE_REG(CREATOR_SW_CFG_SIGVERIFY_SPX_EN); - CASE_REG(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG); - CASE_REG(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG); - CASE_REG(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE); - CASE_REG(CREATOR_SW_CFG_RNG_EN); - CASE_REG(CREATOR_SW_CFG_JITTER_EN); - CASE_REG(CREATOR_SW_CFG_RET_RAM_RESET_MASK); - CASE_REG(CREATOR_SW_CFG_MANUF_STATE); - CASE_REG(CREATOR_SW_CFG_ROM_EXEC_EN); - CASE_REG(CREATOR_SW_CFG_CPUCTRL); - CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT); - CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_BL0); - CASE_REG(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN); - CASE_REG(CREATOR_SW_CFG_RMA_SPIN_EN); - CASE_REG(CREATOR_SW_CFG_RMA_SPIN_CYCLES); - CASE_REG(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS); - CASE_REG(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD); - CASE_REG(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST); - CASE_REG(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN); - CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_EN); - CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_START_OFFSET); - CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_LENGTH); - CASE_RANGE(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_SHA256_HASH); - CASE_RANGE(CREATOR_SW_CFG_RESERVED); - CASE_WIDE(CREATOR_SW_CFG_DIGEST); - CASE_REG(OWNER_SW_CFG_ROM_ERROR_REPORTING); - CASE_REG(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_CLASS_EN); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_ESCALATION); - CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION); - CASE_RANGE(OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION); - CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH); - CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES); - CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV); - CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA); - CASE_REG(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES); - CASE_REG(OWNER_SW_CFG_ROM_KEYMGR_OTP_MEAS_EN); - CASE_REG(OWNER_SW_CFG_MANUF_STATE); - CASE_REG(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN); - CASE_REG(OWNER_SW_CFG_ROM_EXT_BOOTSTRAP_EN); - CASE_RANGE(OWNER_SW_CFG_ROM_SENSOR_CTRL_ALERT_CFG); - CASE_REG(OWNER_SW_CFG_ROM_SRAM_READBACK_EN); - CASE_REG(OWNER_SW_CFG_ROM_PRESERVE_RESET_REASON_EN); - CASE_REG(OWNER_SW_CFG_ROM_RESET_REASON_CHECK_VALUE); - CASE_REG(OWNER_SW_CFG_ROM_BANNER_EN); - CASE_REG(OWNER_SW_CFG_ROM_FLASH_ECC_EXC_HANDLER_EN); - CASE_RANGE(OWNER_SW_CFG_RESERVED); - CASE_WIDE(OWNER_SW_CFG_DIGEST); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE0); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY0); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE1); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY1); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE2); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY2); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE3); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY3); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE0); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY0); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG0); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE1); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY1); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG1); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE2); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY2); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG2); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE3); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY3); - CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG3); - CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_BLOCK_SHA2_256_HASH); - CASE_WIDE(ROT_CREATOR_AUTH_CODESIGN_DIGEST); - CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY0); - CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY1); - CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY2); - CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY3); - CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY0); - CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY1); - CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY2); - CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY3); - CASE_WIDE(ROT_CREATOR_AUTH_STATE_DIGEST); - CASE_RANGE(HW_CFG0_DEVICE_ID); - CASE_RANGE(HW_CFG0_MANUF_STATE); - CASE_WIDE(HW_CFG0_DIGEST); - CASE_BYTE(HW_CFG1_EN_SRAM_IFETCH); - CASE_BYTE(HW_CFG1_EN_CSRNG_SW_APP_READ); - CASE_BYTE(HW_CFG1_DIS_RV_DM_LATE_DEBUG); - CASE_WIDE(HW_CFG1_DIGEST); - CASE_RANGE(SECRET0_TEST_UNLOCK_TOKEN); - CASE_RANGE(SECRET0_TEST_EXIT_TOKEN); - CASE_WIDE(SECRET0_DIGEST); - CASE_RANGE(SECRET1_FLASH_ADDR_KEY_SEED); - CASE_RANGE(SECRET1_FLASH_DATA_KEY_SEED); - CASE_RANGE(SECRET1_SRAM_DATA_KEY_SEED); - CASE_WIDE(SECRET1_DIGEST); - CASE_RANGE(SECRET2_RMA_TOKEN); - CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE0); - CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE1); - CASE_WIDE(SECRET2_DIGEST); - CASE_RANGE(LC_TRANSITION_CNT); - CASE_RANGE(LC_STATE); - default: - return ""; - } - -#undef CASE_BYTE -#undef CASE_SUB -#undef CASE_REG -#undef CASE_RANGE -#undef CASE_DIGEST -} - -static MemTxResult ot_otp_eg_swcfg_read_with_attrs( - void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) -{ - OtOTPEgState *s = OT_OTP_EG(opaque); - (void)attrs; - - g_assert(addr + size <= SW_CFG_WINDOW_SIZE); - - hwaddr reg = R32_OFF(addr); - int partition = ot_otp_eg_get_part_from_address(s, addr); - - if (partition < 0) { - trace_ot_otp_access_error_on(s->ot_id, partition, addr, "invalid"); - *data = 0ull; - - return MEMTX_DECODE_ERROR; - } - - unsigned part_ix = (unsigned)partition; - - if (ot_otp_eg_is_buffered(part_ix)) { - trace_ot_otp_access_error_on(s->ot_id, partition, addr, "buffered"); - ot_otp_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); - - /* real HW seems to stall the Tile Link bus in this case */ - return MEMTX_ACCESS_ERROR; - } - - bool is_readable = ot_otp_eg_is_readable(s, part_ix); - bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, addr); - - if (!is_readable && !is_digest) { - trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); - ot_otp_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); - - return MEMTX_DECODE_ERROR; - } - - uint32_t val32 = s->otp->data[reg]; - ot_otp_eg_set_error(s, part_ix, OTP_NO_ERROR); - - uint64_t pc; - - pc = ibex_get_current_pc(); - trace_ot_otp_io_swcfg_read_out(s->ot_id, (uint32_t)addr, - ot_otp_eg_swcfg_reg_name(addr), val32, pc); - - *data = (uint64_t)val32; - - return MEMTX_OK; -} - -static void ot_otp_eg_get_lc_info( - const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, - uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) -{ - const OtOTPEgState *es = OT_OTP_EG(s); - const OtOTPStorage *otp = es->otp; - - if (lc_tcount) { - memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], - LC_TRANSITION_CNT_SIZE); - } - - if (lc_state) { - memcpy(lc_state, &otp->data[R_LC_STATE], LC_STATE_SIZE); - } - - if (lc_valid) { - *lc_valid = !(es->partctrls[OTP_PART_SECRET0].failed || - es->partctrls[OTP_PART_SECRET2].failed || - es->partctrls[OTP_PART_LIFE_CYCLE].failed) ? - OT_MULTIBITBOOL_LC4_TRUE : - OT_MULTIBITBOOL_LC4_FALSE; - } - if (secret_valid) { - *secret_valid = (!es->partctrls[OTP_PART_SECRET2].failed && - es->partctrls[OTP_PART_SECRET2].locked) ? - OT_MULTIBITBOOL_LC4_TRUE : - OT_MULTIBITBOOL_LC4_FALSE; - } - if (tokens) { - *tokens = es->tokens; - } -} - -static const OtOTPHWCfg *ot_otp_eg_get_hw_cfg(const OtOTPState *s) -{ - const OtOTPEgState *es = OT_OTP_EG(s); - - return (const OtOTPHWCfg *)es->hw_cfg; -} - -static void ot_otp_eg_request_entropy_bh(void *opaque) -{ - OtOTPEgState *s = opaque; - - /* - * Use a BH as entropy should be filled in as soon as possible after reset. - * However, as the EDN / OTP reset order is unknown, this initial request - * can only be performed once the reset sequence is over. - */ - if (!s->keygen->edn_sched) { - int rc = ot_edn_request_entropy(s->edn, s->edn_ep); - g_assert(rc == 0); - s->keygen->edn_sched = true; - } -} - -static void -ot_otp_eg_keygen_push_entropy(void *opaque, uint32_t bits, bool fips) -{ - OtOTPEgState *s = opaque; - (void)fips; - - s->keygen->edn_sched = false; - - if (!ot_fifo32_is_full(&s->keygen->entropy_buf)) { - ot_fifo32_push(&s->keygen->entropy_buf, bits); - } - - bool resched = !ot_fifo32_is_full(&s->keygen->entropy_buf); - - trace_ot_otp_keygen_entropy(s->ot_id, - ot_fifo32_num_used(&s->keygen->entropy_buf), - resched); - - if (resched && !s->keygen->edn_sched) { - qemu_bh_schedule(s->keygen->entropy_bh); - } -} - -static void ot_otp_eg_fake_entropy(OtOTPEgState *s, unsigned count) -{ - /* - * This part departs from real HW: OTP needs to have bufferized enough - * entropy for any SRAM OTP key request to be successfully completed. - * On real HW, entropy is requested on demand, but in QEMU this very API - * (#get_otp_key) needs to be synchronous, as it should be able to complete - * on SRAM controller I/O request, which is itself fully synchronous. - * When not enough entropy has been initiatially collected, this function - * adds some fake entropy to entropy buffer. The main use case is to enable - * SRAM initialization with random values and does not need to be truly - * secure, while limiting emulation code size and complexity. - */ - - OtOTPKeyGen *kgen = s->keygen; - while (count-- && !ot_fifo32_is_full(&kgen->entropy_buf)) { - ot_fifo32_push(&kgen->entropy_buf, ot_prng_random_u32(kgen->prng)); - } -} - -/* - * See - * https://opentitan.org/book/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/ - * theory_of_operation.html#scrambling-datapath - * - * The `fetch_nonce_entropy` field refers to the fetching of additional - * entropy for the nonce output. - * - * The `ingest_entropy` field indicates whether an additional 128 bit entropy - * block should be ingested after the seed. That is, `true` will - * derive an ephemeral scrambling key (path C) and `false` will derive a static - * scrambling key (path D). - * - * Will fake entropy if there is not enough available, rather than waiting. - */ -static void ot_otp_eg_generate_scrambling_key( - OtOTPEgState *s, OtOTPKey *key, OtOTPKeyType type, hwaddr key_reg, - uint64_t k_iv, const uint8_t *k_const, bool fetch_nonce_entropy, - bool ingest_entropy) -{ - g_assert(key->seed_size < OT_OTP_SEED_MAX_SIZE); - g_assert(key->nonce_size < OT_OTP_NONCE_MAX_SIZE); - - g_assert(key->seed_size % sizeof(uint32_t) == 0u); - g_assert(key->nonce_size % sizeof(uint32_t) == 0u); - unsigned seed_words = key->seed_size / sizeof(uint32_t); - unsigned nonce_words = key->nonce_size / sizeof(uint32_t); - unsigned scramble_blocks = key->seed_size / sizeof(uint64_t); - - OtFifo32 *entropy = &s->keygen->entropy_buf; - - /* for QEMU emulation, fake entropy instead of waiting */ - unsigned avail_entropy = ot_fifo32_num_used(entropy); - unsigned needed_entropy = 0u; - needed_entropy += fetch_nonce_entropy ? nonce_words : 0u; - needed_entropy += ingest_entropy ? (seed_words * scramble_blocks) : 0u; - if (avail_entropy < needed_entropy) { - unsigned count = needed_entropy - avail_entropy; - error_report("%s: %s: not enough entropy for key %d, fake %u words", - __func__, s->ot_id, type, count); - ot_otp_eg_fake_entropy(s, count); - } - - if (fetch_nonce_entropy) { - /* fill in the nonce using entropy */ - g_assert(ot_fifo32_num_used(entropy) >= nonce_words); - for (unsigned ix = 0; ix < nonce_words; ix++) { - stl_le_p(&key->nonce[ix * sizeof(uint32_t)], - ot_fifo32_pop(entropy)); - } - } - - OtPresentState *ps = s->keygen->present; - - /* read the key seed from the OTP SECRET1 partition */ - OtOTPPartController *pctrl = &s->partctrls[OTP_PART_SECRET1]; - g_assert(ot_otp_eg_is_buffered(OTP_PART_SECRET1)); - uint32_t poffset = - OtOTPPartDescs[OTP_PART_SECRET1].offset / sizeof(uint32_t); - const uint32_t *key_seed = &pctrl->buffer.data[key_reg - poffset]; - - /* check the key seed's validity */ - key->seed_valid = pctrl->locked && !pctrl->failed; - - uint32_t *ephemeral_entropy = g_new0(uint32_t, seed_words); - for (unsigned rix = 0; rix < scramble_blocks; rix++) { - /* compress the IV state with the OTP key seed */ - uint64_t data = k_iv; - ot_present_init(ps, (const uint8_t *)key_seed); - ot_present_encrypt(ps, data, &data); - - if (ingest_entropy) { - /* ephemeral keys ingest different entropy each round */ - g_assert(ot_fifo32_num_used(entropy) >= seed_words); - for (unsigned ix = 0; ix < seed_words; ix++) { - ephemeral_entropy[ix] = ot_fifo32_pop(entropy); - } - - ot_present_init(ps, (uint8_t *)&ephemeral_entropy[0]); - ot_present_encrypt(ps, data, &data); - } - - /* compress with the finalization constant*/ - ot_present_init(ps, k_const); - ot_present_encrypt(ps, data, &data); - - /* write back to the key */ - for (unsigned ix = 0; ix < sizeof(uint64_t); ix++) { - unsigned seed_byte = rix * sizeof(uint64_t) + ix; - key->seed[seed_byte] = (uint8_t)(data >> (ix * 8u)); - } - } - g_free(ephemeral_entropy); - - trace_ot_otp_key_generated(s->ot_id, type); - - if (needed_entropy) { - /* some entropy bits have been used, refill the buffer */ - qemu_bh_schedule(s->keygen->entropy_bh); - } -} - -static void ot_otp_eg_get_otp_key(OtOTPState *s, OtOTPKeyType type, - OtOTPKey *key) -{ - OtOTPEgState *es = OT_OTP_EG(s); - - hwaddr key_offset; - - trace_ot_otp_get_otp_key(es->ot_id, type); - - /* reference: req_bundles in OpenTitan rtl/otp_ctrl_kdi.sv */ - switch (type) { - case OTP_KEY_FLASH_DATA: - memcpy(key->seed, es->scrmbl_key_init->key, FLASH_KEY_BYTES); - memcpy(key->nonce, es->scrmbl_key_init->nonce, FLASH_NONCE_BYTES); - key->seed_size = FLASH_KEY_BYTES; - key->nonce_size = FLASH_NONCE_BYTES; - key->seed_valid = false; - key_offset = R_SECRET1_FLASH_DATA_KEY_SEED; - ot_otp_eg_generate_scrambling_key(es, key, type, key_offset, - es->flash_data_iv, - es->flash_data_const, true, false); - break; - case OTP_KEY_FLASH_ADDR: - memcpy(key->seed, es->scrmbl_key_init->key, FLASH_KEY_BYTES); - key->seed_size = FLASH_KEY_BYTES; - key->nonce_size = 0u; /* FLASH_ADDR_KEY has nonce_size = 0 */ - key->seed_valid = false; - key_offset = R_SECRET1_FLASH_ADDR_KEY_SEED; - ot_otp_eg_generate_scrambling_key(es, key, type, key_offset, - es->flash_addr_iv, - es->flash_addr_const, true, false); - break; - case OTP_KEY_OTBN: - memcpy(key->seed, es->scrmbl_key_init->key, OTBN_KEY_BYTES); - memcpy(key->nonce, es->scrmbl_key_init->nonce, OTBN_NONCE_BYTES); - key->seed_size = OTBN_KEY_BYTES; - key->nonce_size = OTBN_NONCE_BYTES; - key->seed_valid = false; - /* The OTBN scrambling key is derived from the SRAM scrambling key */ - key_offset = R_SECRET1_SRAM_DATA_KEY_SEED; - ot_otp_eg_generate_scrambling_key(es, key, type, key_offset, - es->sram_iv, es->sram_const, true, - true); - break; - case OTP_KEY_SRAM: - memcpy(key->seed, es->scrmbl_key_init->key, SRAM_KEY_BYTES); - memcpy(key->nonce, es->scrmbl_key_init->nonce, SRAM_NONCE_BYTES); - key->seed_size = SRAM_KEY_BYTES; - key->nonce_size = SRAM_NONCE_BYTES; - key->seed_valid = false; - key_offset = R_SECRET1_SRAM_DATA_KEY_SEED; - ot_otp_eg_generate_scrambling_key(es, key, type, key_offset, - es->sram_iv, es->sram_const, true, - true); - break; - default: - error_report("%s: %s: invalid OTP key type: %d", __func__, es->ot_id, - type); - break; - } -} - -static void ot_otp_eg_get_keymgr_secret( - OtOTPState *s, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) -{ - OtOTPEgState *es = OT_OTP_EG(s); - int partition; - size_t offset; - - switch (type) { - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - - OtOTPPartDescs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - - OtOTPPartDescs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_SEED: - case OTP_KEYMGR_SECRET_OWNER_SEED: - default: - error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, - es->ot_id, type); - secret->valid = false; - memset(secret->secret, 0, OT_OTP_KEYMGR_SECRET_SIZE); - return; - } - - unsigned part_ix = (unsigned)partition; - g_assert(ot_otp_eg_is_buffered(part_ix)); - - const uint8_t *data_ptr; - if (es->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { - data_ptr = (const uint8_t *)es->partctrls[part_ix].buffer.data; - } else { - /* source data from PartInvDefault instead of real buffer */ - data_ptr = es->inv_default_parts[part_ix]; - } - - secret->valid = es->partctrls[part_ix].digest != 0; - memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); -} - -static bool ot_otp_eg_program_req(OtOTPState *s, const uint16_t *lc_tcount, - const uint16_t *lc_state, - ot_otp_program_ack_fn ack, void *opaque) -{ - OtOTPEgState *es = OT_OTP_EG(s); - OtOTPLCIController *lci = es->lci; - - switch (lci->state) { - case OTP_LCI_IDLE: - case OTP_LCI_ERROR: - /* error case is handled asynchronously */ - g_assert(!(lci->ack_fn || lci->ack_data)); - break; - case OTP_LCI_WRITE: - case OTP_LCI_WRITE_WAIT: - /* another LC programming request is on-going */ - return false; - case OTP_LCI_RESET: - /* cannot reach this point if PwrMgr init has been executed */ - default: - g_assert_not_reached(); - break; - } - - lci->ack_fn = ack; - lci->ack_data = opaque; - - if (lci->state == OTP_LCI_IDLE) { - unsigned hpos = 0; - memcpy(&lci->data[hpos], lc_tcount, LC_TRANSITION_CNT_SIZE); - hpos += LC_TRANSITION_CNT_SIZE / sizeof(uint16_t); - memcpy(&lci->data[hpos], lc_state, LC_STATE_SIZE); - hpos += LC_STATE_SIZE / sizeof(uint16_t); - g_assert(hpos == - OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size / sizeof(uint16_t)); - - /* current position in LC buffer to write to backend */ - lci->hpos = 0u; - } - - /* - * schedule even if LCI FSM is already in error to report the issue - * asynchronously - */ - timer_mod(lci->prog_delay, - qemu_clock_get_ns(OT_OTP_HW_CLOCK) + LCI_PROG_SCHED_NS); - - return true; -} - -static void ot_otp_eg_lci_write_complete(OtOTPEgState *s, bool success) -{ - OtOTPLCIController *lci = s->lci; - - if (lci->hpos) { - /* - * if the LC partition has been modified somehow, even if the request - * has failed, update the backend file - */ - const OtOTPPartDesc *lcdesc = &OtOTPPartDescs[OTP_PART_LIFE_CYCLE]; - unsigned lc_off = lcdesc->offset / sizeof(uint32_t); - uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, &s->otp->data[lc_off], - (unsigned)(offset + lcdesc->offset), - lcdesc->size)) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - } - } - if (ot_otp_eg_is_ecc_enabled(s)) { - offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_eg_write_backend(s, &((uint16_t *)s->otp->ecc)[lc_off], - (unsigned)(offset + - (lcdesc->offset >> 1u)), - lcdesc->size >> 1u)) { - error_report("%s: %s: cannot update OTP backend", __func__, - s->ot_id); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - } - } - } - } - - g_assert(lci->ack_fn); - ot_otp_program_ack_fn ack_fn = lci->ack_fn; - void *ack_data = lci->ack_data; - lci->ack_fn = NULL; - lci->ack_data = NULL; - lci->hpos = 0u; - - if (!success && lci->error != OTP_NO_ERROR) { - ot_otp_eg_set_error(s, OTP_PART_LIFE_CYCLE, lci->error); - } - - (*ack_fn)(ack_data, success); -} - -static void ot_otp_eg_lci_write_word(void *opaque) -{ - OtOTPEgState *s = OT_OTP_EG(opaque); - OtOTPLCIController *lci = s->lci; - const OtOTPPartDesc *lcdesc = &OtOTPPartDescs[OTP_PART_LIFE_CYCLE]; - - /* should not be called if already in error */ - if (lci->state == OTP_LCI_ERROR) { - lci->error = OTP_FSM_STATE_ERROR; - ot_otp_eg_lci_write_complete(s, false); - return; - } - - if (!ot_otp_eg_is_backend_writable(s)) { - /* OTP backend missing or read-only; reject any write request */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: OTP backend file is missing or R/O\n", __func__, - s->ot_id); - lci->error = OTP_MACRO_ERROR; - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - ot_otp_eg_lci_write_complete(s, false); - /* abort immediately */ - return; - } - - if (lci->hpos >= lcdesc->size / sizeof(uint16_t)) { - /* the whole LC partition has been updated */ - if (lci->error == OTP_NO_ERROR) { - LCI_CHANGE_STATE(s, OTP_LCI_IDLE); - ot_otp_eg_lci_write_complete(s, true); - } else { - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - ot_otp_eg_lci_write_complete(s, false); - } - return; - } - - LCI_CHANGE_STATE(s, OTP_LCI_WRITE); - - uint16_t *lc_dst = - (uint16_t *)&s->otp->data[lcdesc->offset / sizeof(uint32_t)]; - - uint16_t cur_val = lc_dst[lci->hpos]; - uint16_t new_val = lci->data[lci->hpos]; - - trace_ot_otp_lci_write(s->ot_id, lci->hpos, cur_val, new_val); - - if (cur_val & ~new_val) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP bits @ %u: 0x%04x / 0x%04x\n", - __func__, s->ot_id, lci->hpos, cur_val, new_val); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_WRITE_BLANK_ERROR; - } - /* - * "Note that if errors occur, we aggregate the error code but still - * attempt to program all remaining words. This is done to ensure that - * a life cycle state with ECC correctable errors in some words can - * still be scrapped." - */ - } - - lc_dst[lci->hpos] |= new_val; - - if (ot_otp_eg_is_ecc_enabled(s)) { - uint8_t *lc_edst = - (uint8_t *)&s->otp->ecc[lcdesc->offset / (2u * sizeof(uint32_t))]; - uint8_t cur_ecc = lc_edst[lci->hpos]; - uint8_t new_ecc = ot_otp_eg_compute_ecc_u16(lc_dst[lci->hpos]); - - trace_ot_otp_lci_write_ecc(s->ot_id, lci->hpos, cur_ecc, new_ecc); - - if (cur_ecc & ~new_ecc) { - qemu_log_mask( - LOG_GUEST_ERROR, - "%s: %s: cannot clear OTP ECC @ %u: 0x%02x / 0x%02x\n", - __func__, s->ot_id, lci->hpos, cur_ecc, new_ecc); - if (lci->error == OTP_NO_ERROR) { - lci->error = OTP_MACRO_WRITE_BLANK_ERROR; - } - } - - lc_edst[lci->hpos] |= new_ecc; - } - - lci->hpos += 1u; - - unsigned update_time = s->be_chars.timings.write_ns * sizeof(uint16_t); - timer_mod(lci->prog_delay, - qemu_clock_get_ns(OT_OTP_HW_CLOCK) + update_time); - - LCI_CHANGE_STATE(s, OTP_LCI_WRITE_WAIT); -} - -static void ot_otp_eg_pwr_otp_req(void *opaque, int n, int level) -{ - OtOTPEgState *s = opaque; - - g_assert(n == 0); - - if (level) { - trace_ot_otp_pwr_otp_req(s->ot_id, "signaled"); - qemu_bh_schedule(s->pwr_otp_bh); - } -} - -static void ot_otp_eg_pwr_load(OtOTPEgState *s) -{ - /* - * HEADER_FORMAT - * - * | magic | 4 char | "vOFTP" | - * | hlength | uint32_t | count of header bytes after this point | - * | version | uint32_t | version of the header (v2) | - * | eccbits | uint16_t | ECC size in bits | - * | eccgran | uint16_t | ECC granule | - * | dlength | uint32_t | count of data bytes (% uint64_t) | - * | elength | uint32_t | count of ecc bytes (% uint64_t) | - * | -------- | ---------- | only in V2 | - * | dig_iv | 8 uint8_t | Present digest initialization vector | - * | dig_iv | 16 uint8_t | Present digest initialization vector | - */ - - struct otp_header { - char magic[4]; - uint32_t hlength; - uint32_t version; - uint16_t eccbits; - uint16_t eccgran; - uint32_t data_len; - uint32_t ecc_len; - /* added in V2 */ - uint8_t digest_iv[8u]; - uint8_t digest_constant[16u]; - }; - - static_assert(sizeof(struct otp_header) == 48u, "Invalid header size"); - - /* data following header should always be 64-bit aligned */ - static_assert((sizeof(struct otp_header) % sizeof(uint64_t)) == 0, - "invalid header definition"); - - size_t header_size = sizeof(struct otp_header); - size_t data_size = 0u; - size_t ecc_size = 0u; - - for (unsigned ix = 0u; ix < OTP_PART_COUNT; ix++) { - size_t psize = (size_t)OtOTPPartDescs[ix].size; - size_t dsize = ROUND_UP(psize, sizeof(uint64_t)); - data_size += dsize; - /* up to 1 ECC byte for 2 data bytes */ - ecc_size += DIV_ROUND_UP(dsize, 2u); - } - size_t otp_size = header_size + data_size + ecc_size; - - otp_size = ROUND_UP(otp_size, 4096u); - - OtOTPStorage *otp = s->otp; - - /* always allocates the requested size even if blk is NULL */ - if (!otp->storage) { - /* only allocated once on PoR */ - otp->storage = blk_blockalign(s->blk, otp_size); - } - - uintptr_t base = (uintptr_t)otp->storage; - g_assert(!(base & (sizeof(uint64_t) - 1u))); - - memset(otp->storage, 0, otp_size); - - otp->data = (uint32_t *)(base + sizeof(struct otp_header)); - otp->ecc = (uint32_t *)(base + sizeof(struct otp_header) + data_size); - otp->ecc_bit_count = 0u; - otp->ecc_granule = 0u; - - if (s->blk) { - int rc = blk_pread(s->blk, 0, (int64_t)otp_size, otp->storage, 0); - if (rc < 0) { - error_setg(&error_fatal, - "%s: failed to read the initial OTP content %zu bytes: " - "%d", - s->ot_id, otp_size, rc); - return; - } - - const struct otp_header *otp_hdr = (const struct otp_header *)base; - - if (memcmp(otp_hdr->magic, "vOTP", sizeof(otp_hdr->magic)) != 0) { - error_setg(&error_fatal, "%s: OTP file is not a valid OTP backend", - s->ot_id); - return; - } - if (otp_hdr->version != 1u && otp_hdr->version != 2u) { - error_setg(&error_fatal, "%s: OTP file version %u is not supported", - s->ot_id, otp_hdr->version); - return; - } - - uintptr_t data_offset = otp_hdr->hlength + 8u; /* magic & length */ - uintptr_t ecc_offset = data_offset + otp_hdr->data_len; - - otp->data = (uint32_t *)(base + data_offset); - otp->ecc = (uint32_t *)(base + ecc_offset); - otp->ecc_bit_count = otp_hdr->eccbits; - otp->ecc_granule = otp_hdr->eccgran; - - if (otp->ecc_bit_count != 6u || !ot_otp_eg_is_ecc_enabled(s)) { - qemu_log_mask(LOG_UNIMP, - "%s: %s: support for ECC %u/%u not implemented\n", - __func__, s->ot_id, otp->ecc_granule, - otp->ecc_bit_count); - } - - bool write = blk_supports_write_perm(s->blk); - trace_ot_otp_load_backend(s->ot_id, otp_hdr->version, - write ? "R/W" : "R/O", otp->ecc_bit_count, - otp->ecc_granule); - - if (otp_hdr->version == 2u) { - /* - * Version 2 is deprecated and digest const/IV are now ignored. - * Nonetheless, keep checking for inconsistencies. - */ - if (s->digest_iv != ldq_le_p(otp_hdr->digest_iv)) { - error_report("%s: %s: OTP file digest IV mismatch", __func__, - s->ot_id); - } - if (memcmp(s->digest_const, otp_hdr->digest_constant, - sizeof(s->digest_const)) != 0) { - error_report("%s: %s: OTP file digest const mismatch", __func__, - s->ot_id); - } - } - } - - otp->data_size = data_size; - otp->ecc_size = ecc_size; - otp->size = otp_size; -} - -static void ot_otp_eg_pwr_load_hw_cfg(OtOTPEgState *s) -{ - OtOTPStorage *otp = s->otp; - OtOTPHWCfg *hw_cfg = s->hw_cfg; - - memcpy(hw_cfg->device_id, &otp->data[R_HW_CFG0_DEVICE_ID], - sizeof(hw_cfg->device_id)); - memcpy(hw_cfg->manuf_state, &otp->data[R_HW_CFG0_MANUF_STATE], - sizeof(hw_cfg->manuf_state)); - /* do not prevent execution from SRAM if no OTP configuration is loaded */ - hw_cfg->en_sram_ifetch_mb8 = - s->blk ? (uint8_t)otp->data[R_HW_CFG1_EN_SRAM_IFETCH] : - OT_MULTIBITBOOL8_TRUE; - /* do not prevent CSRNG app reads if no OTP configuration is loaded */ - hw_cfg->en_csrng_sw_app_read_mb8 = - s->blk ? (uint8_t)otp->data[R_HW_CFG1_EN_CSRNG_SW_APP_READ] : - OT_MULTIBITBOOL8_TRUE; -} - -static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) -{ - memset(s->tokens, 0, sizeof(*s->tokens)); - - OtOTPTokens *tokens = s->tokens; - - static_assert(sizeof(OtOTPTokenValue) == 16u, "Invalid token size"); - - for (unsigned tkx = 0; tkx < OTP_TOKEN_COUNT; tkx++) { - unsigned partition; - uint32_t secret_addr; - - switch (tkx) { - case OTP_TOKEN_TEST_UNLOCK: - partition = OTP_PART_SECRET0; - secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; - break; - case OTP_TOKEN_TEST_EXIT: - partition = OTP_PART_SECRET0; - secret_addr = A_SECRET0_TEST_EXIT_TOKEN; - break; - case OTP_TOKEN_RMA: - partition = OTP_PART_SECRET2; - secret_addr = A_SECRET2_RMA_TOKEN; - break; - default: - g_assert_not_reached(); - break; - } - - OtOTPPartController *pctrl = &s->partctrls[partition]; - g_assert(pctrl->buffer.data != NULL); - - /* byte offset of the secret within the partition */ - unsigned secret_offset = - secret_addr - ot_otp_eg_part_data_offset(partition); - g_assert(secret_offset + sizeof(OtOTPTokenValue) <= - OtOTPPartDescs[partition].size); - - OtOTPTokenValue value; - memcpy(&value, &pctrl->buffer.data[secret_offset / sizeof(uint32_t)], - sizeof(OtOTPTokenValue)); - - if (s->partctrls[partition].locked) { - tokens->values[tkx] = value; - tokens->valid_bm |= 1u << tkx; + switch (reg) { + case R_INTR_STATE: + val32 &= INTR_WMASK; + s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ + c->update_irqs(s); + break; + case R_INTR_ENABLE: + val32 &= INTR_WMASK; + s->regs[R_INTR_ENABLE] = val32; + c->update_irqs(s); + break; + case R_INTR_TEST: + val32 &= INTR_WMASK; + s->regs[R_INTR_STATE] = val32; + c->update_irqs(s); + break; + case R_ALERT_TEST: + val32 &= ALERT_WMASK; + s->regs[reg] = val32; + c->update_alerts(s); + break; + case R_DIRECT_ACCESS_REGWEN: + val32 &= R_DIRECT_ACCESS_REGWEN_REGWEN_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_DIRECT_ACCESS_CMD: + if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, RD)) { + c->dai_read(s); + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, WR)) { + c->dai_write(s); + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, DIGEST)) { + c->dai_digest(s); } - trace_ot_otp_load_token(s->ot_id, OTP_TOKEN_NAME(tkx), tkx, value.hi, - value.lo, - (s->tokens->valid_bm & (1u << tkx)) ? "" : - "in"); + break; + case R_DIRECT_ACCESS_ADDRESS: + val32 &= R_DIRECT_ACCESS_ADDRESS_ADDRESS_MASK; + s->regs[reg] = val32; + break; + case R_DIRECT_ACCESS_WDATA_0: + case R_DIRECT_ACCESS_WDATA_1: + s->regs[reg] = val32; + break; + case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + val32 &= OT_OTP_READ_LOCK_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_CHECK_TRIGGER_REGWEN: + val32 &= R_CHECK_TRIGGER_REGWEN_REGWEN_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_CHECK_REGWEN: + val32 &= R_CHECK_REGWEN_REGWEN_MASK; + s->regs[reg] &= val32; /* RW0C */ + break; + case R_CHECK_TRIGGER: + case R_CHECK_TIMEOUT: + case R_INTEGRITY_CHECK_PERIOD: + case R_CONSISTENCY_CHECK_PERIOD: + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(reg)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); + break; } } -static void ot_otp_eg_pwr_initialize_partitions(OtOTPEgState *s) +static const char *ot_otp_eg_swcfg_reg_name(unsigned swreg) { - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - /* sanity check: all secret partitions are also buffered */ - g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); - - if (ot_otp_eg_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { - if (ot_otp_eg_apply_ecc(s, ix)) { - continue; - } - } - - if (OtOTPPartDescs[ix].sw_digest) { - s->partctrls[ix].digest = ot_otp_eg_get_part_digest(s, ix); - s->partctrls[ix].locked = s->partctrls[ix].digest != 0; - continue; - } +#define CASE_BYTE(_reg_) \ + case A_##_reg_: \ + return stringify(_reg_) +#define CASE_SUB(_reg_, _sz_) \ + case A_##_reg_...(A_##_reg_ + (_sz_) - 1u): \ + return stringify(_reg_) +#define CASE_REG(_reg_) \ + case A_##_reg_...(A_##_reg_ + 3u): \ + return stringify(_reg_) +#define CASE_WIDE(_reg_) \ + case A_##_reg_...(A_##_reg_ + 7u): \ + return stringify(_reg_) +#define CASE_RANGE(_reg_) \ + case A_##_reg_...(A_##_reg_ + (_reg_##_SIZE) - 1u): \ + return stringify(_reg_) - if (OtOTPPartDescs[ix].buffered) { - ot_otp_eg_bufferize_partition(s, ix); - if (OtOTPPartDescs[ix].hw_digest) { - ot_otp_eg_check_buffered_partition_integrity(s, ix); - } - continue; - } + switch (swreg) { + CASE_RANGE(VENDOR_TEST_SCRATCH); + CASE_WIDE(VENDOR_TEST_DIGEST); + CASE_RANGE(CREATOR_SW_CFG_AST_CFG); + CASE_REG(CREATOR_SW_CFG_AST_INIT_EN); + CASE_REG(CREATOR_SW_CFG_ROM_EXT_SKU); + CASE_REG(CREATOR_SW_CFG_SIGVERIFY_SPX_EN); + CASE_REG(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG); + CASE_REG(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG); + CASE_REG(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE); + CASE_REG(CREATOR_SW_CFG_RNG_EN); + CASE_REG(CREATOR_SW_CFG_JITTER_EN); + CASE_REG(CREATOR_SW_CFG_RET_RAM_RESET_MASK); + CASE_REG(CREATOR_SW_CFG_MANUF_STATE); + CASE_REG(CREATOR_SW_CFG_ROM_EXEC_EN); + CASE_REG(CREATOR_SW_CFG_CPUCTRL); + CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT); + CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_BL0); + CASE_REG(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN); + CASE_REG(CREATOR_SW_CFG_RMA_SPIN_EN); + CASE_REG(CREATOR_SW_CFG_RMA_SPIN_CYCLES); + CASE_REG(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD); + CASE_REG(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST); + CASE_REG(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_EN); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_START_OFFSET); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_LENGTH); + CASE_RANGE(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_SHA256_HASH); + CASE_RANGE(CREATOR_SW_CFG_RESERVED); + CASE_WIDE(CREATOR_SW_CFG_DIGEST); + CASE_REG(OWNER_SW_CFG_ROM_ERROR_REPORTING); + CASE_REG(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_CLASS_EN); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_ESCALATION); + CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION); + CASE_RANGE(OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION); + CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH); + CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES); + CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA); + CASE_REG(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES); + CASE_REG(OWNER_SW_CFG_ROM_KEYMGR_OTP_MEAS_EN); + CASE_REG(OWNER_SW_CFG_MANUF_STATE); + CASE_REG(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN); + CASE_REG(OWNER_SW_CFG_ROM_EXT_BOOTSTRAP_EN); + CASE_RANGE(OWNER_SW_CFG_ROM_SENSOR_CTRL_ALERT_CFG); + CASE_REG(OWNER_SW_CFG_ROM_SRAM_READBACK_EN); + CASE_REG(OWNER_SW_CFG_ROM_PRESERVE_RESET_REASON_EN); + CASE_REG(OWNER_SW_CFG_ROM_RESET_REASON_CHECK_VALUE); + CASE_REG(OWNER_SW_CFG_ROM_BANNER_EN); + CASE_REG(OWNER_SW_CFG_ROM_FLASH_ECC_EXC_HANDLER_EN); + CASE_RANGE(OWNER_SW_CFG_RESERVED); + CASE_WIDE(OWNER_SW_CFG_DIGEST); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE0); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY0); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE1); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE2); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE3); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY3); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE0); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY0); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG0); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE1); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE2); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE3); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY3); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG3); + CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_BLOCK_SHA2_256_HASH); + CASE_WIDE(ROT_CREATOR_AUTH_CODESIGN_DIGEST); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY0); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY1); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY2); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY3); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY0); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY1); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY2); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY3); + CASE_WIDE(ROT_CREATOR_AUTH_STATE_DIGEST); + CASE_RANGE(HW_CFG0_DEVICE_ID); + CASE_RANGE(HW_CFG0_MANUF_STATE); + CASE_WIDE(HW_CFG0_DIGEST); + CASE_BYTE(HW_CFG1_EN_SRAM_IFETCH); + CASE_BYTE(HW_CFG1_EN_CSRNG_SW_APP_READ); + CASE_BYTE(HW_CFG1_DIS_RV_DM_LATE_DEBUG); + CASE_WIDE(HW_CFG1_DIGEST); + CASE_RANGE(SECRET0_TEST_UNLOCK_TOKEN); + CASE_RANGE(SECRET0_TEST_EXIT_TOKEN); + CASE_WIDE(SECRET0_DIGEST); + CASE_RANGE(SECRET1_FLASH_ADDR_KEY_SEED); + CASE_RANGE(SECRET1_FLASH_DATA_KEY_SEED); + CASE_RANGE(SECRET1_SRAM_DATA_KEY_SEED); + CASE_WIDE(SECRET1_DIGEST); + CASE_RANGE(SECRET2_RMA_TOKEN); + CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE0); + CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE1); + CASE_WIDE(SECRET2_DIGEST); + CASE_RANGE(LC_TRANSITION_CNT); + CASE_RANGE(LC_STATE); + default: + return ""; } -} - -static void ot_otp_eg_pwr_otp_bh(void *opaque) -{ - OtOTPEgState *s = opaque; - - /* - * This sequence is triggered from the Power Manager, in the early boot - * sequence while the OT IPs are maintained in reset. - * This means that all ot_otp_eg_pwr_* functions are called before the OTP - * IP is released from reset. - * - * The QEMU reset is not a 1:1 mapping to the actual HW. - */ - trace_ot_otp_pwr_otp_req(s->ot_id, "initialize"); - /* load OTP data from OTP back-end file */ - ot_otp_eg_pwr_load(s); - /* check ECC, digests, configure locks and bufferize partitions */ - ot_otp_eg_pwr_initialize_partitions(s); - /* load HW configuration, that is HW "broadcasted signals" */ - ot_otp_eg_pwr_load_hw_cfg(s); - /* load LC controller tokens */ - ot_otp_eg_pwr_load_tokens(s); - - /* initialize direct access interface */ - ot_otp_eg_dai_init(s); - /* initialize LC controller interface */ - ot_otp_eg_lci_init(s); - - trace_ot_otp_pwr_otp_req(s->ot_id, "done"); - - /* toggle OTP completion to signal the power manager OTP init is complete */ - ibex_irq_set(&s->pwc_otp_rsp, 1); - ibex_irq_set(&s->pwc_otp_rsp, 0); +#undef CASE_BYTE +#undef CASE_SUB +#undef CASE_REG +#undef CASE_RANGE +#undef CASE_DIGEST } -static void ot_otp_eg_configure_scrmbl_key(OtOTPEgState *s) +static MemTxResult ot_otp_eg_swcfg_read_with_attrs( + void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - if (!s->scrmbl_key_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "scrmbl_key"); - return; - } + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); + (void)size; + (void)attrs; - size_t len = strlen(s->scrmbl_key_xstr); - if (len != (size_t)(SRAM_KEY_BYTES + SRAM_NONCE_BYTES) * 2u) { - error_setg(&error_fatal, "%s: %s invalid scrmbl_key length\n", __func__, - s->ot_id); - return; - } + hwaddr reg = R32_OFF(addr); + int partition = c->get_part_from_address(s, addr); - if (ot_common_parse_hexa_str(s->scrmbl_key_init->key, - &s->scrmbl_key_xstr[0], SRAM_KEY_BYTES, false, - false)) { - error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", - __func__, s->ot_id); - return; - } + if (partition < 0) { + trace_ot_otp_access_error_on(s->ot_id, partition, addr, "invalid"); + *data = 0ull; - if (ot_common_parse_hexa_str(s->scrmbl_key_init->nonce, - &s->scrmbl_key_xstr[SRAM_KEY_BYTES * 2u], - SRAM_NONCE_BYTES, false, true)) { - error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", - __func__, s->ot_id); - return; + return MEMTX_DECODE_ERROR; } -} -static void ot_otp_eg_configure_digest(OtOTPEgState *s) -{ - memset(s->digest_const, 0, sizeof(s->digest_const)); - s->digest_iv = 0ull; + unsigned part_ix = (unsigned)partition; - if (!s->digest_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "digest_const"); - return; - } + if (ot_otp_engine_is_buffered(s, part_ix)) { + trace_ot_otp_access_error_on(s->ot_id, partition, addr, "buffered"); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); - if (!s->digest_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "digest_iv"); - return; + /* real HW seems to stall the Tile Link bus in this case */ + return MEMTX_ACCESS_ERROR; } - size_t len; + bool is_readable = c->is_readable(s, part_ix); + bool is_digest = ot_otp_engine_is_part_digest_offset(s, part_ix, addr); + bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, addr); - len = strlen(s->digest_const_xstr); - if (len != sizeof(s->digest_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid digest_const length\n", - __func__, s->ot_id); - return; - } + if (!is_readable && !(is_digest || is_zer)) { + trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); - if (ot_common_parse_hexa_str(s->digest_const, s->digest_const_xstr, - sizeof(s->digest_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse digest_const\n", - __func__, s->ot_id); - return; + return MEMTX_DECODE_ERROR; } - uint8_t digest_iv[sizeof(uint64_t)]; + uint32_t val32 = s->otp->data[reg]; + c->set_error(s, part_ix, OT_OTP_NO_ERROR); - len = strlen(s->digest_iv_xstr); - if (len != sizeof(digest_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid digest_iv length\n", __func__, - s->ot_id); - return; - } + uint64_t pc; - if (ot_common_parse_hexa_str(digest_iv, s->digest_iv_xstr, - sizeof(digest_iv), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse digest_iv\n", __func__, - s->ot_id); - return; - } + pc = ibex_get_current_pc(); + trace_ot_otp_io_swcfg_read_out(s->ot_id, (uint32_t)addr, + ot_otp_eg_swcfg_reg_name(addr), val32, pc); + + *data = (uint64_t)val32; - s->digest_iv = ldq_le_p(digest_iv); + return MEMTX_OK; } -static void ot_otp_eg_configure_flash(OtOTPEgState *s) +static void ot_otp_eg_get_lc_info( + const OtOTPIf *dev, uint16_t *lc_tcount, uint16_t *lc_state, + uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) { - memset(s->flash_data_const, 0, sizeof(s->flash_data_const)); - memset(s->flash_addr_const, 0, sizeof(s->flash_addr_const)); - s->flash_data_iv = 0ull; - s->flash_addr_iv = 0ull; - - if (!s->flash_data_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "flash_data_const"); - return; - } - if (!s->flash_addr_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "flash_addr_const"); - return; - } - if (!s->flash_data_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "flash_data_iv"); - return; - } - if (!s->flash_addr_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "flash_addr_iv"); - return; - } - - size_t len; - - len = strlen(s->flash_data_const_xstr); - if (len != sizeof(s->flash_data_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid flash_data_const length\n", - __func__, s->ot_id); - return; - } - - if (ot_common_parse_hexa_str(s->flash_data_const, s->flash_data_const_xstr, - sizeof(s->flash_data_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse flash_data_const\n", - __func__, s->ot_id); - return; - } - - len = strlen(s->flash_addr_const_xstr); - if (len != sizeof(s->flash_addr_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid flash_addr_const length\n", - __func__, s->ot_id); - return; - } + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); + const OtOTPStorage *otp = s->otp; - if (ot_common_parse_hexa_str(s->flash_addr_const, s->flash_addr_const_xstr, - sizeof(s->flash_addr_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse flash_addr_const\n", - __func__, s->ot_id); - return; + if (lc_tcount) { + memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], + LC_TRANSITION_CNT_SIZE); } - uint8_t flash_data_iv[sizeof(uint64_t)]; - - len = strlen(s->flash_data_iv_xstr); - if (len != sizeof(flash_data_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid flash_data_iv length\n", - __func__, s->ot_id); - return; + if (lc_state) { + memcpy(lc_state, &otp->data[R_LC_STATE], LC_STATE_SIZE); } - if (ot_common_parse_hexa_str(flash_data_iv, s->flash_data_iv_xstr, - sizeof(flash_data_iv), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse flash_data_iv\n", - __func__, s->ot_id); - return; + if (lc_valid) { + *lc_valid = !(s->part_ctrls[OTP_PART_SECRET0].failed || + s->part_ctrls[OTP_PART_SECRET2].failed || + s->part_ctrls[s->part_lc_num].failed) ? + OT_MULTIBITBOOL_LC4_TRUE : + OT_MULTIBITBOOL_LC4_FALSE; } - - s->flash_data_iv = ldq_le_p(flash_data_iv); - - uint8_t flash_addr_iv[sizeof(uint64_t)]; - - len = strlen(s->flash_addr_iv_xstr); - if (len != sizeof(flash_addr_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid flash_addr_iv length\n", - __func__, s->ot_id); - return; + if (secret_valid) { + *secret_valid = (!s->part_ctrls[OTP_PART_SECRET2].failed && + s->part_ctrls[OTP_PART_SECRET2].locked) ? + OT_MULTIBITBOOL_LC4_TRUE : + OT_MULTIBITBOOL_LC4_FALSE; } - - if (ot_common_parse_hexa_str(flash_addr_iv, s->flash_addr_iv_xstr, - sizeof(flash_addr_iv), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse flash_addr_iv\n", - __func__, s->ot_id); - return; + if (tokens) { + *tokens = s->tokens; } - - s->flash_addr_iv = ldq_le_p(flash_addr_iv); } - -static void ot_otp_eg_configure_sram(OtOTPEgState *s) +static void ot_otp_eg_get_keymgr_secret( + OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) { - memset(s->sram_const, 0, sizeof(s->sram_const)); - s->sram_iv = 0ull; - - if (!s->sram_const_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "sram_const"); - return; - } + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + int partition; + size_t offset; - if (!s->sram_iv_xstr) { - trace_ot_otp_configure_missing(s->ot_id, "sram_iv"); + switch (type) { + case OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: + partition = OTP_PART_SECRET2; + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - s->part_descs[partition].offset; + break; + case OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: + partition = OTP_PART_SECRET2; + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - s->part_descs[partition].offset; + break; + case OT_OTP_KEYMGR_SECRET_CREATOR_SEED: + case OT_OTP_KEYMGR_SECRET_OWNER_SEED: + default: + error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, + s->ot_id, type); + secret->valid = false; + memset(secret->secret, 0, OT_OTP_KEYMGR_SECRET_SIZE); return; } - size_t len; + unsigned part_ix = (unsigned)partition; + g_assert(ot_otp_engine_is_buffered(s, part_ix)); - len = strlen(s->sram_const_xstr); - if (len != sizeof(s->sram_const) * 2u) { - error_setg(&error_fatal, "%s: %s invalid sram_const length\n", __func__, - s->ot_id); - return; + const uint8_t *data_ptr; + if (s->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { + data_ptr = (const uint8_t *)s->part_ctrls[part_ix].buffer.data; + } else { + /* source data from PartInvDefault instead of real buffer */ + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + data_ptr = pctrl->inv_default_data; } - if (ot_common_parse_hexa_str(s->sram_const, s->sram_const_xstr, - sizeof(s->sram_const), true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse sram_const\n", - __func__, s->ot_id); - return; - } + secret->valid = s->part_ctrls[part_ix].digest != 0; + memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); +} - uint8_t sram_iv[sizeof(uint64_t)]; +static void +ot_otp_eg_update_status_error(OtOTPImplIf *dev, OtOTPStatus error, bool set) +{ + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); - len = strlen(s->sram_iv_xstr); - if (len != sizeof(sram_iv) * 2u) { - error_setg(&error_fatal, "%s: %s invalid sram_iv length\n", __func__, - s->ot_id); + uint32_t mask; + switch (error) { + case OT_OTP_STATUS_DAI: + mask = R_STATUS_DAI_ERROR_MASK; + break; + case OT_OTP_STATUS_LCI: + mask = R_STATUS_LCI_ERROR_MASK; + break; + default: + g_assert_not_reached(); return; } - if (ot_common_parse_hexa_str(sram_iv, s->sram_iv_xstr, sizeof(sram_iv), - true, true)) { - error_setg(&error_fatal, "%s: %s unable to parse sram_iv\n", __func__, - s->ot_id); - return; + if (set) { + s->regs[R_STATUS] |= mask; + } else { + s->regs[R_STATUS] &= ~mask; } - - s->sram_iv = ldq_le_p(sram_iv); } -static void ot_otp_eg_configure_part_scramble_keys(OtOTPEgState *s) +static void ot_otp_eg_pwr_load_hw_cfg(OtOTPEngineState *s) { - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { - continue; - } - - if (!s->otp_scramble_key_xstrs[ix]) { - /* if OTP data is loaded, unscrambling keys are mandatory */ - if (s->blk) { - error_setg(&error_fatal, - "%s: %s Missing OTP scrambling key for part %s (%u)", - __func__, s->ot_id, PART_NAME(ix), ix); - return; - } - continue; - } - - size_t len = strlen(s->otp_scramble_key_xstrs[ix]); - if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { - error_setg( - &error_fatal, - "%s: %s Invalid OTP scrambling key length %zu for part %s (%u)", - __func__, s->ot_id, len, PART_NAME(ix), ix); - return; - } + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); - g_assert(!s->otp_scramble_keys[ix]); + const OtOTPPartDesc *pdesc0 = &ic->part_descs[OTP_PART_HW_CFG0]; + const OtOTPPartDesc *pdesc1 = &ic->part_descs[OTP_PART_HW_CFG1]; + const OtOTPPartController *pctrl0 = &s->part_ctrls[OTP_PART_HW_CFG0]; + const OtOTPPartController *pctrl1 = &s->part_ctrls[OTP_PART_HW_CFG1]; + const uint8_t *pdata0 = (const uint8_t *)pctrl0->buffer.data; + const uint8_t *pdata1 = (const uint8_t *)pctrl1->buffer.data; - s->otp_scramble_keys[ix] = g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - if (ot_common_parse_hexa_str(s->otp_scramble_keys[ix], - s->otp_scramble_key_xstrs[ix], - OTP_SCRAMBLING_KEY_BYTES, true, true)) { - error_setg(&error_fatal, - "%s: %s unable to parse otp_scramble_keys[%u] for %s", - __func__, s->ot_id, ix, PART_NAME(ix)); - return; - } + OtOTPHWCfg *hw_cfg = s->hw_cfg; - TRACE_OTP("otp_scramble_keys[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->otp_scramble_keys[ix], - OTP_SCRAMBLING_KEY_BYTES)); - } + memcpy(hw_cfg->device_id, &pdata0[A_HW_CFG0_DEVICE_ID - pdesc0->offset], + sizeof(hw_cfg->device_id)); + memcpy(hw_cfg->manuf_state, &pdata0[A_HW_CFG0_MANUF_STATE - pdesc0->offset], + sizeof(hw_cfg->manuf_state)); + /* do not prevent execution from SRAM if no OTP configuration is loaded */ + hw_cfg->en_sram_ifetch_mb8 = + s->blk ? pdata1[A_HW_CFG1_EN_SRAM_IFETCH - pdesc1->offset] : + OT_MULTIBITBOOL8_TRUE; + /* do not prevent CSRNG app reads if no OTP configuration is loaded */ + hw_cfg->en_csrng_sw_app_read_mb8 = + s->blk ? pdata1[A_HW_CFG1_EN_CSRNG_SW_APP_READ - pdesc1->offset] : + OT_MULTIBITBOOL8_TRUE; } -static void ot_otp_eg_class_add_scramble_key_props(OtOTPClass *odc) +static void ot_otp_eg_pwr_load_tokens(OtOTPEngineState *s) { - unsigned secret_ix = 0u; - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { - continue; - } + memset(s->tokens, 0, sizeof(*s->tokens)); - Property *prop = g_new0(Property, 1u); + OtOTPTokens *tokens = s->tokens; - /* - * Assumes secret partitions are sequentially ordered and named - * SECRET0, SECRET1, SECRET2, etc. - */ - prop->name = g_strdup_printf("secret%u_scramble_key", secret_ix++); - prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPEgState, otp_scramble_key_xstrs) + - sizeof(char *) * ix; + static_assert(sizeof(OtOTPTokenValue) == 16u, "Invalid token size"); - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); - } -} + for (unsigned tkx = 0; tkx < OT_OTP_TOKEN_COUNT; tkx++) { + unsigned partition; + uint32_t secret_addr; -static void ot_otp_eg_configure_inv_default_parts(OtOTPEgState *s) -{ - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!s->inv_default_part_xstrs[ix]) { - continue; + switch (tkx) { + case OT_OTP_TOKEN_TEST_UNLOCK: + partition = (unsigned)OTP_PART_SECRET0; + secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; + break; + case OT_OTP_TOKEN_TEST_EXIT: + partition = (unsigned)OTP_PART_SECRET0; + secret_addr = A_SECRET0_TEST_EXIT_TOKEN; + break; + case OT_OTP_TOKEN_RMA: + partition = (unsigned)OTP_PART_SECRET2; + secret_addr = A_SECRET2_RMA_TOKEN; + break; + default: + g_assert_not_reached(); + break; } - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; - - size_t len; + OtOTPPartController *pctrl = &s->part_ctrls[partition]; + g_assert(pctrl->buffer.data != NULL); - len = strlen(s->inv_default_part_xstrs[ix]); - if (len != part->size * 2u) { - error_setg(&error_fatal, - "%s: %s invalid inv_default_part[%u] length\n", __func__, - s->ot_id, ix); - return; - } + /* byte offset of the secret within the partition */ + unsigned secret_offset = + secret_addr - ot_otp_engine_part_data_offset(s, partition); + g_assert(secret_offset + sizeof(OtOTPTokenValue) <= + OT_OTP_PART_DESCS[partition].size); - g_assert(!s->inv_default_parts[ix]); + OtOTPTokenValue value; + memcpy(&value, &pctrl->buffer.data[secret_offset / sizeof(uint32_t)], + sizeof(OtOTPTokenValue)); - s->inv_default_parts[ix] = g_new0(uint8_t, part->size + 1u); - if (ot_common_parse_hexa_str(s->inv_default_parts[ix], - s->inv_default_part_xstrs[ix], part->size, - false, true)) { - error_setg(&error_fatal, - "%s: %s unable to parse inv_default_part[%u]\n", - __func__, s->ot_id, ix); - return; + if (s->part_ctrls[partition].locked) { + tokens->values[tkx] = value; + tokens->valid_bm |= 1u << tkx; } - - TRACE_OTP("inv_default_part[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->inv_default_parts[ix], part->size)); + trace_ot_otp_load_token(s->ot_id, OTP_TOKEN_NAME(tkx), tkx, value.hi, + value.lo, + (s->tokens->valid_bm & (1u << tkx)) ? "" : + "in"); } } -static void ot_otp_eg_class_add_inv_def_props(OtOTPClass *odc) +static void ot_otp_eg_signal_pwr_sequence(OtOTPImplIf *dev) { - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].buffered) { - continue; - } + OtOTPEngineState *s = OT_OTP_ENGINE(dev); - Property *prop = g_new0(Property, 1u); - - prop->name = g_strdup_printf("inv_default_part_%u", ix); - prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPEgState, inv_default_part_xstrs) + - sizeof(char *) * ix; - - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); - } + ot_otp_eg_pwr_load_hw_cfg(s); + ot_otp_eg_pwr_load_tokens(s); } -static Property ot_otp_eg_properties[] = { - DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOTPEgState, ot_id), - DEFINE_PROP_DRIVE("drive", OtOTPEgState, blk), - DEFINE_PROP_LINK("backend", OtOTPEgState, otp_backend, TYPE_OT_OTP_BE_IF, - OtOtpBeIf *), - DEFINE_PROP_LINK("edn", OtOTPEgState, edn, TYPE_OT_EDN, OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtOTPEgState, edn_ep, UINT8_MAX), - DEFINE_PROP_STRING("scrmbl_key", OtOTPEgState, scrmbl_key_xstr), - DEFINE_PROP_STRING("digest_const", OtOTPEgState, digest_const_xstr), - DEFINE_PROP_STRING("digest_iv", OtOTPEgState, digest_iv_xstr), - DEFINE_PROP_STRING("sram_const", OtOTPEgState, sram_const_xstr), - DEFINE_PROP_STRING("sram_iv", OtOTPEgState, sram_iv_xstr), - DEFINE_PROP_STRING("flash_data_const", OtOTPEgState, flash_data_const_xstr), - DEFINE_PROP_STRING("flash_data_iv", OtOTPEgState, flash_data_iv_xstr), - DEFINE_PROP_STRING("flash_addr_const", OtOTPEgState, flash_addr_const_xstr), - DEFINE_PROP_STRING("flash_addr_iv", OtOTPEgState, flash_addr_iv_xstr), - DEFINE_PROP_BOOL("fatal_escalate", OtOTPEgState, fatal_escalate, false), - DEFINE_PROP_END_OF_LIST(), -}; - static const MemoryRegionOps ot_otp_eg_reg_ops = { .read = &ot_otp_eg_reg_read, .write = &ot_otp_eg_reg_write, @@ -3934,46 +1111,10 @@ static const MemoryRegionOps ot_otp_eg_swcfg_ops = { static void ot_otp_eg_reset_enter(Object *obj, ResetType type) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); - OtOTPEgState *s = OT_OTP_EG(obj); - - /* - * Note: beware of the special reset sequence for the OTP controller, - * see comments from ot_otp_eg_pwr_otp_bh, as this very QEMU reset may be - * called after ot_otp_eg_pwr_otp_bh is invoked, hereby changing the usual - * realize-reset sequence. - * - * File back-end storage (loading) is processed from - * the ot_otp_eg_pwr_otp_bh handler, to ensure data is reloaded from the - * backend on each reset, prior to this very reset function. This reset - * function should not alter the storage content. - * - * Ideally the OTP reset functions should be decoupled from the regular - * IP reset, which are exercised automatically from the SoC, since all the - * OT SysBysDevice IPs are connected to the private system bus of the Ibex. - * This is by-design in QEMU. The reset management is already far too - * complex to create a special case for the OTP. Kind in mind that the OTP - * reset_enter/reset_exit functions are QEMU regular reset functions called - * as part of the private bus reset and do not represent the actual OTP HW - * reset. Part of this reset is handled in the Power Manager handler. - */ - trace_ot_otp_reset(s->ot_id, "enter"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - qemu_bh_cancel(s->dai->digest_bh); - qemu_bh_cancel(s->lc_broadcast.bh); - qemu_bh_cancel(s->pwr_otp_bh); + OtOTPEngineState *s = OT_OTP_ENGINE(obj); + OtOTPEgClass *dc = OT_OTP_EG_GET_CLASS(obj); - timer_del(s->dai->delay); - timer_del(s->lci->prog_delay); - qemu_bh_cancel(s->keygen->entropy_bh); - s->keygen->edn_sched = false; - - memset(s->regs, 0, REGS_COUNT * sizeof(uint32_t)); - memset(s->hw_cfg, 0, sizeof(*s->hw_cfg)); + memset(s->regs, 0, REGS_SIZE); s->regs[R_DIRECT_ACCESS_REGWEN] = 0x1u; s->regs[R_CHECK_TRIGGER_REGWEN] = 0x1u; @@ -3984,201 +1125,101 @@ static void ot_otp_eg_reset_enter(Object *obj, ResetType type) s->regs[R_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK] = 0x1u; s->regs[R_ROT_CREATOR_AUTH_STATE_READ_LOCK] = 0x1u; - s->alert_bm = 0; - - s->lc_broadcast.current_level = 0u; - s->lc_broadcast.level = 0u; - s->lc_broadcast.signal = 0u; - - ot_otp_eg_update_irqs(s); - ot_otp_eg_update_alerts(s); - ibex_irq_set(&s->pwc_otp_rsp, 0); - - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - /* TODO: initialize with actual default partition data once known */ - if (OtOTPPartDescs[ix].buffered) { - s->partctrls[ix].state.b = OTP_BUF_IDLE; - } else { - s->partctrls[ix].state.u = OTP_UNBUF_IDLE; - continue; - } - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); - memset(s->partctrls[ix].buffer.data, 0, part_size); - s->partctrls[ix].digest = 0; - if (OtOTPPartDescs[ix].iskeymgr_creator || - OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = true; - s->partctrls[ix].write_lock = true; - } + if (dc->parent_phases.enter) { + /* OtOTPEngineState cleanup */ + dc->parent_phases.enter(obj, type); } - DAI_CHANGE_STATE(s, OTP_DAI_RESET); - LCI_CHANGE_STATE(s, OTP_LCI_RESET); } -static void ot_otp_eg_reset_exit(Object *obj, ResetType type) +static void ot_otp_eg_init(Object *obj) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); OtOTPEgState *s = OT_OTP_EG(obj); + OtOTPEngineState *es = OT_OTP_ENGINE(obj); - trace_ot_otp_reset(s->ot_id, "exit"); - - if (c->parent_phases.exit) { - c->parent_phases.exit(obj, type); - } - - OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); - memcpy(&s->be_chars, bec->get_characteristics(s->otp_backend), - sizeof(OtOtpBeCharacteristics)); - - ot_edn_connect_endpoint(s->edn, s->edn_ep, &ot_otp_eg_keygen_push_entropy, - s); - - qemu_bh_schedule(s->keygen->entropy_bh); -} - -static void ot_otp_eg_realize(DeviceState *dev, Error **errp) -{ - OtOTPEgState *s = OT_OTP_EG(dev); - (void)errp; - - g_assert(s->ot_id); - g_assert(s->otp_backend); - - /* - * Set the OTP drive's permissions now during realization. We can't leave it - * until reset because QEMU might have `-daemonize`d and changed directory, - * invalidating the filesystem path to the OTP image. - */ - if (s->blk) { - bool write = blk_supports_write_perm(s->blk); - uint64_t perm = BLK_PERM_CONSISTENT_READ | (write ? BLK_PERM_WRITE : 0); - if (blk_set_perm(s->blk, perm, perm, &error_fatal)) { - warn_report("%s: %s: OTP backend is R/O", __func__, s->ot_id); - } - } - - ot_otp_eg_configure_scrmbl_key(s); - ot_otp_eg_configure_digest(s); - ot_otp_eg_configure_sram(s); - ot_otp_eg_configure_flash(s); - ot_otp_eg_configure_part_scramble_keys(s); - ot_otp_eg_configure_inv_default_parts(s); -} + /* note: device realization is implemented in OtOTPEngineState */ -static void ot_otp_eg_init(Object *obj) -{ - OtOTPEgState *s = OT_OTP_EG(obj); + es->regs = g_new0(uint32_t, REGS_COUNT); + es->reg_offset.dai_base = R_DIRECT_ACCESS_REGWEN; + es->reg_offset.err_code_base = R_ERR_CODE_0; + es->reg_offset.read_lock_base = R_VENDOR_TEST_READ_LOCK; /* * "ctrl" region covers two sub-regions: * - "regs", registers: * offset 0, size REGS_SIZE * - "swcfg", software config window - * offset SW_CFG_WINDOW, size SW_CFG_WINDOW_SIZE + * offset SW_CFG_WINDOW_OFFSET, size SW_CFG_WINDOW_SIZE */ - memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP "-ctrl", - SW_CFG_WINDOW + SW_CFG_WINDOW_SIZE); + memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP_EG "-ctrl", + SW_CFG_WINDOW_OFFSET + SW_CFG_WINDOW_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio.ctrl); memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_eg_reg_ops, s, - TYPE_OT_OTP "-regs", REGS_SIZE); + TYPE_OT_OTP_EG "-regs", REGS_SIZE); memory_region_add_subregion(&s->mmio.ctrl, 0u, &s->mmio.sub.regs); - /* TODO: it might be worthwhile to use a ROM-kind here */ + /* @todo it might be worthwhile to use a ROM-kind here */ memory_region_init_io(&s->mmio.sub.swcfg, obj, &ot_otp_eg_swcfg_ops, s, - TYPE_OT_OTP "-swcfg", SW_CFG_WINDOW_SIZE); - memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, + TYPE_OT_OTP_EG "-swcfg", SW_CFG_WINDOW_SIZE); + memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW_OFFSET, &s->mmio.sub.swcfg); - - ibex_qdev_init_irq(obj, &s->pwc_otp_rsp, OT_PWRMGR_OTP_RSP); - - qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_eg_pwr_otp_req, - OT_PWRMGR_OTP_REQ, 1); - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_sysbus_init_irq(obj, &s->irqs[ix]); - } - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); - } - - qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_eg_lc_broadcast_recv, - OT_LC_BROADCAST, OT_OTP_LC_BROADCAST_COUNT); - - s->hw_cfg = g_new0(OtOTPHWCfg, 1u); - s->tokens = g_new0(OtOTPTokens, 1u); - s->regs = g_new0(uint32_t, REGS_COUNT); - s->dai = g_new0(OtOTPDAIController, 1u); - s->lci = g_new0(OtOTPLCIController, 1u); - s->partctrls = g_new0(OtOTPPartController, OTP_PART_COUNT); - s->keygen = g_new0(OtOTPKeyGen, 1u); - s->otp = g_new0(OtOTPStorage, 1u); - s->scrmbl_key_init = g_new0(OtOTPScrmblKeyInit, 1u); - - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (!OtOTPPartDescs[ix].buffered) { - continue; - } - size_t part_words = - ot_otp_eg_part_data_byte_size(ix) / sizeof(uint32_t); - s->partctrls[ix].buffer.data = g_new0(uint32_t, part_words); - } - - ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT); - s->keygen->present = ot_present_new(); - s->keygen->prng = ot_prng_allocate(); - - s->dai->delay = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_otp_eg_dai_complete, s); - s->dai->digest_bh = qemu_bh_new(&ot_otp_eg_dai_write_digest, s); - s->lci->prog_delay = - timer_new_ns(OT_OTP_HW_CLOCK, &ot_otp_eg_lci_write_word, s); - s->pwr_otp_bh = qemu_bh_new(&ot_otp_eg_pwr_otp_bh, s); - s->lc_broadcast.bh = qemu_bh_new(&ot_otp_eg_lc_broadcast_bh, s); - s->keygen->entropy_bh = qemu_bh_new(&ot_otp_eg_request_entropy_bh, s); - - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - ot_prng_reseed(s->keygen->prng, (uint32_t)now); - -#ifdef OT_OTP_DEBUG - s->hexstr = g_new0(char, OT_OTP_HEXSTR_SIZE); -#endif } static void ot_otp_eg_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - g_assert(OTP_PART_LIFE_CYCLE_SIZE == - OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size); - - dc->realize = &ot_otp_eg_realize; - device_class_set_props(dc, ot_otp_eg_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - ResettableClass *rc = RESETTABLE_CLASS(klass); - OtOTPClass *oc = OT_OTP_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_otp_eg_reset_enter, NULL, - &ot_otp_eg_reset_exit, - &oc->parent_phases); + OtOTPEgClass *dc = OT_OTP_EG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_eg_reset_enter, NULL, NULL, + &dc->parent_phases); + OtOTPEngineClass *ec = OT_OTP_ENGINE_CLASS(klass); + OtOTPIfClass *oc = OT_OTP_IF_CLASS(klass); oc->get_lc_info = &ot_otp_eg_get_lc_info; - oc->get_hw_cfg = &ot_otp_eg_get_hw_cfg; - oc->get_otp_key = &ot_otp_eg_get_otp_key; + oc->get_hw_cfg = ec->get_hw_cfg; + oc->get_otp_key = ec->get_otp_key; oc->get_keymgr_secret = &ot_otp_eg_get_keymgr_secret; - oc->program_req = &ot_otp_eg_program_req; + oc->program_req = ec->program_req; + + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_CLASS(klass); + ic->signal_pwr_sequence = &ot_otp_eg_signal_pwr_sequence; + ic->update_status_error = &ot_otp_eg_update_status_error; + + ic->part_descs = OT_OTP_PART_DESCS; + ic->part_count = (unsigned)OTP_PART_COUNT; + ic->part_lc_num = (unsigned)OTP_PART_LIFE_CYCLE; + ic->sram_key_req_slot_count = NUM_SRAM_KEY_REQ_SLOTS; + + ic->key_seeds = OT_OTP_KEY_SEEDS; + ic->has_flash_support = true; + ic->has_zer_support = false; + for (unsigned part_ix = 0; part_ix < ic->part_count; part_ix++) { + g_assert(!ic->part_descs[part_ix].zeroizable && + ic->part_descs[part_ix].zer_offset == UINT16_MAX); + } - ot_otp_eg_class_add_scramble_key_props(oc); - ot_otp_eg_class_add_inv_def_props(oc); + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_FLASH_ADDR].size == + SECRET1_FLASH_ADDR_KEY_SEED_SIZE); + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_FLASH_DATA].size == + SECRET1_FLASH_DATA_KEY_SEED_SIZE); + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_SRAM].size == + SECRET1_SRAM_DATA_KEY_SEED_SIZE); } static const TypeInfo ot_otp_eg_info = { .name = TYPE_OT_OTP_EG, - .parent = TYPE_OT_OTP, + .parent = TYPE_OT_OTP_ENGINE, .instance_size = sizeof(OtOTPEgState), .instance_init = &ot_otp_eg_init, - .class_size = sizeof(OtOTPClass), + .class_size = sizeof(OtOTPEgClass), .class_init = &ot_otp_eg_class_init, + .interfaces = + (InterfaceInfo[]){ + { TYPE_OT_OTP_IF }, /* public OTP API */ + { TYPE_OT_OTP_IMPL_IF }, /* private OTP API for OTP engine */ + {}, + }, }; static void ot_otp_eg_register_types(void) diff --git a/hw/opentitan/ot_otp_eg_parts.c b/hw/opentitan/ot_otp_eg_parts.c index dfdc986d00f4c..a8c76dd53897d 100644 --- a/hw/opentitan/ot_otp_eg_parts.c +++ b/hw/opentitan/ot_otp_eg_parts.c @@ -1,14 +1,19 @@ -/* Generated from otp_ctrl_mmap.hjson with otptool.py */ +/* + * Generated from otp_ctrl_mmap.hjson with otptool.py + * Top version: 011b6ea1fe + */ /* this prevents linters from checking this file without its parent file */ #ifdef OT_OTP_EG_PARTS /* clang-format off */ /* NOLINTBEGIN */ -static const OtOTPPartDesc OtOTPPartDescs[] = { +static const OtOTPPartDesc OT_OTP_PART_DESCS[] = { [OTP_PART_VENDOR_TEST] = { + .name = "VENDOR_TEST", .size = 64u, .offset = 0u, + .zer_offset = UINT16_MAX, .digest_offset = 56u, .hw_digest = false, .sw_digest = true, @@ -18,10 +23,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock_csr = true, .read_lock = true, .integrity = false, + .zeroizable = false, }, [OTP_PART_CREATOR_SW_CFG] = { + .name = "CREATOR_SW_CFG", .size = 368u, .offset = 64u, + .zer_offset = UINT16_MAX, .digest_offset = 424u, .hw_digest = false, .sw_digest = true, @@ -31,10 +39,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock_csr = true, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_OWNER_SW_CFG] = { + .name = "OWNER_SW_CFG", .size = 712u, .offset = 432u, + .zer_offset = UINT16_MAX, .digest_offset = 1136u, .hw_digest = false, .sw_digest = true, @@ -44,10 +55,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock_csr = true, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_ROT_CREATOR_AUTH_CODESIGN] = { + .name = "ROT_CREATOR_AUTH_CODESIGN", .size = 472u, .offset = 1144u, + .zer_offset = UINT16_MAX, .digest_offset = 1608u, .hw_digest = false, .sw_digest = true, @@ -57,10 +71,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock_csr = true, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_ROT_CREATOR_AUTH_STATE] = { + .name = "ROT_CREATOR_AUTH_STATE", .size = 40u, .offset = 1616u, + .zer_offset = UINT16_MAX, .digest_offset = 1648u, .hw_digest = false, .sw_digest = true, @@ -70,10 +87,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock_csr = true, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_HW_CFG0] = { + .name = "HW_CFG0", .size = 72u, .offset = 1656u, + .zer_offset = UINT16_MAX, .digest_offset = 1720u, .hw_digest = true, .sw_digest = false, @@ -82,10 +102,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .write_lock = false, .read_lock = false, .integrity = true, + .zeroizable = false, }, [OTP_PART_HW_CFG1] = { + .name = "HW_CFG1", .size = 16u, .offset = 1728u, + .zer_offset = UINT16_MAX, .digest_offset = 1736u, .hw_digest = true, .sw_digest = false, @@ -94,10 +117,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .write_lock = false, .read_lock = false, .integrity = true, + .zeroizable = false, }, [OTP_PART_SECRET0] = { + .name = "SECRET0", .size = 40u, .offset = 1744u, + .zer_offset = UINT16_MAX, .digest_offset = 1776u, .hw_digest = true, .sw_digest = false, @@ -106,10 +132,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .write_lock = false, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_SECRET1] = { + .name = "SECRET1", .size = 88u, .offset = 1784u, + .zer_offset = UINT16_MAX, .digest_offset = 1864u, .hw_digest = true, .sw_digest = false, @@ -118,10 +147,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .write_lock = false, .read_lock = true, .integrity = true, + .zeroizable = false, }, [OTP_PART_SECRET2] = { + .name = "SECRET2", .size = 88u, .offset = 1872u, + .zer_offset = UINT16_MAX, .digest_offset = 1952u, .hw_digest = true, .sw_digest = false, @@ -131,10 +163,13 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .read_lock = true, .integrity = true, .iskeymgr_creator = true, + .zeroizable = false, }, [OTP_PART_LIFE_CYCLE] = { + .name = "LIFE_CYCLE", .size = 88u, .offset = 1960u, + .zer_offset = UINT16_MAX, .digest_offset = UINT16_MAX, .hw_digest = false, .sw_digest = false, @@ -143,10 +178,34 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .write_lock = false, .read_lock = false, .integrity = true, + .zeroizable = false, }, }; -#define OTP_PART_COUNT ARRAY_SIZE(OtOTPPartDescs) +#define OTP_PART_COUNT ARRAY_SIZE(OT_OTP_PART_DESCS) + +static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OT_OTP_KEY_COUNT] = { + [OT_OTP_KEY_FLASH_ADDR] = { + .partition = OTP_PART_SECRET1, + .offset = 0, + .size = 32, + }, + [OT_OTP_KEY_FLASH_DATA] = { + .partition = OTP_PART_SECRET1, + .offset = 32, + .size = 32, + }, + [OT_OTP_KEY_OTBN] = { + .partition = OTP_PART_SECRET1, + .offset = 64, + .size = 16, + }, + [OT_OTP_KEY_SRAM] = { + .partition = OTP_PART_SECRET1, + .offset = 64, + .size = 16, + }, +}; /* NOLINTEND */ /* clang-format on */ diff --git a/hw/opentitan/ot_otp_engine.c b/hw/opentitan/ot_otp_engine.c new file mode 100644 index 0000000000000..f2d9db69dd853 --- /dev/null +++ b/hw/opentitan/ot_otp_engine.c @@ -0,0 +1,2974 @@ +/* + * QEMU OpenTitan EarlGrey One Time Programmable (OTP) memory controller + * + * Copyright (c) 2023-2025 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * Alex Jones + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define OT_OTP_COMPORTABLE_REGS + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_fifo32.h" +#include "hw/opentitan/ot_lc_ctrl.h" +#include "hw/opentitan/ot_otp_engine.h" +#include "hw/opentitan/ot_otp_impl_if.h" +#include "hw/opentitan/ot_present.h" +#include "hw/opentitan/ot_prng.h" +#include "hw/opentitan/ot_pwrmgr.h" +#include "hw/qdev-properties-system.h" +#include "hw/qdev-properties.h" +#include "sysemu/block-backend.h" +#include "trace.h" + +/* + * The OTP may be used before any CPU is started, This may cause the default + * virtual clock to stall, as the hart does not execute. OTP nevertheless may + * be active, updating the OTP content where write delays are still needed. + * Use the alternative clock source which counts even when the CPU is stalled. + */ +#define OT_OTP_HW_CLOCK QEMU_CLOCK_VIRTUAL_RT + +/* the following delays are arbitrary for now */ +#define DAI_DIGEST_DELAY_NS 50000u /* 50us */ +#define LCI_PROG_SCHED_NS 1000u /* 1us*/ + +/* The size of keys used for OTP scrambling */ +#define OTP_SCRAMBLING_KEY_WIDTH 128u +#define OTP_SCRAMBLING_KEY_BYTES ((OTP_SCRAMBLING_KEY_WIDTH) / 8u) + +#define LC_TRANSITION_CNT_SIZE 48u +#define LC_STATE_SIZE 40u + +/* Sizes of constants used for deriving scrambling keys */ +#define KEY_MGR_KEY_WIDTH 256u + +#define OT_OTP_NAME_ENTRY(_st_) [OT_OTP_##_st_] = stringify(OT_OTP_##_st_) + +static const char *DAI_STATE_NAMES[] = { + /* clang-format off */ + OT_OTP_NAME_ENTRY(DAI_RESET), + OT_OTP_NAME_ENTRY(DAI_INIT_OTP), + OT_OTP_NAME_ENTRY(DAI_INIT_PART), + OT_OTP_NAME_ENTRY(DAI_IDLE), + OT_OTP_NAME_ENTRY(DAI_ERROR), + OT_OTP_NAME_ENTRY(DAI_READ), + OT_OTP_NAME_ENTRY(DAI_READ_WAIT), + OT_OTP_NAME_ENTRY(DAI_DESCR), + OT_OTP_NAME_ENTRY(DAI_DESCR_WAIT), + OT_OTP_NAME_ENTRY(DAI_WRITE), + OT_OTP_NAME_ENTRY(DAI_WRITE_WAIT), + OT_OTP_NAME_ENTRY(DAI_SCR), + OT_OTP_NAME_ENTRY(DAI_SCR_WAIT), + OT_OTP_NAME_ENTRY(DAI_DIG_CLR), + OT_OTP_NAME_ENTRY(DAI_DIG_READ), + OT_OTP_NAME_ENTRY(DAI_DIG_READ_WAIT), + OT_OTP_NAME_ENTRY(DAI_DIG), + OT_OTP_NAME_ENTRY(DAI_DIG_PAD), + OT_OTP_NAME_ENTRY(DAI_DIG_FIN), + OT_OTP_NAME_ENTRY(DAI_DIG_WAIT), + /* clang-format on */ +}; + +static const char *LCI_STATE_NAMES[] = { + /* clang-format off */ + OT_OTP_NAME_ENTRY(LCI_RESET), + OT_OTP_NAME_ENTRY(LCI_IDLE), + OT_OTP_NAME_ENTRY(LCI_WRITE), + OT_OTP_NAME_ENTRY(LCI_WRITE_WAIT), + OT_OTP_NAME_ENTRY(LCI_ERROR), + /* clang-format on */ +}; + +static const char *ERR_CODE_NAMES[] = { + /* clang-format off */ + OT_OTP_NAME_ENTRY(NO_ERROR), + OT_OTP_NAME_ENTRY(MACRO_ERROR), + OT_OTP_NAME_ENTRY(MACRO_ECC_CORR_ERROR), + OT_OTP_NAME_ENTRY(MACRO_ECC_UNCORR_ERROR), + OT_OTP_NAME_ENTRY(MACRO_WRITE_BLANK_ERROR), + OT_OTP_NAME_ENTRY(ACCESS_ERROR), + OT_OTP_NAME_ENTRY(CHECK_FAIL_ERROR), + OT_OTP_NAME_ENTRY(FSM_STATE_ERROR), + /* clang-format on */ +}; + +#define BUF_STATE_NAME(_st_) \ + ((unsigned)(_st_) < ARRAY_SIZE(BUF_STATE_NAMES) ? \ + BUF_STATE_NAMES[(_st_)] : \ + "?") +#define UNBUF_STATE_NAME(_st_) \ + ((unsigned)(_st_) < ARRAY_SIZE(UNBUF_STATE_NAMES) ? \ + UNBUF_STATE_NAMES[(_st_)] : \ + "?") +#define DAI_STATE_NAME(_st_) \ + ((unsigned)(_st_) < ARRAY_SIZE(DAI_STATE_NAMES) ? \ + DAI_STATE_NAMES[(_st_)] : \ + "?") +#define LCI_STATE_NAME(_st_) \ + ((unsigned)(_st_) < ARRAY_SIZE(LCI_STATE_NAMES) ? \ + LCI_STATE_NAMES[(_st_)] : \ + "?") +#define ERR_CODE_NAME(_err_) \ + (((unsigned)(_err_)) < ARRAY_SIZE(ERR_CODE_NAMES) ? \ + ERR_CODE_NAMES[(_err_)] : \ + "?") + +/* @todo add assertion to validate those */ +#define OTP_ENTRY_DAI(_s_) ((_s_)->part_count + 0u) +#define OTP_ENTRY_KDI(_s_) ((_s_)->part_count + 1u) +#define OTP_ENTRY_COUNT(_s_) ((_s_)->part_count + 2u) + +#define DIRECT_ACCESS_REG(_s_, _reg_) \ + ((_s_)->regs[(_s_)->reg_offset.dai_base + \ + (unsigned)(OT_OTP_DA_REG_##_reg_)]) +#define ERR_CODE_PART_REG(_s_, _pix_) \ + ((_s_)->regs[(_s_)->reg_offset.err_code_base + (_pix_)]) + +/* + * See RTL files otp_ctrl/rtl/otp_ctrl_kdi.sv and otp_ctrl/rtl/otp_ctrl_pkg.sv + * and OTP doc otp_ctrl/theory_of_operation.html#scrambling-key-derivation. + */ +#define SRAM_KEY_BYTES(_ic_) ((_ic_)->key_seeds[OT_OTP_KEY_SRAM].size) +#define SRAM_NONCE_BYTES(_ic_) ((_ic_)->key_seeds[OT_OTP_KEY_SRAM].size) +#define OTBN_KEY_BYTES(_ic_) ((_ic_)->key_seeds[OT_OTP_KEY_OTBN].size) +#define OTBN_NONCE_BYTES(_ic_) (((_ic_)->key_seeds[OT_OTP_KEY_OTBN].size) / 2u) + +#define OT_OTP_SCRMBL_KEY_SIZE 16u +#define OT_OTP_SCRMBL_NONE_SIZE (OT_OTP_SCRMBL_KEY_SIZE) + +/* Need 128 bits of entropy to compute each 64-bit key part */ +#define OTP_ENTROPY_PRESENT_BYTES(_ic_) \ + (((((_ic_)->sram_key_req_slot_count) * SRAM_KEY_BYTES(_ic_)) + \ + (OTBN_KEY_BYTES(_ic_))) * \ + 2u) +#define OTP_ENTROPY_NONCE_BYTES(_ic_) \ + (((_ic_)->sram_key_req_slot_count) * SRAM_NONCE_BYTES(_ic_) + \ + OTBN_NONCE_BYTES(_ic_)) +#define OTP_ENTROPY_BUF_COUNT(_ic_) \ + ((OTP_ENTROPY_PRESENT_BYTES(_ic_) + OTP_ENTROPY_NONCE_BYTES(_ic_)) / 4u) + +#ifdef OT_OTP_DEBUG +#define OT_OTP_HEXSTR_SIZE 256u +#define TRACE_OTP(msg, ...) qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); +#define ot_otp_hexdump(_s_, _b_, _l_) \ + ot_common_lhexdump((const uint8_t *)_b_, _l_, false, (_s_)->hexstr, \ + OT_OTP_HEXSTR_SIZE) +#else +#define TRACE_OTP(msg, ...) +#define ot_otp_hexdump(_s_, _b_, _l_) +#endif + +#define DAI_CHANGE_STATE(_s_, _st_) \ + ot_otp_engine_dai_change_state_line(_s_, _st_, __LINE__) +#define LCI_CHANGE_STATE(_s_, _st_) \ + ot_otp_engine_lci_change_state_line(_s_, _st_, __LINE__) + +static void ot_otp_engine_dai_set_error(OtOTPEngineState *s, OtOTPError err); +static void ot_otp_engine_dai_change_state_line(OtOTPEngineState *s, + OtOTPDAIState state, int line); +static void ot_otp_engine_lci_change_state_line(OtOTPEngineState *s, + OtOTPLCIState state, int line); + +struct OtOTPScrmblKeyInit_ { + uint8_t key[OT_OTP_SCRMBL_KEY_SIZE]; + uint8_t nonce[OT_OTP_SCRMBL_NONE_SIZE]; +}; + +struct OtOTPKeyGen_ { + QEMUBH *entropy_bh; + OtPresentState *present; + OtPrngState *prng; + OtFifo32 entropy_buf; + bool edn_sched; +}; + +static void ot_otp_engine_update_irqs(OtOTPEngineState *s) +{ + uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { + int level = (int)((levels >> ix) & 0x1u); + if (level != ibex_irq_get_level(&s->irqs[ix])) { + trace_ot_otp_update_irq(s->ot_id, ibex_irq_get_level(&s->irqs[ix]), + level); + } + ibex_irq_set(&s->irqs[ix], level); + } +} + +static void ot_otp_engine_update_alerts(OtOTPEngineState *s) +{ + uint32_t levels = s->regs[R_ALERT_TEST]; + + levels |= s->alert_bm; + + 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_otp_update_alert(s->ot_id, + ibex_irq_get_level(&s->alerts[ix]), + level); + } + ibex_irq_set(&s->alerts[ix], level); + } + + /* alert test is transient */ + if (s->regs[R_ALERT_TEST]) { + s->regs[R_ALERT_TEST] = 0; + + levels = s->alert_bm; + 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_otp_update_alert(s->ot_id, + ibex_irq_get_level(&s->alerts[ix]), + level); + } + ibex_irq_set(&s->alerts[ix], level); + } + } +} + +static const char * +ot_otp_engine_part_name(const OtOTPEngineState *s, unsigned part_ix) +{ + if (part_ix < s->part_count) { + return s->part_descs[part_ix].name; + } + + if (part_ix == OTP_ENTRY_DAI(s)) { + return "DAI"; + } + + if (part_ix == OTP_ENTRY_KDI(s)) { + return "KDI"; + } + + return "?"; +} + +static void ot_otp_engine_disable_all_partitions(OtOTPEngineState *s) +{ + DAI_CHANGE_STATE(s, OT_OTP_DAI_ERROR); + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + + for (unsigned pix = 0; pix < s->part_count; pix++) { + OtOTPPartController *pctrl = &s->part_ctrls[pix]; + pctrl->failed = true; + } +} + +static void ot_otp_engine_set_error(OtOTPEngineState *s, unsigned part_ix, + OtOTPError err) +{ + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + g_assert(part_ix < OTP_ENTRY_COUNT(s)); + + uint32_t errval = ((uint32_t)err) & OT_OTP_ERR_CODE_MASK; + if (errval || errval != ERR_CODE_PART_REG(s, part_ix)) { + trace_ot_otp_set_error(s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix, ERR_CODE_NAME(err), err); + } + ERR_CODE_PART_REG(s, part_ix) = errval; + + switch (err) { + case OT_OTP_MACRO_ERROR: + case OT_OTP_MACRO_ECC_UNCORR_ERROR: + s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; + ot_otp_engine_update_alerts(s); + break; + /* NOLINTNEXTLINE */ + case OT_OTP_MACRO_ECC_CORR_ERROR: + /* + * "The corresponding controller automatically recovers from this error + * when issuing a new command." + */ + break; + case OT_OTP_MACRO_WRITE_BLANK_ERROR: + break; + case OT_OTP_ACCESS_ERROR: + ic->update_status_error(OT_OTP_IMPL_IF(s), OT_OTP_STATUS_DAI, true); + break; + case OT_OTP_CHECK_FAIL_ERROR: + case OT_OTP_FSM_STATE_ERROR: + s->alert_bm |= ALERT_FATAL_CHECK_ERROR_MASK; + ot_otp_engine_update_alerts(s); + break; + default: + break; + } + + if (s->alert_bm & ALERT_FATAL_CHECK_ERROR_MASK) { + ot_otp_engine_disable_all_partitions(s); + error_report("%s: %s: OTP disabled on fatal error", __func__, s->ot_id); + } + + if (err != OT_OTP_NO_ERROR) { + s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; + ot_otp_engine_update_irqs(s); + } +} + +static int +ot_otp_engine_get_part_from_address(const OtOTPEngineState *s, hwaddr addr) +{ + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + const OtOTPPartDesc *part = &s->part_descs[part_ix]; + if ((addr >= part->offset) && + ((addr + sizeof(uint32_t)) <= (part->offset + part->size))) { + trace_ot_otp_addr_to_part(s->ot_id, (uint32_t)addr, + ot_otp_engine_part_name(s, part_ix), + part_ix); + return (int)part_ix; + } + } + + return -1; +} + +static uint8_t ot_otp_engine_compute_ecc_u16(uint16_t data) +{ + uint32_t data_o = (uint32_t)data; + + data_o |= __builtin_parity(data_o & 0x00ad5bu) << 16u; + data_o |= __builtin_parity(data_o & 0x00366du) << 17u; + data_o |= __builtin_parity(data_o & 0x00c78eu) << 18u; + data_o |= __builtin_parity(data_o & 0x0007f0u) << 19u; + data_o |= __builtin_parity(data_o & 0x00f800u) << 20u; + data_o |= __builtin_parity(data_o & 0x1fffffu) << 21u; + + return (uint8_t)(data_o >> 16u); +} + +static uint16_t ot_otp_engine_compute_ecc_u32(uint32_t data) +{ + uint16_t data_lo = (uint16_t)(data & UINT16_MAX); + uint16_t data_hi = (uint16_t)(data >> 16u); + + uint16_t ecc_lo = (uint16_t)ot_otp_engine_compute_ecc_u16(data_lo); + uint16_t ecc_hi = (uint16_t)ot_otp_engine_compute_ecc_u16(data_hi); + + return (ecc_hi << 8u) | ecc_lo; +} + +static uint32_t ot_otp_engine_compute_ecc_u64(uint64_t data) +{ + uint32_t data_lo = (uint32_t)(data & UINT32_MAX); + uint32_t data_hi = (uint32_t)(data >> 32u); + + uint32_t ecc_lo = (uint32_t)ot_otp_engine_compute_ecc_u32(data_lo); + uint32_t ecc_hi = (uint32_t)ot_otp_engine_compute_ecc_u32(data_hi); + + return (ecc_hi << 16u) | ecc_lo; +} + +static uint32_t ot_otp_engine_verify_ecc_22_16_u16( + const OtOTPEngineState *s, uint32_t data_i, unsigned *err_o) +{ + unsigned syndrome = 0u; + + syndrome |= __builtin_parity(data_i & 0x01ad5bu) << 0u; + syndrome |= __builtin_parity(data_i & 0x02366du) << 1u; + syndrome |= __builtin_parity(data_i & 0x04c78eu) << 2u; + syndrome |= __builtin_parity(data_i & 0x0807f0u) << 3u; + syndrome |= __builtin_parity(data_i & 0x10f800u) << 4u; + syndrome |= __builtin_parity(data_i & 0x3fffffu) << 5u; + + unsigned err = (syndrome >> 5u) & 1u; + if (!err && (syndrome & 0x1fu)) { + err = 2u; + } + + *err_o = err; + + if (!err) { + return data_i & UINT16_MAX; + } + + uint32_t data_o = 0; + +#define OTP_ECC_RECOVER(_sy_, _di_, _ix_) \ + ((unsigned)((syndrome == (_sy_)) ^ (bool)((_di_) & (1u << (_ix_)))) \ + << (_ix_)) + + data_o |= OTP_ECC_RECOVER(0x23u, data_i, 0u); + data_o |= OTP_ECC_RECOVER(0x25u, data_i, 1u); + data_o |= OTP_ECC_RECOVER(0x26u, data_i, 2u); + data_o |= OTP_ECC_RECOVER(0x27u, data_i, 3u); + data_o |= OTP_ECC_RECOVER(0x29u, data_i, 4u); + data_o |= OTP_ECC_RECOVER(0x2au, data_i, 5u); + data_o |= OTP_ECC_RECOVER(0x2bu, data_i, 6u); + data_o |= OTP_ECC_RECOVER(0x2cu, data_i, 7u); + data_o |= OTP_ECC_RECOVER(0x2du, data_i, 8u); + data_o |= OTP_ECC_RECOVER(0x2eu, data_i, 9u); + data_o |= OTP_ECC_RECOVER(0x2fu, data_i, 10u); + data_o |= OTP_ECC_RECOVER(0x31u, data_i, 11u); + data_o |= OTP_ECC_RECOVER(0x32u, data_i, 12u); + data_o |= OTP_ECC_RECOVER(0x33u, data_i, 13u); + data_o |= OTP_ECC_RECOVER(0x34u, data_i, 14u); + data_o |= OTP_ECC_RECOVER(0x35u, data_i, 15u); + +#undef OTP_ECC_RECOVER + + if (err > 1u) { + trace_ot_otp_ecc_unrecoverable_error(s->ot_id, data_i & UINT16_MAX); + } else { + if ((data_i & UINT16_MAX) != data_o) { + trace_ot_otp_ecc_recovered_error(s->ot_id, data_i & UINT16_MAX, + data_o); + } else { + /* ECC bit is corrupted */ + trace_ot_otp_ecc_parity_error(s->ot_id, data_i & UINT16_MAX, + data_i >> 16u); + } + } + + return data_o; +} + +static uint32_t ot_otp_engine_verify_ecc( + const OtOTPEngineState *s, uint32_t data, uint32_t ecc, unsigned *err) +{ + uint32_t data_lo_i, data_lo_o, data_hi_i, data_hi_o; + unsigned err_lo, err_hi; + + data_lo_i = (data & 0xffffu) | ((ecc & 0xffu) << 16u); + data_lo_o = ot_otp_engine_verify_ecc_22_16_u16(s, data_lo_i, &err_lo); + + data_hi_i = (data >> 16u) | (((ecc >> 8u) & 0xffu) << 16u); + data_hi_o = ot_otp_engine_verify_ecc_22_16_u16(s, data_hi_i, &err_hi); + + *err |= err_lo | err_hi; + + return (data_hi_o << 16u) | data_lo_o; +} + +static uint64_t ot_otp_engine_apply_digest_ecc( + OtOTPEngineState *s, unsigned partition, uint64_t digest, uint32_t ecc) +{ + uint32_t dig_lo = (uint32_t)(digest & UINT32_MAX); + uint32_t dig_hi = (uint32_t)(digest >> 32u); + + unsigned err = 0; + dig_lo = ot_otp_engine_verify_ecc(s, dig_lo, ecc & 0xffffu, &err); + dig_hi = ot_otp_engine_verify_ecc(s, dig_hi, ecc >> 16u, &err); + digest = (((uint64_t)dig_hi) << 32u) | ((uint64_t)dig_lo); + + if (err) { + OtOTPError otp_err = (err > 1) ? OT_OTP_MACRO_ECC_UNCORR_ERROR : + OT_OTP_MACRO_ECC_CORR_ERROR; + /* + * Note: need to check if any caller could override the error/state + * in this case + */ + ot_otp_engine_set_error(s, partition, otp_err); + } + + return digest; +} + +static int ot_otp_engine_apply_ecc(OtOTPEngineState *s, unsigned part_ix) +{ + g_assert(ot_otp_engine_is_ecc_enabled(s)); + + unsigned start = s->part_descs[part_ix].offset >> 2u; + unsigned end = (ot_otp_engine_is_buffered(s, (int)part_ix) && + ot_otp_engine_has_digest(s, part_ix)) ? + (unsigned)(s->part_descs[part_ix].digest_offset >> 2u) : + start + (unsigned)(s->part_descs[part_ix].size >> 2u); + + g_assert(start < end && (end / sizeof(uint32_t)) < s->otp->data_size); + for (unsigned ix = start; ix < end; ix++) { + unsigned err = 0; + uint32_t *word = &s->otp->data[ix]; + uint16_t ecc = ((const uint16_t *)s->otp->ecc)[ix]; + *word = ot_otp_engine_verify_ecc(s, *word, (uint32_t)ecc, &err); + if (err) { + OtOTPError otp_err = (err > 1) ? OT_OTP_MACRO_ECC_UNCORR_ERROR : + OT_OTP_MACRO_ECC_CORR_ERROR; + /* + * Note: need to check if any caller could override the error/state + * in this case + */ + ot_otp_engine_set_error(s, part_ix, otp_err); + if (err > 1) { + trace_ot_otp_ecc_init_error(s->ot_id, + ot_otp_engine_part_name(s, part_ix), + part_ix, ix << 2u, *word, ecc); + s->part_ctrls[part_ix].failed = true; + return -1; + } + } + } + + return 0; +} + +static uint64_t +ot_otp_engine_get_part_digest(OtOTPEngineState *s, unsigned part_ix) +{ + g_assert(!ot_otp_engine_is_buffered(s, part_ix)); + + uint16_t offset = s->part_descs[part_ix].digest_offset; + + if (offset == UINT16_MAX) { + return 0u; + } + + const uint8_t *data = (const uint8_t *)s->otp->data; + uint64_t digest = ldq_le_p(data + offset); + + if (s->part_descs[part_ix].integrity && ot_otp_engine_is_ecc_enabled(s)) { + unsigned waddr = offset >> 2u; + unsigned ewaddr = waddr >> 1u; + g_assert(ewaddr < s->otp->ecc_size); + uint32_t ecc = s->otp->ecc[ewaddr]; + digest = ot_otp_engine_apply_digest_ecc(s, part_ix, digest, ecc); + } + + return digest; +} + +static uint32_t +ot_otp_engine_get_part_digest_reg(OtOTPEngineState *s, uint32_t offset) +{ + /* + * Offset is the register offset from the first read 32-bit digest register. + * All digests are 64-bits, which means each partition have two 32-bit + * registers to expose their digest. + * + * Not all partitions have digests, which means the offset argument is the + * relative partition offset for those partitions that features a digest, as + * there is no defined digest access registers defined for partitions that + * do not have a digest. + * + * Need to traverse the partition table to only account for those partitions + * with a digest to match the proper offset. + */ + + /* + * part_look_ix is the index of the partition in the contiguous array of + * digest registers, which would be equivalent as the index of the partition + * that would exist in a virtual partition-with-digest array. + */ + unsigned part_look_ix = (unsigned)(offset / OT_OTP_NUM_DIGEST_WORDS); + /* whether to retrieve the top most 32-bit of the digest or not */ + bool hi = (bool)(offset & 0x1u); + + /* part_ix: the partition number in the global partition array */ + unsigned part_ix = 0; + /* traverse the partition array and count each partition with a digest */ + for (unsigned part_dig_ix = 0; part_ix < s->part_count; part_ix++) { + if (ot_otp_engine_has_digest(s, part_ix)) { + /* + * stop searching if we've found the part-with-digest defined from + * the offset argument. Otherwise, increment the part-with-digest + * index and continue. + */ + if (part_dig_ix == part_look_ix) { + break; + } + part_dig_ix++; + } + } + + /* + * If the part_ix as reached the latest partition, there is something wrong + * with the partition table or the register definitions, as it is assumed + * that LifeCycle partition is the last partition. + */ + g_assert(s->part_lc_num == s->part_count - 1u); + g_assert(part_ix < s->part_count); + + const OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + uint64_t digest = pctrl->digest; + + if (hi) { + digest >>= 32u; + } + + return (uint32_t)digest; +} + +static uint32_t +ot_otp_engine_get_sw_readlock(const OtOTPEngineState *s, unsigned rdlk_ix) +{ + uint32_t reg = s->reg_offset.read_lock_base + rdlk_ix; + + return (bool)SHARED_FIELD_EX32(s->regs[reg], OT_OTP_READ_LOCK); +} + +static bool ot_otp_engine_is_readable(const OtOTPEngineState *s, + unsigned part_ix) +{ + g_assert(part_ix < s->part_count); + + const OtOTPPartDesc *pdesc = &s->part_descs[part_ix]; + const OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + if (pdesc->secret) { + /* secret partitions are only readable if digest is not yet set. */ + return pctrl->digest == 0u; + } + + if (!pdesc->read_lock_csr) { + if (!pdesc->read_lock) { + /* read lock is not supported for this partition */ + return true; + } + + /* hw read lock, not locked */ + return !pctrl->read_lock; + } + + unsigned roffset = 0; + unsigned pix; + for (pix = 0; pix < s->part_count; pix++) { + if (pix == part_ix) { + break; + } + if (pdesc->read_lock_csr) { + roffset++; + } + } + /* + * know for sure last partition is the life cycle one, which never + * support a read_lock_csr. Ideally this g_assert should be a + * static_assert, but C being C, constants are not defined as such + * at build time... + */ + g_assert(!s->part_descs[s->part_lc_num].read_lock_csr); + + /* + * If the previous loop reached the last partition, something + * seriously wrong occurred. Use this feature as a sanity check + */ + g_assert(pix < s->part_lc_num); + + /* + * now that the count of read_lock_csr is known, use it to access + * the register for the selected partition + */ + return ot_otp_engine_get_sw_readlock(s, roffset); +} + +static void ot_otp_engine_dai_change_state_line(OtOTPEngineState *s, + OtOTPDAIState state, int line) +{ + trace_ot_otp_dai_change_state(s->ot_id, line, DAI_STATE_NAME(s->dai->state), + s->dai->state, DAI_STATE_NAME(state), state); + + s->dai->state = state; +} + +static void ot_otp_engine_lci_change_state_line(OtOTPEngineState *s, + OtOTPLCIState state, int line) +{ + trace_ot_otp_lci_change_state(s->ot_id, line, LCI_STATE_NAME(s->lci->state), + s->lci->state, LCI_STATE_NAME(state), state); + + s->lci->state = state; +} + +static void ot_otp_engine_lc_broadcast_recv(void *opaque, int n, int level) +{ + OtOTPEngineState *s = opaque; + OtOTPLcBroadcast *bcast = &s->lc_broadcast; + + g_assert((unsigned)n < OT_OTP_LC_BROADCAST_COUNT); + + uint16_t bit = 1u << (unsigned)n; + bcast->signal |= 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->level |= bit; + } else { + bcast->level &= ~bit; + } + + /* use a BH to decouple IRQ signaling from actual handling */ + qemu_bh_schedule(s->lc_broadcast.bh); +} + +static void ot_otp_engine_lc_broadcast_bh(void *opaque) +{ + OtOTPEngineState *s = opaque; + OtOTPLcBroadcast *bcast = &s->lc_broadcast; + + /* handle all flagged signals */ + while (bcast->signal) { + /* pick and clear */ + unsigned sig = ctz16(bcast->signal); + uint16_t bit = 1u << (unsigned)sig; + bcast->signal &= ~bit; + bcast->current_level = + (bcast->current_level & ~bit) | (bcast->level & bit); + bool level = (bool)(bcast->current_level & bit); + + trace_ot_otp_lc_broadcast(s->ot_id, sig, level); + + switch ((int)sig) { + case OT_OTP_LC_DFT_EN: + qemu_log_mask(LOG_UNIMP, "%s: %s: DFT feature not supported\n", + __func__, s->ot_id); + break; + case OT_OTP_LC_ESCALATE_EN: + if (level) { + DAI_CHANGE_STATE(s, OT_OTP_DAI_ERROR); + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + /* @todo manage other FSMs */ + qemu_log_mask(LOG_UNIMP, + "%s: %s: ESCALATE partially implemented\n", + __func__, s->ot_id); + if (s->fatal_escalate) { + error_setg(&error_fatal, "%s: OTP LC escalate", s->ot_id); + } + } + break; + case OT_OTP_LC_CHECK_BYP_EN: + qemu_log_mask(LOG_UNIMP, "%s: %s: bypass is ignored\n", __func__, + s->ot_id); + break; + case OT_OTP_LC_CREATOR_SEED_SW_RW_EN: + for (unsigned ix = 0; ix < s->part_count; ix++) { + if (s->part_descs[ix].iskeymgr_creator) { + s->part_ctrls[ix].read_lock = !level; + s->part_ctrls[ix].write_lock = !level; + } + } + break; + case OT_OTP_LC_OWNER_SEED_SW_RW_EN: + for (unsigned ix = 0; ix < s->part_count; ix++) { + if (s->part_descs[ix].iskeymgr_owner) { + s->part_ctrls[ix].read_lock = !level; + s->part_ctrls[ix].write_lock = !level; + } + } + break; + case OT_OTP_LC_SEED_HW_RD_EN: + /* nothing to do here, SEED_HW_RD_EN flag is in current_level */ + break; + default: + error_setg(&error_fatal, "%s: %s: unexpected LC broadcast %d\n", + __func__, s->ot_id, sig); + g_assert_not_reached(); + break; + } + } +} + +static uint64_t ot_otp_engine_compute_partition_digest( + OtOTPEngineState *s, const uint8_t *base, unsigned size) +{ + OtPresentState *ps = ot_present_new(); + + g_assert((size & (sizeof(uint64_t) - 1u)) == 0); + + uint8_t buf[sizeof(uint64_t) * 2u]; + uint64_t state = s->digest_iv; + uint64_t out; + for (unsigned off = 0; off < size; off += sizeof(buf)) { + memcpy(buf, base + off, sizeof(uint64_t)); + if (off + sizeof(uint64_t) != size) { + memcpy(&buf[sizeof(uint64_t)], base + off + sizeof(uint64_t), + sizeof(uint64_t)); + } else { + /* special case, duplicate last block if block number is odd */ + memcpy(&buf[sizeof(uint64_t)], base + off, sizeof(uint64_t)); + } + + ot_present_init(ps, buf); + ot_present_encrypt(ps, state, &out); + state ^= out; + } + + ot_present_init(ps, s->digest_const); + ot_present_encrypt(ps, state, &out); + state ^= out; + + ot_present_free(ps); + + return state; +} + +static uint64_t +ot_otp_engine_load_partition_digest(OtOTPEngineState *s, unsigned partition) +{ + unsigned digoff = (unsigned)s->part_descs[partition].digest_offset; + + if ((digoff + sizeof(uint64_t)) > s->otp->data_size) { + error_setg(&error_fatal, "%s: partition located outside storage?", + s->ot_id); + /* linter doest not know the above call never returns */ + return 0u; + } + + const uint8_t *data = (const uint8_t *)s->otp->data; + uint64_t digest = ldq_le_p(data + digoff); + + if (ot_otp_engine_is_ecc_enabled(s)) { + unsigned ewaddr = (digoff >> 3u); + g_assert(ewaddr < s->otp->ecc_size); + uint32_t ecc = s->otp->ecc[ewaddr]; + digest = ot_otp_engine_apply_digest_ecc(s, partition, digest, ecc); + } + + return digest; +} + +static void +ot_otp_engine_unscramble_partition(OtOTPEngineState *s, unsigned part_ix) +{ + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_engine_part_data_byte_size(s, part_ix); + + /* part_size should be a multiple of PRESENT block size */ + g_assert((part_size & (sizeof(uint64_t) - 1u)) == 0u); + unsigned dword_count = part_size / sizeof(uint64_t); + + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; + + /* source address should be aligned to 64-bit boundary */ + g_assert(((uintptr_t)base & (sizeof(uint64_t) - 1u)) == 0u); + const uint64_t *scrambled = (const uint64_t *)base; + + /* destination address should be aligned to 64-bit boundary */ + g_assert(pctrl->buffer.data != NULL); + uint64_t *clear = (uint64_t *)pctrl->buffer.data; + + g_assert(pctrl->otp_scramble_key); + + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, pctrl->otp_scramble_key); + + trace_ot_otp_unscramble_partition(s->ot_id, + ot_otp_engine_part_name(s, part_ix), + part_ix, part_size); + /* neither the digest block nor the zeroizable block are scrambled */ + for (unsigned dix = 0u; dix < dword_count; dix++) { + ot_present_decrypt(ps, scrambled[dix], &clear[dix]); + } + + ot_present_free(ps); +} + +static void +ot_otp_engine_bufferize_partition(OtOTPEngineState *s, unsigned part_ix) +{ + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + g_assert(pctrl->buffer.data != NULL); + + if (s->part_descs[part_ix].hw_digest) { + pctrl->digest = ot_otp_engine_load_partition_digest(s, part_ix); + } else { + pctrl->digest = 0; + } + + if (s->part_descs[part_ix].secret) { + /* secret partitions need to be unscrambled */ + if (s->blk) { + /* + * nothing to unscramble if no OTP data is loaded + * scrambling keys in this case may not be known + */ + ot_otp_engine_unscramble_partition(s, part_ix); + } + } else { + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_engine_part_data_byte_size(s, part_ix); + + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; + + memcpy(pctrl->buffer.data, base, part_size); + } +} + +static void ot_otp_engine_check_buffered_partition_integrity( + OtOTPEngineState *s, unsigned part_ix) +{ + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + if (pctrl->digest == 0) { + trace_ot_otp_skip_digest(s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix); + pctrl->locked = false; + return; + } + + pctrl->locked = true; + + /* + * digests are always calculated over the original data (scrambled or not) + */ + const uint8_t *part_data = ((const uint8_t *)s->otp->data) + + ot_otp_engine_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_engine_part_data_byte_size(s, part_ix); + + uint64_t digest = + ot_otp_engine_compute_partition_digest(s, part_data, part_size); + + if (digest != pctrl->digest) { + trace_ot_otp_mismatch_digest(s->ot_id, + ot_otp_engine_part_name(s, part_ix), + part_ix, digest, pctrl->digest); + + TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", + ot_otp_engine_part_name(s, part_ix), digest, + ot_otp_hexdump(s, part_data, part_size)); + + pctrl->failed = true; + /* this is a fatal error */ + ot_otp_engine_set_error(s, part_ix, OT_OTP_CHECK_FAIL_ERROR); + /* @todo revert buffered part to default */ + } else { + trace_ot_otp_integrity_report(s->ot_id, + ot_otp_engine_part_name(s, part_ix), + part_ix, "digest OK"); + } +} + +static bool ot_otp_engine_is_backend_writable(OtOTPEngineState *s) +{ + return (s->blk != NULL) && blk_is_writable(s->blk); +} + +static inline int ot_otp_engine_write_backend( + OtOTPEngineState *s, const void *buffer, unsigned offset, size_t size) +{ + /* + * the blk_pwrite API is awful, isolate it so that linter exceptions are + * are not repeated over and over + */ + g_assert(offset + size <= s->otp->size); + + /* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */ + return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer, + /* a bitfield of enum is not an enum item */ + (BdrvRequestFlags)0); + /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ +} + +static void ot_otp_engine_dai_init(OtOTPEngineState *s) +{ + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); +} + +static void ot_otp_engine_dai_set_error(OtOTPEngineState *s, OtOTPError err) +{ + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), err); + + switch (err) { + case OT_OTP_FSM_STATE_ERROR: + case OT_OTP_MACRO_ERROR: + case OT_OTP_MACRO_ECC_UNCORR_ERROR: + DAI_CHANGE_STATE(s, OT_OTP_DAI_ERROR); + break; + default: + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); + break; + } +} + +static void ot_otp_engine_dai_clear_error(OtOTPEngineState *s) +{ + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + ic->update_status_error(OT_OTP_IMPL_IF(s), OT_OTP_STATUS_DAI, false); + + ERR_CODE_PART_REG(s, OTP_ENTRY_DAI(s)) = 0u; +} + +static void ot_otp_engine_dai_read(OtOTPEngineState *s) +{ + if (ot_otp_engine_dai_is_busy(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", + __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); + return; + } + + ot_otp_engine_dai_clear_error(s); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_READ); + + unsigned address = DIRECT_ACCESS_REG(s, ADDRESS); + + int partition = ot_otp_engine_get_part_from_address(s, address); + + if (partition < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: invalid partition address 0x%x\n", __func__, + s->ot_id, address); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + unsigned part_ix = (unsigned)partition; + if (part_ix >= s->part_lc_num) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: life cycle partition cannot be accessed from DAI\n", + __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + const OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + if (pctrl->failed) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix)); + return; + } + + bool is_readable = ot_otp_engine_is_readable(s, part_ix); + bool is_buffered = ot_otp_engine_is_buffered(s, part_ix); + bool is_secret = ot_otp_engine_is_secret(s, part_ix); + bool is_digest = ot_otp_engine_is_part_digest_offset(s, part_ix, address); + bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, address); + + /* in all partitions, the digest and zer fields are always readable. */ + if (!is_digest && !is_zer && !is_readable) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: partition %s @ 0x%04x not readable\n", __func__, + s->ot_id, ot_otp_engine_part_name(s, part_ix), address); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + unsigned part_offset = address - ot_otp_engine_part_data_offset(s, part_ix); + unsigned part_waddr = part_offset >> 2u; + bool do_ecc = + s->part_descs[part_ix].integrity && ot_otp_engine_is_ecc_enabled(s); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_READ_WAIT); + + uint32_t data_lo, data_hi; + unsigned err = 0; + unsigned cell_count = sizeof(uint32_t) + (do_ecc ? sizeof(uint16_t) : 0); + + const uint32_t *data = (const uint32_t *)s->otp->data; + /* parenthesis inform the C linter sizeof() call is valid with 'data */ + data += (ot_otp_engine_part_data_offset(s, part_ix) / sizeof(uint32_t)); + + bool is_wide = is_digest || is_zer || is_secret; + if (is_wide) { + /* 64-bit requests */ + part_waddr &= ~0b1u; + + g_assert((part_waddr + 1u) * sizeof(uint32_t) < + s->part_descs[part_ix].size); + + data_lo = data[part_waddr]; + data_hi = data[part_waddr + 1u]; + + if (do_ecc) { + unsigned ewaddr = address >> 3u; + g_assert(ewaddr < s->otp->ecc_size); + uint32_t ecc = s->otp->ecc[ewaddr]; + if (ot_otp_engine_is_ecc_enabled(s)) { + data_lo = + ot_otp_engine_verify_ecc(s, data_lo, ecc & 0xffffu, &err); + data_hi = + ot_otp_engine_verify_ecc(s, data_hi, ecc >> 16u, &err); + } + } + + cell_count *= 2u; + } else { + /* 32-bit request */ + g_assert(part_waddr * sizeof(uint32_t) < s->part_descs[part_ix].size); + + data_lo = data[part_waddr]; + data_hi = 0u; + + if (do_ecc) { + unsigned ewaddr = address >> 3u; + g_assert(ewaddr < s->otp->ecc_size); + uint32_t ecc = s->otp->ecc[ewaddr]; + if ((address >> 2u) & 1u) { + ecc >>= 16u; + } + if (ot_otp_engine_is_ecc_enabled(s)) { + data_lo = + ot_otp_engine_verify_ecc(s, data_lo, ecc & 0xffffu, &err); + } + cell_count = 4u + 2u; + } else { + cell_count = 4u; + } + } + + if (is_secret && !(is_zer || is_digest)) { + /* + * if the partition is a secret partition, OTP storage is scrambled + * except the digest and the zeroification fields + */ + g_assert(pctrl->otp_scramble_key); + uint64_t tmp_data = ((uint64_t)data_hi << 32u) | data_lo; + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, pctrl->otp_scramble_key); + ot_present_decrypt(ps, tmp_data, &tmp_data); + ot_present_free(ps); + data_lo = (uint32_t)tmp_data; + data_hi = (uint32_t)(tmp_data >> 32u); + } + + DIRECT_ACCESS_REG(s, RDATA_0) = data_lo; + DIRECT_ACCESS_REG(s, RDATA_1) = data_hi; + + if (err) { + OtOTPError otp_err = (err > 1) ? OT_OTP_MACRO_ECC_UNCORR_ERROR : + OT_OTP_MACRO_ECC_CORR_ERROR; + ot_otp_engine_dai_set_error(s, otp_err); + return; + } + + s->dai->partition = partition; + + if (!is_buffered) { + /* fake slow access to OTP cell */ + unsigned access_time = s->be_chars.timings.read_ns * cell_count; + timer_mod(s->dai->delay, + qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + access_time); + } else { + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); + } +} + +static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) +{ + unsigned waddr = (address / sizeof(uint32_t)) & ~1u; + uint32_t *dst = &s->otp->data[waddr]; + + uint32_t dst_lo = dst[0u]; + uint32_t dst_hi = dst[1u]; + + uint32_t lo = DIRECT_ACCESS_REG(s, WDATA_0); + uint32_t hi = DIRECT_ACCESS_REG(s, WDATA_1); + + unsigned part_ix = (unsigned)s->dai->partition; + bool is_secret = ot_otp_engine_is_secret(s, part_ix); + bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, address); + + if (is_secret && !is_zer) { + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + g_assert(pctrl->otp_scramble_key); + + uint64_t data = ((uint64_t)hi << 32u) | lo; + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, pctrl->otp_scramble_key); + ot_present_encrypt(ps, data, &data); + lo = (uint32_t)data; + hi = (uint32_t)(data >> 32u); + } + + if ((dst_lo & ~lo) || (dst_hi & ~hi)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Cannot clear OTP bits\n", + __func__, s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + + dst[0u] |= lo; + dst[1u] |= hi; + + uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, dst, + (unsigned)(offset + + waddr * sizeof(uint32_t)), + sizeof(uint64_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return -1; + } + + if (ot_otp_engine_is_ecc_enabled(s)) { + unsigned ewaddr = waddr >> 1u; + g_assert(ewaddr < s->otp->ecc_size); + uint32_t *edst = &s->otp->ecc[ewaddr]; + + uint32_t ecc_lo = (uint32_t)ot_otp_engine_compute_ecc_u32(lo); + uint32_t ecc_hi = (uint32_t)ot_otp_engine_compute_ecc_u32(hi); + uint32_t ecc = (ecc_hi << 16u) | ecc_lo; + + if (*edst & ~ecc) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Cannot clear OTP ECC bits\n", __func__, + s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + *edst |= ecc; + + offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, edst, + (unsigned)(offset + (waddr << 1u)), + sizeof(uint32_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, + s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return -1; + } + + trace_ot_otp_dai_new_dword_ecc(s->ot_id, + ot_otp_engine_part_name(s, + (unsigned)s->dai + ->partition), + s->dai->partition, *dst, *edst); + } + + return 0; +} + +static int ot_otp_engine_dai_write_u32(OtOTPEngineState *s, unsigned address) +{ + unsigned waddr = address / sizeof(uint32_t); + uint32_t *dst = &s->otp->data[waddr]; + uint32_t data = DIRECT_ACCESS_REG(s, WDATA_0); + + if (*dst & ~data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP bits\n", + __func__, s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + + *dst |= data; + + uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, dst, + (unsigned)(offset + + waddr * sizeof(uint32_t)), + sizeof(uint32_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return -1; + } + + if (ot_otp_engine_is_ecc_enabled(s)) { + g_assert((waddr >> 1u) < s->otp->ecc_size); + uint16_t *edst = &((uint16_t *)s->otp->ecc)[waddr]; + uint16_t ecc = ot_otp_engine_compute_ecc_u32(*dst); + + if (*edst & ~ecc) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: cannot clear OTP ECC bits\n", __func__, + s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + *edst |= ecc; + + offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, edst, + (unsigned)(offset + (address >> 1u)), + sizeof(uint16_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, + s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return -1; + } + + trace_ot_otp_dai_new_word_ecc(s->ot_id, + ot_otp_engine_part_name(s, + (unsigned)s->dai + ->partition), + s->dai->partition, *dst, *edst); + } + + return 0; +} + +static void ot_otp_engine_dai_write(OtOTPEngineState *s) +{ + if (ot_otp_engine_dai_is_busy(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", + __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); + return; + } + + if (!ot_otp_engine_is_backend_writable(s)) { + /* OTP backend missing or read-only; reject any write request */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: OTP backend file is missing or R/O\n", __func__, + s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return; + } + + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE); + + ot_otp_engine_dai_clear_error(s); + + unsigned address = DIRECT_ACCESS_REG(s, ADDRESS); + + int partition = ot_otp_engine_get_part_from_address(s, address); + + if (partition < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: invalid partition address 0x%x\n", __func__, + s->ot_id, address); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + unsigned part_ix = (unsigned)partition; + + if (part_ix >= s->part_lc_num) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: Life cycle partition cannot be accessed from DAI\n", + __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + if (pctrl->failed) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix)); + return; + } + + if (pctrl->locked) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s (%u) is locked\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + if (pctrl->write_lock) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: artition %s (%u) is write locked\n", __func__, + s->ot_id, ot_otp_engine_part_name(s, part_ix), part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + bool is_digest = ot_otp_engine_is_part_digest_offset(s, part_ix, address); + + if (is_digest) { + if (s->part_descs[part_ix].hw_digest) { + /* should have been a Digest command, not a Write command */ + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: partition %s (%u) HW digest cannot be directly " + "written\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + } + + s->dai->partition = partition; + + bool do_ecc = ot_otp_engine_is_ecc_enabled(s); + unsigned cell_count = sizeof(uint32_t); + + bool is_secret = ot_otp_engine_is_secret(s, part_ix); + bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, address); + + bool is_wide = is_secret || is_digest || is_zer; + if (is_wide) { + if (ot_otp_engine_dai_write_u64(s, address)) { + return; + } + cell_count *= 2u; + } else { + if (ot_otp_engine_dai_write_u32(s, address)) { + return; + } + } + + if (do_ecc) { + cell_count += cell_count / 2u; + }; + + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE_WAIT); + + /* fake slow update of OTP cell */ + unsigned update_time = s->be_chars.timings.write_ns * cell_count; + timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); +} + +static void ot_otp_engine_dai_digest(OtOTPEngineState *s) +{ + if (ot_otp_engine_dai_is_busy(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: DAI controller busy: %s\n", + __func__, s->ot_id, DAI_STATE_NAME(s->dai->state)); + return; + } + + if (!ot_otp_engine_is_backend_writable(s)) { + /* OTP backend missing or read-only; reject any write request */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: OTP backend file is missing or R/O\n", __func__, + s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return; + } + + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG_CLR); + + ot_otp_engine_dai_clear_error(s); + + unsigned address = DIRECT_ACCESS_REG(s, ADDRESS); + + int partition = ot_otp_engine_get_part_from_address(s, address); + + if (partition < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Invalid partition address 0x%x\n", __func__, + s->ot_id, address); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + unsigned part_ix = (unsigned)partition; + + if (part_ix >= s->part_lc_num) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: Life cycle partition cannot be accessed from DAI\n", + __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + if (!s->part_descs[part_ix].hw_digest) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Invalid partition, no HW digest on %s (#%u)\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + + if (pctrl->failed) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix)); + return; + } + + if (pctrl->locked) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Partition %s (%u) is locked\n", + __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix), + part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + if (pctrl->write_lock) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Partition %s (%u) is write locked\n", __func__, + s->ot_id, ot_otp_engine_part_name(s, part_ix), part_ix); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); + return; + } + + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG_READ); + + const uint8_t *data = ((const uint8_t *)s->otp->data) + + ot_otp_engine_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_engine_part_data_byte_size(s, part_ix); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG); + + pctrl->buffer.next_digest = + ot_otp_engine_compute_partition_digest(s, data, part_size); + s->dai->partition = partition; + + TRACE_OTP("%s: %s: next digest %016" PRIx64 " from %s\n", __func__, + s->ot_id, pctrl->buffer.next_digest, + ot_otp_hexdump(s, data, part_size)); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG_WAIT); + + /* fake slow update of OTP cell */ + timer_mod(s->dai->delay, + qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_DIGEST_DELAY_NS); +} + +static void ot_otp_engine_dai_write_digest(void *opaque) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + + g_assert((s->dai->partition >= 0) && (s->dai->partition < s->part_count)); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE); + + unsigned part_ix = (unsigned)s->dai->partition; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + unsigned address = s->part_descs[part_ix].digest_offset; + unsigned dwaddr = address / sizeof(uint64_t); + uint64_t *dst = &((uint64_t *)s->otp->data)[dwaddr]; + uint64_t data = pctrl->buffer.next_digest; + pctrl->buffer.next_digest = 0; + + if (*dst & ~data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP data bits\n", + __func__, s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + *dst |= data; + + uintptr_t offset; + offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, dst, (unsigned)(offset + address), + sizeof(uint64_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return; + } + + uint32_t ecc = ot_otp_engine_compute_ecc_u64(data); + + /* dwaddr is 64-bit based, convert it to 32-bit base for ECC */ + unsigned ewaddr = (dwaddr << 1u) / s->otp->ecc_granule; + g_assert(ewaddr < s->otp->ecc_size); + uint32_t *edst = &s->otp->ecc[ewaddr]; + + if (*edst & ~ecc) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: cannot clear OTP ECC bits\n", + __func__, s->ot_id); + ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), + OT_OTP_MACRO_WRITE_BLANK_ERROR); + } + *edst |= ecc; + + offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, edst, + (unsigned)(offset + (ewaddr << 2u)), + sizeof(uint32_t))) { + error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); + return; + } + + trace_ot_otp_dai_new_digest_ecc(s->ot_id, + ot_otp_engine_part_name(s, part_ix), + part_ix, *dst, *edst); + + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE_WAIT); + + /* fake slow update of OTP cell */ + unsigned cell_count = sizeof(uint64_t) + sizeof(uint32_t); + unsigned update_time = s->be_chars.timings.write_ns * cell_count; + timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + update_time); +} + +static void ot_otp_engine_dai_complete(void *opaque) +{ + OtOTPEngineState *s = opaque; + + switch (s->dai->state) { + case OT_OTP_DAI_READ_WAIT: + g_assert(s->dai->partition >= 0); + trace_ot_otp_dai_read(s->ot_id, + ot_otp_engine_part_name(s, (unsigned) + s->dai->partition), + (unsigned)s->dai->partition, + DIRECT_ACCESS_REG(s, RDATA_0), + DIRECT_ACCESS_REG(s, RDATA_1)); + s->dai->partition = -1; + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); + break; + case OT_OTP_DAI_WRITE_WAIT: + g_assert(s->dai->partition >= 0); + s->regs[R_INTR_STATE] |= INTR_OTP_OPERATION_DONE_MASK; + s->dai->partition = -1; + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); + break; + case OT_OTP_DAI_DIG_WAIT: + g_assert(s->dai->partition >= 0); + qemu_bh_schedule(s->dai->digest_bh); + break; + case OT_OTP_DAI_ERROR: + break; + default: + g_assert_not_reached(); + break; + }; +} + +static void ot_otp_engine_lci_init(OtOTPEngineState *s) +{ + LCI_CHANGE_STATE(s, OT_OTP_LCI_IDLE); +} + +static const OtOTPHWCfg *ot_otp_engine_get_hw_cfg(const OtOTPIf *dev) +{ + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); + + return (const OtOTPHWCfg *)s->hw_cfg; +} + +static void ot_otp_engine_request_entropy_bh(void *opaque) +{ + OtOTPEngineState *s = opaque; + + /* + * Use a BH as entropy should be filled in as soon as possible after reset. + * However, as the EDN / OTP reset order is unknown, this initial request + * can only be performed once the reset sequence is over. + */ + if (!s->keygen->edn_sched) { + int rc = ot_edn_request_entropy(s->edn, s->edn_ep); + g_assert(rc == 0); + s->keygen->edn_sched = true; + } +} + +static void +ot_otp_engine_keygen_push_entropy(void *opaque, uint32_t bits, bool fips) +{ + OtOTPEngineState *s = opaque; + (void)fips; + + s->keygen->edn_sched = false; + + if (!ot_fifo32_is_full(&s->keygen->entropy_buf)) { + ot_fifo32_push(&s->keygen->entropy_buf, bits); + } + + bool resched = !ot_fifo32_is_full(&s->keygen->entropy_buf); + + trace_ot_otp_keygen_entropy(s->ot_id, + ot_fifo32_num_used(&s->keygen->entropy_buf), + resched); + + if (resched && !s->keygen->edn_sched) { + qemu_bh_schedule(s->keygen->entropy_bh); + } +} + +static void ot_otp_engine_fake_entropy(OtOTPEngineState *s, unsigned count) +{ + /* + * This part departs from real HW: OTP needs to have bufferized enough + * entropy for any SRAM OTP key request to be successfully completed. + * On real HW, entropy is requested on demand, but in QEMU this very API + * (#get_otp_key) needs to be synchronous, as it should be able to complete + * on SRAM controller I/O request, which is itself fully synchronous. + * When not enough entropy has been initiatially collected, this function + * adds some fake entropy to entropy buffer. The main use case is to enable + * SRAM initialization with random values and does not need to be truly + * secure, while limiting emulation code size and complexity. + */ + + OtOTPKeyGen *kgen = s->keygen; + while (count-- && !ot_fifo32_is_full(&kgen->entropy_buf)) { + ot_fifo32_push(&kgen->entropy_buf, ot_prng_random_u32(kgen->prng)); + } +} + +/* + * See + * https://opentitan.org/book/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/ + * theory_of_operation.html#scrambling-datapath + * + * The `fetch_nonce_entropy` field refers to the fetching of additional + * entropy for the nonce output. + * + * The `ingest_entropy` field indicates whether an additional 128 bit entropy + * block should be ingested after the seed. That is, `true` will + * derive an ephemeral scrambling key (path C) and `false` will derive a static + * scrambling key (path D). + * + * Will fake entropy if there is not enough available, rather than waiting. + */ +static void ot_otp_engine_generate_scrambling_key( + OtOTPEngineState *s, OtOTPKey *key, OtOTPKeyType type, uint64_t k_iv, + const uint8_t *k_const, bool fetch_nonce_entropy, bool ingest_entropy) +{ + g_assert(type < OT_OTP_KEY_COUNT); + g_assert(key->seed_size < OT_OTP_SEED_MAX_SIZE); + g_assert(key->nonce_size < OT_OTP_NONCE_MAX_SIZE); + + g_assert(key->seed_size % sizeof(uint32_t) == 0u); + g_assert(key->nonce_size % sizeof(uint32_t) == 0u); + unsigned seed_words = key->seed_size / sizeof(uint32_t); + unsigned nonce_words = key->nonce_size / sizeof(uint32_t); + unsigned scramble_blocks = key->seed_size / sizeof(uint64_t); + + OtFifo32 *entropy = &s->keygen->entropy_buf; + + /* for QEMU emulation, fake entropy instead of waiting */ + unsigned avail_entropy = ot_fifo32_num_used(entropy); + unsigned needed_entropy = 0u; + needed_entropy += fetch_nonce_entropy ? nonce_words : 0u; + needed_entropy += ingest_entropy ? (seed_words * scramble_blocks) : 0u; + if (avail_entropy < needed_entropy) { + unsigned count = needed_entropy - avail_entropy; + error_report("%s: %s: not enough entropy for key %d, fake %u words", + __func__, s->ot_id, type, count); + ot_otp_engine_fake_entropy(s, count); + } + + if (fetch_nonce_entropy) { + /* fill in the nonce using entropy */ + g_assert(ot_fifo32_num_used(entropy) >= nonce_words); + for (unsigned ix = 0; ix < nonce_words; ix++) { + stl_le_p(&key->nonce[ix * sizeof(uint32_t)], + ot_fifo32_pop(entropy)); + } + } + + OtPresentState *ps = s->keygen->present; + + /* obtain the key seed from the OTP SECRET partition(s) */ + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + g_assert(ic->key_seeds); + unsigned part_ix = ic->key_seeds[type].partition; + /* + * assume the key seeds are never stored in the first partition. + * if partition is zero, the slot contains no key seed. + * there is no reason for this API client to request a key seed which is + * not available on the current Top. + */ + g_assert(ic->key_seeds[type].partition); + unsigned key_offset = ic->key_seeds[type].offset; + g_assert(part_ix < s->part_count); + g_assert(key_offset <= s->part_descs[part_ix].size - key->seed_size); + + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + g_assert(ot_otp_engine_is_buffered(s, part_ix)); + const uint32_t *key_seed = &pctrl->buffer.data[key_offset]; + + /* check the key seed's validity */ + key->seed_valid = pctrl->locked && !pctrl->failed; + + uint32_t *ephemeral_entropy = g_new0(uint32_t, seed_words); + for (unsigned rix = 0; rix < scramble_blocks; rix++) { + /* compress the IV state with the OTP key seed */ + uint64_t data = k_iv; + ot_present_init(ps, (const uint8_t *)key_seed); + ot_present_encrypt(ps, data, &data); + + if (ingest_entropy) { + /* ephemeral keys ingest different entropy each round */ + g_assert(ot_fifo32_num_used(entropy) >= seed_words); + for (unsigned ix = 0; ix < seed_words; ix++) { + ephemeral_entropy[ix] = ot_fifo32_pop(entropy); + } + + ot_present_init(ps, (uint8_t *)&ephemeral_entropy[0]); + ot_present_encrypt(ps, data, &data); + } + + /* compress with the finalization constant*/ + ot_present_init(ps, k_const); + ot_present_encrypt(ps, data, &data); + + /* write back to the key */ + for (unsigned ix = 0; ix < sizeof(uint64_t); ix++) { + unsigned seed_byte = rix * sizeof(uint64_t) + ix; + key->seed[seed_byte] = (uint8_t)(data >> (ix * 8u)); + } + } + g_free(ephemeral_entropy); + + trace_ot_otp_key_generated(s->ot_id, type); + + if (needed_entropy) { + /* some entropy bits have been used, refill the buffer */ + qemu_bh_schedule(s->keygen->entropy_bh); + } +} + +static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, + OtOTPKey *key) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + trace_ot_otp_get_otp_key(s->ot_id, type); + + /* reference: req_bundles in OpenTitan rtl/otp_ctrl_kdi.sv */ + const uint64_t *iv; + const uint8_t *constant; + bool ingest_entropy; + switch (type) { + case OT_OTP_KEY_FLASH_DATA: + if (!ic->has_flash_support) { + iv = NULL; + break; + } + key->seed_size = (uint8_t)ic->key_seeds[type].size; + key->nonce_size = key->seed_size; + iv = &s->flash_data_iv; + constant = s->flash_data_const; + ingest_entropy = false; + break; + case OT_OTP_KEY_FLASH_ADDR: + if (!ic->has_flash_support) { + iv = NULL; + break; + } + key->seed_size = (uint8_t)ic->key_seeds[type].size; + key->nonce_size = 0u; + iv = &s->flash_addr_iv; + constant = s->flash_addr_const; + ingest_entropy = false; + break; + case OT_OTP_KEY_OTBN: + key->seed_size = (uint8_t)ic->key_seeds[type].size; + key->nonce_size = key->seed_size / 2u; + iv = &s->sram_iv; + constant = s->sram_const; + ingest_entropy = true; + break; + case OT_OTP_KEY_SRAM: + key->seed_size = (uint8_t)ic->key_seeds[type].size; + key->nonce_size = key->seed_size; + iv = &s->sram_iv; + constant = s->sram_const; + ingest_entropy = true; + break; + default: + iv = NULL; + ingest_entropy = false; + break; + } + + if (!iv) { + error_report("%s: %s: invalid OTP key type: %d", __func__, s->ot_id, + type); + g_assert_not_reached(); + } + + g_assert(key->seed_size <= sizeof(s->scrmbl_key_init->key)); + g_assert(key->nonce_size <= sizeof(s->scrmbl_key_init->nonce)); + memcpy(key->seed, s->scrmbl_key_init->key, key->seed_size); + memcpy(key->nonce, s->scrmbl_key_init->nonce, key->nonce_size); + key->seed_valid = false; + ot_otp_engine_generate_scrambling_key(s, key, type, *iv, constant, true, + ingest_entropy); +} + +static bool ot_otp_engine_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, + const uint16_t *lc_state, + ot_otp_program_ack_fn ack, void *opaque) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + OtOTPLCIController *lci = s->lci; + + switch (lci->state) { + case OT_OTP_LCI_IDLE: + case OT_OTP_LCI_ERROR: + /* error case is handled asynchronously */ + g_assert(!(lci->ack_fn || lci->ack_data)); + break; + case OT_OTP_LCI_WRITE: + case OT_OTP_LCI_WRITE_WAIT: + /* another LC programming request is on-going */ + return false; + case OT_OTP_LCI_RESET: + /* cannot reach this point if PwrMgr init has been executed */ + default: + g_assert_not_reached(); + break; + } + + lci->ack_fn = ack; + lci->ack_data = opaque; + + if (lci->state == OT_OTP_LCI_IDLE) { + unsigned hpos = 0; + memcpy(&lci->data[hpos], lc_tcount, LC_TRANSITION_CNT_SIZE); + hpos += LC_TRANSITION_CNT_SIZE / sizeof(uint16_t); + memcpy(&lci->data[hpos], lc_state, LC_STATE_SIZE); + hpos += LC_STATE_SIZE / sizeof(uint16_t); + g_assert(hpos == s->part_descs[s->part_lc_num].size / sizeof(uint16_t)); + + /* current position in LC buffer to write to backend */ + lci->hpos = 0u; + } + + /* + * schedule even if LCI FSM is already in error to report the issue + * asynchronously + */ + timer_mod(lci->prog_delay, + qemu_clock_get_ns(OT_OTP_HW_CLOCK) + LCI_PROG_SCHED_NS); + + return true; +} + +static void ot_otp_engine_lci_write_complete(OtOTPEngineState *s, bool success) +{ + OtOTPLCIController *lci = s->lci; + + if (lci->hpos) { + /* + * if the LC partition has been modified somehow, even if the request + * has failed, update the backend file + */ + const OtOTPPartDesc *lcdesc = &s->part_descs[s->part_lc_num]; + unsigned lc_off = lcdesc->offset / sizeof(uint32_t); + uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, &s->otp->data[lc_off], + (unsigned)(offset + lcdesc->offset), + lcdesc->size)) { + error_report("%s: %s: cannot update OTP backend", __func__, + s->ot_id); + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + } + } + if (ot_otp_engine_is_ecc_enabled(s)) { + offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; + if (ot_otp_engine_write_backend(s, + &((uint16_t *)s->otp->ecc)[lc_off], + (unsigned)(offset + + (lcdesc->offset >> 1u)), + lcdesc->size >> 1u)) { + error_report("%s: %s: cannot update OTP backend", __func__, + s->ot_id); + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + } + } + } + } + + g_assert(lci->ack_fn); + ot_otp_program_ack_fn ack_fn = lci->ack_fn; + void *ack_data = lci->ack_data; + lci->ack_fn = NULL; + lci->ack_data = NULL; + lci->hpos = 0u; + + if (!success && lci->error != OT_OTP_NO_ERROR) { + ot_otp_engine_set_error(s, s->part_lc_num, lci->error); + } + + (*ack_fn)(ack_data, success); +} + +static void ot_otp_engine_lci_write_word(void *opaque) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPLCIController *lci = s->lci; + const OtOTPPartDesc *lcdesc = &s->part_descs[s->part_lc_num]; + + /* should not be called if already in error */ + if (lci->state == OT_OTP_LCI_ERROR) { + lci->error = OT_OTP_FSM_STATE_ERROR; + ot_otp_engine_lci_write_complete(s, false); + return; + } + + if (!ot_otp_engine_is_backend_writable(s)) { + /* OTP backend missing or read-only; reject any write request */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: OTP backend file is missing or R/O\n", __func__, + s->ot_id); + lci->error = OT_OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + ot_otp_engine_lci_write_complete(s, false); + /* abort immediately */ + return; + } + + if (lci->hpos >= lcdesc->size / sizeof(uint16_t)) { + /* the whole LC partition has been updated */ + if (lci->error == OT_OTP_NO_ERROR) { + LCI_CHANGE_STATE(s, OT_OTP_LCI_IDLE); + ot_otp_engine_lci_write_complete(s, true); + } else { + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); + ot_otp_engine_lci_write_complete(s, false); + } + return; + } + + LCI_CHANGE_STATE(s, OT_OTP_LCI_WRITE); + + uint16_t *lc_dst = + (uint16_t *)&s->otp->data[lcdesc->offset / sizeof(uint32_t)]; + + uint16_t cur_val = lc_dst[lci->hpos]; + uint16_t new_val = lci->data[lci->hpos]; + + trace_ot_otp_lci_write(s->ot_id, lci->hpos, cur_val, new_val); + + if (cur_val & ~new_val) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: cannot clear OTP bits @ %u: 0x%04x / 0x%04x\n", + __func__, s->ot_id, lci->hpos, cur_val, new_val); + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_WRITE_BLANK_ERROR; + } + /* + * "Note that if errors occur, we aggregate the error code but still + * attempt to program all remaining words. This is done to ensure that + * a life cycle state with ECC correctable errors in some words can + * still be scrapped." + */ + } + + lc_dst[lci->hpos] |= new_val; + + if (ot_otp_engine_is_ecc_enabled(s)) { + uint8_t *lc_edst = + (uint8_t *)&s->otp->ecc[lcdesc->offset / (2u * sizeof(uint32_t))]; + uint8_t cur_ecc = lc_edst[lci->hpos]; + uint8_t new_ecc = ot_otp_engine_compute_ecc_u16(lc_dst[lci->hpos]); + + trace_ot_otp_lci_write_ecc(s->ot_id, lci->hpos, cur_ecc, new_ecc); + + if (cur_ecc & ~new_ecc) { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: %s: cannot clear OTP ECC @ %u: 0x%02x / 0x%02x\n", + __func__, s->ot_id, lci->hpos, cur_ecc, new_ecc); + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_WRITE_BLANK_ERROR; + } + } + + lc_edst[lci->hpos] |= new_ecc; + } + + lci->hpos += 1u; + + unsigned update_time = s->be_chars.timings.write_ns * sizeof(uint16_t); + timer_mod(lci->prog_delay, + qemu_clock_get_ns(OT_OTP_HW_CLOCK) + update_time); + + LCI_CHANGE_STATE(s, OT_OTP_LCI_WRITE_WAIT); +} + +static void ot_otp_engine_pwr_otp_req(void *opaque, int n, int level) +{ + OtOTPEngineState *s = opaque; + + g_assert(n == 0); + + if (level) { + trace_ot_otp_pwr_otp_req(s->ot_id, "signaled"); + qemu_bh_schedule(s->pwr_otp_bh); + } +} + +static void ot_otp_engine_pwr_load(OtOTPEngineState *s) +{ + /* + * HEADER_FORMAT + * + * | magic | 4 char | "vOFTP" | + * | hlength | uint32_t | count of header bytes after this point | + * | version | uint32_t | version of the header (v2) | + * | eccbits | uint16_t | ECC size in bits | + * | eccgran | uint16_t | ECC granule | + * | dlength | uint32_t | count of data bytes (% uint64_t) | + * | elength | uint32_t | count of ecc bytes (% uint64_t) | + * | -------- | ---------- | only in V2 | + * | dig_iv | 8 uint8_t | Present digest initialization vector | + * | dig_iv | 16 uint8_t | Present digest initialization vector | + */ + + struct otp_header { + char magic[4]; + uint32_t hlength; + uint32_t version; + uint16_t eccbits; + uint16_t eccgran; + uint32_t data_len; + uint32_t ecc_len; + /* added in V2 */ + uint8_t digest_iv[8u]; + uint8_t digest_constant[16u]; + }; + + static_assert(sizeof(struct otp_header) == 48u, "Invalid header size"); + + /* data following header should always be 64-bit aligned */ + static_assert((sizeof(struct otp_header) % sizeof(uint64_t)) == 0, + "invalid header definition"); + + size_t header_size = sizeof(struct otp_header); + size_t data_size = 0u; + size_t ecc_size = 0u; + + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + size_t psize = (size_t)s->part_descs[part_ix].size; + size_t dsize = ROUND_UP(psize, sizeof(uint64_t)); + data_size += dsize; + /* up to 1 ECC byte for 2 data bytes */ + ecc_size += DIV_ROUND_UP(dsize, 2u); + } + size_t otp_size = header_size + data_size + ecc_size; + + otp_size = ROUND_UP(otp_size, 4096u); + + OtOTPStorage *otp = s->otp; + + /* always allocates the requested size even if blk is NULL */ + if (!otp->storage) { + /* only allocated once on PoR */ + otp->storage = blk_blockalign(s->blk, otp_size); + } + + uintptr_t base = (uintptr_t)otp->storage; + g_assert(!(base & (sizeof(uint64_t) - 1u))); + + memset(otp->storage, 0, otp_size); + + otp->data = (uint32_t *)(base + sizeof(struct otp_header)); + otp->ecc = (uint32_t *)(base + sizeof(struct otp_header) + data_size); + otp->ecc_bit_count = 0u; + otp->ecc_granule = 0u; + + if (s->blk) { + /* NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) */ + int rc = blk_pread(s->blk, 0, (int64_t)otp_size, otp->storage, + (BdrvRequestFlags)0); + /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ + if (rc < 0) { + error_setg(&error_fatal, + "%s: failed to read the initial OTP content %zu bytes: " + "%d", + s->ot_id, otp_size, rc); + return; + } + + const struct otp_header *otp_hdr = (const struct otp_header *)base; + + if (memcmp(otp_hdr->magic, "vOTP", sizeof(otp_hdr->magic)) != 0) { + error_setg(&error_fatal, "%s: OTP file is not a valid OTP backend", + s->ot_id); + return; + } + if (otp_hdr->version != 1u && otp_hdr->version != 2u) { + error_setg(&error_fatal, "%s: OTP file version %u is not supported", + s->ot_id, otp_hdr->version); + return; + } + + uintptr_t data_offset = otp_hdr->hlength + 8u; /* magic & length */ + uintptr_t ecc_offset = data_offset + otp_hdr->data_len; + + otp->data = (uint32_t *)(base + data_offset); + otp->ecc = (uint32_t *)(base + ecc_offset); + otp->ecc_bit_count = otp_hdr->eccbits; + otp->ecc_granule = otp_hdr->eccgran; + + if (otp->ecc_bit_count != 6u || !ot_otp_engine_is_ecc_enabled(s)) { + qemu_log_mask(LOG_UNIMP, + "%s: %s: support for ECC %u/%u not implemented\n", + __func__, s->ot_id, otp->ecc_granule, + otp->ecc_bit_count); + } + + bool write = blk_supports_write_perm(s->blk); + trace_ot_otp_load_backend(s->ot_id, otp_hdr->version, + write ? "R/W" : "R/O", otp->ecc_bit_count, + otp->ecc_granule); + + if (otp_hdr->version == 2u) { + /* + * Version 2 is deprecated and digest const/IV are now ignored. + * Nonetheless, keep checking for inconsistencies. + */ + if (s->digest_iv != ldq_le_p(otp_hdr->digest_iv)) { + error_report("%s: %s: OTP file digest IV mismatch", __func__, + s->ot_id); + } + if (memcmp(s->digest_const, otp_hdr->digest_constant, + sizeof(s->digest_const)) != 0) { + error_report("%s: %s: OTP file digest const mismatch", __func__, + s->ot_id); + } + } + } + + otp->data_size = data_size; + otp->ecc_size = ecc_size; + otp->size = otp_size; +} +static void ot_otp_engine_pwr_initialize_partitions(OtOTPEngineState *s) +{ + for (unsigned ix = 0; ix < s->part_count; ix++) { + /* sanity check: all secret partitions are also buffered */ + g_assert(!s->part_descs[ix].secret || s->part_descs[ix].buffered); + + if (ot_otp_engine_is_ecc_enabled(s) && s->part_descs[ix].integrity) { + if (ot_otp_engine_apply_ecc(s, ix)) { + continue; + } + } + + if (s->part_descs[ix].sw_digest) { + s->part_ctrls[ix].digest = ot_otp_engine_get_part_digest(s, ix); + s->part_ctrls[ix].locked = s->part_ctrls[ix].digest != 0; + continue; + } + + if (s->part_descs[ix].buffered) { + ot_otp_engine_bufferize_partition(s, ix); + if (s->part_descs[ix].hw_digest) { + ot_otp_engine_check_buffered_partition_integrity(s, ix); + } + continue; + } + } +} + +static void ot_otp_engine_pwr_otp_bh(void *opaque) +{ + OtOTPEngineState *s = opaque; + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + /* + * This sequence is triggered from the Power Manager, in the early boot + * sequence while the OT IPs are maintained in reset. + * This means that all ot_otp_engine_pwr_* functions are called before the + * OTP IP is released from reset. + * + * The QEMU reset is not a 1:1 mapping to the actual HW. + */ + trace_ot_otp_pwr_otp_req(s->ot_id, "initialize"); + + /* load OTP data from OTP back-end file */ + ot_otp_engine_pwr_load(s); + /* check ECC, digests, configure locks and bufferize partitions */ + ot_otp_engine_pwr_initialize_partitions(s); + + + if (ic->signal_pwr_sequence) { + ic->signal_pwr_sequence(OT_OTP_IMPL_IF(s)); + } + + /* initialize direct access interface */ + ot_otp_engine_dai_init(s); + /* initialize LC controller interface */ + ot_otp_engine_lci_init(s); + + trace_ot_otp_pwr_otp_req(s->ot_id, "done"); + + /* toggle OTP completion to signal the power manager OTP init is complete */ + ibex_irq_set(&s->pwc_otp_rsp, 1); + ibex_irq_set(&s->pwc_otp_rsp, 0); +} + +static void ot_otp_engine_configure_scrmbl_key(OtOTPEngineState *s) +{ + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + if (!s->scrmbl_key_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "scrmbl_key"); + return; + } + + size_t sram_key_bytes = SRAM_KEY_BYTES(ic); + size_t sram_nonce_bytes = SRAM_NONCE_BYTES(ic); + size_t len = strlen(s->scrmbl_key_xstr); + if (len != (size_t)(sram_key_bytes + sram_nonce_bytes) * 2u) { + error_setg(&error_fatal, "%s: %s invalid scrmbl_key length\n", __func__, + s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->scrmbl_key_init->key, + &s->scrmbl_key_xstr[0], sram_key_bytes, false, + false)) { + error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->scrmbl_key_init->nonce, + &s->scrmbl_key_xstr[sram_key_bytes * 2u], + sram_nonce_bytes, false, true)) { + error_setg(&error_fatal, "%s: %s unable to parse scrmbl_key\n", + __func__, s->ot_id); + return; + } +} + +static void ot_otp_engine_configure_digest(OtOTPEngineState *s) +{ + memset(s->digest_const, 0, sizeof(s->digest_const)); + s->digest_iv = 0ull; + + if (!s->digest_const_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "digest_const"); + return; + } + + if (!s->digest_iv_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "digest_iv"); + return; + } + + size_t len; + + len = strlen(s->digest_const_xstr); + if (len != sizeof(s->digest_const) * 2u) { + error_setg(&error_fatal, "%s: %s invalid digest_const length\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->digest_const, s->digest_const_xstr, + sizeof(s->digest_const), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse digest_const\n", + __func__, s->ot_id); + return; + } + + uint8_t digest_iv[sizeof(uint64_t)]; + + len = strlen(s->digest_iv_xstr); + if (len != sizeof(digest_iv) * 2u) { + error_setg(&error_fatal, "%s: %s invalid digest_iv length\n", __func__, + s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(digest_iv, s->digest_iv_xstr, + sizeof(digest_iv), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse digest_iv\n", __func__, + s->ot_id); + return; + } + + s->digest_iv = ldq_le_p(digest_iv); +} + +static void ot_otp_engine_configure_flash(OtOTPEngineState *s) +{ + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); + + if (!ic->has_flash_support) { + return; + } + + memset(s->flash_data_const, 0, sizeof(s->flash_data_const)); + memset(s->flash_addr_const, 0, sizeof(s->flash_addr_const)); + s->flash_data_iv = 0ull; + s->flash_addr_iv = 0ull; + + if (!s->flash_data_const_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "flash_data_const"); + return; + } + if (!s->flash_addr_const_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "flash_addr_const"); + return; + } + if (!s->flash_data_iv_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "flash_data_iv"); + return; + } + if (!s->flash_addr_iv_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "flash_addr_iv"); + return; + } + + size_t len; + + len = strlen(s->flash_data_const_xstr); + if (len != sizeof(s->flash_data_const) * 2u) { + error_setg(&error_fatal, "%s: %s invalid flash_data_const length\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->flash_data_const, s->flash_data_const_xstr, + sizeof(s->flash_data_const), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse flash_data_const\n", + __func__, s->ot_id); + return; + } + + len = strlen(s->flash_addr_const_xstr); + if (len != sizeof(s->flash_addr_const) * 2u) { + error_setg(&error_fatal, "%s: %s invalid flash_addr_const length\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->flash_addr_const, s->flash_addr_const_xstr, + sizeof(s->flash_addr_const), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse flash_addr_const\n", + __func__, s->ot_id); + return; + } + + uint8_t flash_data_iv[sizeof(uint64_t)]; + + len = strlen(s->flash_data_iv_xstr); + if (len != sizeof(flash_data_iv) * 2u) { + error_setg(&error_fatal, "%s: %s invalid flash_data_iv length\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(flash_data_iv, s->flash_data_iv_xstr, + sizeof(flash_data_iv), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse flash_data_iv\n", + __func__, s->ot_id); + return; + } + + s->flash_data_iv = ldq_le_p(flash_data_iv); + + uint8_t flash_addr_iv[sizeof(uint64_t)]; + + len = strlen(s->flash_addr_iv_xstr); + if (len != sizeof(flash_addr_iv) * 2u) { + error_setg(&error_fatal, "%s: %s invalid flash_addr_iv length\n", + __func__, s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(flash_addr_iv, s->flash_addr_iv_xstr, + sizeof(flash_addr_iv), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse flash_addr_iv\n", + __func__, s->ot_id); + return; + } + + s->flash_addr_iv = ldq_le_p(flash_addr_iv); +} + +static void ot_otp_engine_configure_sram(OtOTPEngineState *s) +{ + memset(s->sram_const, 0, sizeof(s->sram_const)); + s->sram_iv = 0ull; + + if (!s->sram_const_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "sram_const"); + return; + } + + if (!s->sram_iv_xstr) { + trace_ot_otp_configure_missing(s->ot_id, "sram_iv"); + return; + } + + size_t len; + + len = strlen(s->sram_const_xstr); + if (len != sizeof(s->sram_const) * 2u) { + error_setg(&error_fatal, "%s: %s invalid sram_const length\n", __func__, + s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(s->sram_const, s->sram_const_xstr, + sizeof(s->sram_const), true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse sram_const\n", + __func__, s->ot_id); + return; + } + + uint8_t sram_iv[sizeof(uint64_t)]; + + len = strlen(s->sram_iv_xstr); + if (len != sizeof(sram_iv) * 2u) { + error_setg(&error_fatal, "%s: %s invalid sram_iv length\n", __func__, + s->ot_id); + return; + } + + if (ot_common_parse_hexa_str(sram_iv, s->sram_iv_xstr, sizeof(sram_iv), + true, true)) { + error_setg(&error_fatal, "%s: %s unable to parse sram_iv\n", __func__, + s->ot_id); + return; + } + + s->sram_iv = ldq_le_p(sram_iv); +} + +static void ot_otp_engine_configure_part_scramble_keys(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + unsigned secret_ix = 0; + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { + continue; + } + + /* + * the property exists, but QEMU only assigns a value when the property + * is given a value. + */ + if (!s->otp_scramble_key_xstrs[secret_ix]) { + /* if OTP data is loaded, unscrambling keys are mandatory */ + if (s->blk) { + error_setg(&error_fatal, + "%s: %s Missing OTP scrambling key for part %s (%u)", + __func__, s->ot_id, + ot_otp_engine_part_name(s, part_ix), part_ix); + return; + } + secret_ix += 1u; + continue; + } + + size_t len = strlen(s->otp_scramble_key_xstrs[secret_ix]); + if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { + error_setg( + &error_fatal, + "%s: %s Invalid OTP scrambling key length %zu for part %s (%u)", + __func__, s->ot_id, len, ot_otp_engine_part_name(s, part_ix), + part_ix); + return; + } + + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + pctrl->otp_scramble_key = g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); + + if (ot_common_parse_hexa_str(pctrl->otp_scramble_key, + s->otp_scramble_key_xstrs[secret_ix], + OTP_SCRAMBLING_KEY_BYTES, true, true)) { + error_setg(&error_fatal, + "%s: %s unable to parse otp_scramble_keys[%u] for %s", + __func__, s->ot_id, part_ix, + ot_otp_engine_part_name(s, part_ix)); + return; + } + + TRACE_OTP("otp_scramble_keys[%s] %s", + ot_otp_engine_part_name(s, part_ix), + ot_otp_hexdump(s, pctrl->otp_scramble_key, + OTP_SCRAMBLING_KEY_BYTES)); + + secret_ix += 1u; + } +} + +static void ot_otp_engine_add_scramble_key_props(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + unsigned secret_part_count = 0; + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (s->part_descs[part_ix].secret) { + secret_part_count++; + } + } + + s->otp_scramble_key_xstrs = g_new0(char *, secret_part_count); + unsigned secret_ix = 0u; + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { + continue; + } + + Property *prop = g_new0(Property, 1u); + + /* + * Assumes secret partitions are sequentially ordered and named + * SECRET0, SECRET1, SECRET2, ... + */ + prop->name = g_strdup_printf("secret%u_scramble_key", secret_ix); + prop->info = &qdev_prop_string; + /* + * Property stores the address of the stored string as a relative offset + * from the parent address + */ + prop->offset = + (intptr_t)&s->otp_scramble_key_xstrs[secret_ix] - (intptr_t)s; + + object_property_add(OBJECT(s), prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, prop); + + secret_ix++; + } +} + +static void ot_otp_engine_configure_inv_default_parts(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + if (!s->inv_default_part_xstrs[part_ix]) { + continue; + } + + const OtOTPPartDesc *part = &s->part_descs[part_ix]; + + size_t len; + + len = strlen(s->inv_default_part_xstrs[part_ix]); + if (len != part->size * 2u) { + error_setg(&error_fatal, + "%s: %s invalid inv_default_part[%u] length\n", __func__, + s->ot_id, part_ix); + return; + } + + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + pctrl->inv_default_data = g_new0(uint8_t, part->size + 1u); + if (ot_common_parse_hexa_str(pctrl->inv_default_data, + s->inv_default_part_xstrs[part_ix], + part->size, false, true)) { + error_setg(&error_fatal, + "%s: %s unable to parse inv_default_part[%u]\n", + __func__, s->ot_id, part_ix); + return; + } + + TRACE_OTP("inv_default_part[%s] %s", + ot_otp_engine_part_name(s, part_ix), + ot_otp_hexdump(s, pctrl->inv_default_data, part->size)); + } +} + +static void ot_otp_engine_add_inv_def_props(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + s->inv_default_part_xstrs = g_new0(char *, s->part_count); + + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].buffered) { + continue; + } + + Property *prop = g_new0(Property, 1u); + + prop->name = g_strdup_printf("inv_default_part_%u", part_ix); + prop->info = &qdev_prop_string; + /* + * Property stores the address of the stored string as a relative offset + * from the parent address + */ + prop->offset = + (intptr_t)&s->inv_default_part_xstrs[part_ix] - (intptr_t)s; + + object_property_add(OBJECT(s), prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, prop); + } +} + +static Property ot_otp_engine_properties[] = { + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOTPEngineState, ot_id), + DEFINE_PROP_DRIVE("drive", OtOTPEngineState, blk), + DEFINE_PROP_LINK("backend", OtOTPEngineState, otp_backend, + TYPE_OT_OTP_BE_IF, OtOtpBeIf *), + DEFINE_PROP_LINK("edn", OtOTPEngineState, edn, TYPE_OT_EDN, OtEDNState *), + DEFINE_PROP_UINT8("edn-ep", OtOTPEngineState, edn_ep, UINT8_MAX), + DEFINE_PROP_STRING("scrmbl_key", OtOTPEngineState, scrmbl_key_xstr), + DEFINE_PROP_STRING("digest_const", OtOTPEngineState, digest_const_xstr), + DEFINE_PROP_STRING("digest_iv", OtOTPEngineState, digest_iv_xstr), + DEFINE_PROP_STRING("sram_const", OtOTPEngineState, sram_const_xstr), + DEFINE_PROP_STRING("sram_iv", OtOTPEngineState, sram_iv_xstr), + DEFINE_PROP_STRING("flash_data_const", OtOTPEngineState, + flash_data_const_xstr), + DEFINE_PROP_STRING("flash_data_iv", OtOTPEngineState, flash_data_iv_xstr), + DEFINE_PROP_STRING("flash_addr_const", OtOTPEngineState, + flash_addr_const_xstr), + DEFINE_PROP_STRING("flash_addr_iv", OtOTPEngineState, flash_addr_iv_xstr), + DEFINE_PROP_BOOL("fatal_escalate", OtOTPEngineState, fatal_escalate, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ot_otp_engine_reset_enter(Object *obj, ResetType type) +{ + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(obj); + OtOTPEngineState *s = OT_OTP_ENGINE(obj); + + /* + * Note: beware of the special reset sequence for the OTP controller, + * see comments from ot_otp_engine_pwr_otp_bh, as this very QEMU reset may + * be called after ot_otp_engine_pwr_otp_bh is invoked, hereby changing the + * usual realize-reset sequence. + * + * File back-end storage (loading) is processed from + * the ot_otp_engine_pwr_otp_bh handler, to ensure data is reloaded from the + * backend on each reset, prior to this very reset function. This reset + * function should not alter the storage content. + * + * Ideally the OTP reset functions should be decoupled from the regular + * IP reset, which are exercised automatically from the SoC, since all the + * OT SysBysDevice IPs are connected to the private system bus of the Ibex. + * This is by-design in QEMU. The reset management is already far too + * complex to create a special case for the OTP. Keep in mind that the OTP + * reset_enter/reset_exit functions are QEMU regular reset functions called + * as part of the private bus reset and do not represent the actual OTP HW + * reset. Part of this reset is handled in the Power Manager handler. + */ + trace_ot_otp_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + qemu_bh_cancel(s->dai->digest_bh); + qemu_bh_cancel(s->lc_broadcast.bh); + qemu_bh_cancel(s->pwr_otp_bh); + + timer_del(s->dai->delay); + timer_del(s->lci->prog_delay); + qemu_bh_cancel(s->keygen->entropy_bh); + s->keygen->edn_sched = false; + + memset(s->hw_cfg, 0, sizeof(*s->hw_cfg)); + + /* ensure OTP implementation has defined all the required values */ + g_assert(s->reg_offset.dai_base > R_ALERT_TEST); + g_assert(s->reg_offset.err_code_base > R_ALERT_TEST); + g_assert(s->reg_offset.read_lock_base > R_ALERT_TEST); + + s->alert_bm = 0; + + s->lc_broadcast.current_level = 0u; + s->lc_broadcast.level = 0u; + s->lc_broadcast.signal = 0u; + + ot_otp_engine_update_irqs(s); + ot_otp_engine_update_alerts(s); + ibex_irq_set(&s->pwc_otp_rsp, 0); + + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + /* @todo initialize with actual default partition data once known */ + if (s->part_descs[part_ix].buffered) { + s->part_ctrls[part_ix].state.b = OT_OTP_BUF_IDLE; + } else { + s->part_ctrls[part_ix].state.u = OT_OTP_UNBUF_IDLE; + continue; + } + unsigned part_size = ot_otp_engine_part_data_byte_size(s, part_ix); + memset(s->part_ctrls[part_ix].buffer.data, 0, part_size); + s->part_ctrls[part_ix].digest = 0; + if (s->part_descs[part_ix].iskeymgr_creator || + s->part_descs[part_ix].iskeymgr_owner) { + s->part_ctrls[part_ix].read_lock = true; + s->part_ctrls[part_ix].write_lock = true; + } + } + DAI_CHANGE_STATE(s, OT_OTP_DAI_RESET); + LCI_CHANGE_STATE(s, OT_OTP_LCI_RESET); +} + +static void ot_otp_engine_reset_exit(Object *obj, ResetType type) +{ + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(obj); + OtOTPEngineState *s = OT_OTP_ENGINE(obj); + + trace_ot_otp_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); + memcpy(&s->be_chars, bec->get_characteristics(s->otp_backend), + sizeof(OtOtpBeCharacteristics)); + + ot_edn_connect_endpoint(s->edn, s->edn_ep, + &ot_otp_engine_keygen_push_entropy, s); + + qemu_bh_schedule(s->keygen->entropy_bh); +} + +static void ot_otp_engine_realize(DeviceState *dev, Error **errp) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + (void)errp; + + g_assert(s->ot_id); + g_assert(s->otp_backend); + + /* ensure OTP implementation has initialized these values */ + g_assert(s->regs != NULL); + + /* + * Set the OTP drive's permissions now during realization. We can't leave it + * until reset because QEMU might have `-daemonize`d and changed directory, + * invalidating the filesystem path to the OTP image. + */ + if (s->blk) { + bool write = blk_supports_write_perm(s->blk); + uint64_t perm = BLK_PERM_CONSISTENT_READ | (write ? BLK_PERM_WRITE : 0); + if (blk_set_perm(s->blk, perm, perm, &error_fatal)) { + warn_report("%s: %s: OTP backend is R/O", __func__, s->ot_id); + } + } + + ot_otp_engine_configure_scrmbl_key(s); + ot_otp_engine_configure_digest(s); + ot_otp_engine_configure_sram(s); + ot_otp_engine_configure_flash(s); + ot_otp_engine_configure_part_scramble_keys(s); + ot_otp_engine_configure_inv_default_parts(s); +} + +static void ot_otp_engine_init(Object *obj) +{ + OtOTPEngineState *s = OT_OTP_ENGINE(obj); + + OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(obj); + + g_assert(ic->update_status_error != NULL); + + g_assert(ic->part_descs != NULL); + g_assert(ic->part_count > 1u); + g_assert(ic->part_lc_num > 0u && ic->part_lc_num < ic->part_count); + g_assert(ic->sram_key_req_slot_count > 0u); + + /* + * The following members are constant values, and are used very often in + * this implementation. Add them to the Engine instance to avoid querying + * them every time from the concrete class object + */ + s->part_descs = ic->part_descs; + s->part_count = ic->part_count; + s->part_lc_num = ic->part_lc_num; + + ibex_qdev_init_irq(obj, &s->pwc_otp_rsp, OT_PWRMGR_OTP_RSP); + + qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_engine_pwr_otp_req, + OT_PWRMGR_OTP_REQ, 1); + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { + ibex_sysbus_init_irq(obj, &s->irqs[ix]); + } + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); + } + + qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_engine_lc_broadcast_recv, + OT_LC_BROADCAST, OT_OTP_LC_BROADCAST_COUNT); + + s->part_ctrls = g_new0(OtOTPPartController, s->part_count); + s->hw_cfg = g_new0(OtOTPHWCfg, 1u); + s->tokens = g_new0(OtOTPTokens, 1u); + s->dai = g_new0(OtOTPDAIController, 1u); + s->lci = g_new0(OtOTPLCIController, 1u); + s->keygen = g_new0(OtOTPKeyGen, 1u); + s->otp = g_new0(OtOTPStorage, 1u); + s->scrmbl_key_init = g_new0(OtOTPScrmblKeyInit, 1u); + s->lci->data = + g_new0(uint16_t, s->part_descs[s->part_lc_num].size / sizeof(uint16_t)); + + ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT(ic)); + s->keygen->present = ot_present_new(); + s->keygen->prng = ot_prng_allocate(); + + s->dai->delay = + timer_new_ns(OT_VIRTUAL_CLOCK, &ot_otp_engine_dai_complete, s); + s->dai->digest_bh = qemu_bh_new(&ot_otp_engine_dai_write_digest, s); + s->lci->prog_delay = + timer_new_ns(OT_OTP_HW_CLOCK, &ot_otp_engine_lci_write_word, s); + s->pwr_otp_bh = qemu_bh_new(&ot_otp_engine_pwr_otp_bh, s); + s->lc_broadcast.bh = qemu_bh_new(&ot_otp_engine_lc_broadcast_bh, s); + s->keygen->entropy_bh = qemu_bh_new(&ot_otp_engine_request_entropy_bh, s); + + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].buffered) { + continue; + } + size_t part_words = + ot_otp_engine_part_data_byte_size(s, part_ix) / sizeof(uint32_t); + s->part_ctrls[part_ix].buffer.data = g_new0(uint32_t, part_words); + } + + ot_otp_engine_add_scramble_key_props(s); + ot_otp_engine_add_inv_def_props(s); + + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + ot_prng_reseed(s->keygen->prng, (uint32_t)now); + +#ifdef OT_OTP_DEBUG + s->hexstr = g_new0(char, OT_OTP_HEXSTR_SIZE); +#endif +} + +static void ot_otp_engine_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_otp_engine_realize; + device_class_set_props(dc, ot_otp_engine_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtOTPEngineClass *ec = OT_OTP_ENGINE_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_engine_reset_enter, NULL, + &ot_otp_engine_reset_exit, + &ec->parent_phases); + + ec->update_irqs = &ot_otp_engine_update_irqs; + ec->update_alerts = &ot_otp_engine_update_alerts; + ec->get_part_from_address = &ot_otp_engine_get_part_from_address; + ec->get_part_digest_reg = &ot_otp_engine_get_part_digest_reg; + ec->is_readable = &ot_otp_engine_is_readable; + ec->set_error = &ot_otp_engine_set_error; + ec->dai_read = &ot_otp_engine_dai_read; + ec->dai_write = &ot_otp_engine_dai_write; + ec->dai_digest = &ot_otp_engine_dai_digest; + ec->get_hw_cfg = &ot_otp_engine_get_hw_cfg; + ec->get_otp_key = &ot_otp_engine_get_otp_key; + ec->program_req = &ot_otp_engine_program_req; +} + +static const TypeInfo ot_otp_engine_info = { + .name = TYPE_OT_OTP_ENGINE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtOTPEngineState), + .instance_init = &ot_otp_engine_init, + .class_size = sizeof(OtOTPEngineClass), + .class_init = &ot_otp_engine_class_init, + .abstract = true, +}; + +static void ot_otp_engine_register_types(void) +{ + type_register_static(&ot_otp_engine_info); +} + +type_init(ot_otp_engine_register_types); diff --git a/hw/opentitan/ot_otp.c b/hw/opentitan/ot_otp_if.c similarity index 72% rename from hw/opentitan/ot_otp.c rename to hw/opentitan/ot_otp_if.c index d2132c7d7c6e9..ccafdab71c69c 100644 --- a/hw/opentitan/ot_otp.c +++ b/hw/opentitan/ot_otp_if.c @@ -1,7 +1,7 @@ /* - * QEMU OpenTitan One Time Programmable (OTP) memory controller + * QEMU OpenTitan One Time Programmable (OTP) memory controller public interface * - * Copyright (c) 2023-2025 Rivos, Inc. + * Copyright (c) 2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -26,14 +26,17 @@ */ #include "qemu/osdep.h" -#include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" -OT_OBJECT_DEFINE_ABSTRACT_TYPE(OtOTPState, OtOTPClass, ot_otp, OT_OTP, - SYS_BUS_DEVICE) +static const TypeInfo ot_otp_if_info = { + .name = TYPE_OT_OTP_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(OtOTPIfClass), +}; -static void ot_otp_class_init(ObjectClass *oc, void *data) {} +static void ot_otp_if_register_types(void) +{ + type_register_static(&ot_otp_if_info); +} -static void ot_otp_init(Object *obj) {} - -static void ot_otp_finalize(Object *obj) {} +type_init(ot_otp_if_register_types); diff --git a/hw/opentitan/ot_otp_impl_if.c b/hw/opentitan/ot_otp_impl_if.c new file mode 100644 index 0000000000000..e9687a38d4122 --- /dev/null +++ b/hw/opentitan/ot_otp_impl_if.c @@ -0,0 +1,43 @@ +/* + * QEMU OpenTitan One Time Programmable (OTP) implementer interface. + * An OTP device should implement this API used by the OTP engine. + * + * Copyright (c) 2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/opentitan/ot_otp_impl_if.h" + +static const TypeInfo ot_otp_impl_if_info = { + .name = TYPE_OT_OTP_IMPL_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(OtOTPImplIfClass), +}; + +static void ot_otp_impl_if_register_types(void) +{ + type_register_static(&ot_otp_impl_if_info); +} + +type_init(ot_otp_impl_if_register_types); diff --git a/hw/opentitan/ot_otp_ot_be.c b/hw/opentitan/ot_otp_ot_be.c index f9568304ef376..7e575e0858d6d 100644 --- a/hw/opentitan/ot_otp_ot_be.c +++ b/hw/opentitan/ot_otp_ot_be.c @@ -31,8 +31,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_otp.h" #include "hw/opentitan/ot_otp_be_if.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_otp_ot_be.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index 99c689e85c073..d0c99bdd903e8 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -35,7 +35,7 @@ #include "qemu/typedefs.h" #include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_otp.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_prng.h" #include "hw/opentitan/ot_sram_ctrl.h" #include "hw/opentitan/ot_vmapper.h" @@ -136,7 +136,7 @@ struct OtSramCtrlState { char *hexstr; char *ot_id; - OtOTPState *otp_ctrl; /* optional */ + DeviceState *otp_ctrl; /* optional */ OtVMapperState *vmapper; /* optional */ uint32_t size; /* in bytes */ uint32_t init_chunk_words; /* init chunk size in words */ @@ -284,13 +284,15 @@ static void ot_sram_ctrl_reseed(OtSramCtrlState *s) * neither PRINCE block cipher nor shallow substitution-permutation. * Seed and Nonce are combined to initialize a QEMU PRNG instance. */ - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + if (!oc->get_otp_key) { /* on EarlGrey, OTP key handing has not been implemented */ qemu_log_mask(LOG_UNIMP, "%s: %s OTP does not support key generation\n", __func__, s->ot_id); } else { - oc->get_otp_key(s->otp_ctrl, OTP_KEY_SRAM, s->otp_key); + oc->get_otp_key(oi, OT_OTP_KEY_SRAM, s->otp_key); TRACE_SRAM_CTRL("Scrambing seed: %s (valid: %u)", ot_sram_ctrl_hexdump(s, s->otp_key->seed, @@ -355,9 +357,10 @@ static void ot_sram_ctrl_update_exec(OtSramCtrlState *s) * Configuration need to be loaded on demand */ if (s->otp_ctrl) { - OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); - s->otp_ifetch = oc->get_hw_cfg(s->otp_ctrl)->en_sram_ifetch_mb8 == - OT_MULTIBITBOOL8_TRUE; + OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); + OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); + s->otp_ifetch = + oc->get_hw_cfg(oi)->en_sram_ifetch_mb8 == OT_MULTIBITBOOL8_TRUE; } bool ifetch = s->ifetch && s->csr_ifetch && s->otp_ifetch; @@ -668,8 +671,8 @@ static MemTxResult ot_sram_ctrl_mem_init_write_with_attrs( static Property ot_sram_ctrl_properties[] = { DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSramCtrlState, ot_id), - DEFINE_PROP_LINK("otp-ctrl", OtSramCtrlState, otp_ctrl, TYPE_OT_OTP, - OtOTPState *), + DEFINE_PROP_LINK("otp-ctrl", OtSramCtrlState, otp_ctrl, TYPE_OT_OTP_IF, + DeviceState *), DEFINE_PROP_LINK("vmapper", OtSramCtrlState, vmapper, TYPE_OT_VMAPPER, OtVMapperState *), DEFINE_PROP_UINT32("size", OtSramCtrlState, size, 0u), @@ -752,6 +755,8 @@ static void ot_sram_ctrl_realize(DeviceState *dev, Error **errp) */ g_assert(s->ifetch || !s->otp_ctrl || s->vmapper); + (void)OBJECT_CHECK(OtOTPIf, s->otp_ctrl, TYPE_OT_OTP_IF); + s->wsize = DIV_ROUND_UP(s->size, sizeof(uint32_t)); unsigned size = s->wsize * sizeof(uint32_t); diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 18ea1b3ce6cca..80d963156b0c0 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -45,6 +45,7 @@ config OT_DARJEELING select OT_LC_CTRL select OT_MBX select OT_OTBN + select OT_OTP_IF select OT_OTP_DJ select OT_OTP_OT_BE select OT_PINMUX_DJ @@ -90,6 +91,7 @@ config OT_EARLGREY select OT_LC_CTRL select OT_OTBN select OT_OTP_EG + select OT_OTP_IF select OT_OTP_OT_BE select OT_PINMUX_EG select OT_PLIC_EXT diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index b267500cb0500..a1b1e99a715b3 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -55,6 +55,7 @@ #include "hw/opentitan/ot_mbx.h" #include "hw/opentitan/ot_otbn.h" #include "hw/opentitan/ot_otp_dj.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_otp_ot_be.h" #include "hw/opentitan/ot_pinmux_dj.h" #include "hw/opentitan/ot_plic_ext.h" @@ -758,8 +759,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_ALERT(1, 36) ), .link = IBEXDEVICELINKDEFS( - OT_DJ_SOC_DEVLINK("noise-src", AST), - OT_DJ_SOC_DEVLINK("otp-ctrl", OTP_CTRL) + OT_DJ_SOC_DEVLINK("noise-src", AST) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("version", 3) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 8323989df662a..38d2a830e0c12 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -57,6 +57,7 @@ #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otbn.h" #include "hw/opentitan/ot_otp_eg.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_otp_ot_be.h" #include "hw/opentitan/ot_pinmux_eg.h" #include "hw/opentitan/ot_plic_ext.h" @@ -1246,8 +1247,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(1, 54) ), .link = IBEXDEVICELINKDEFS( - OT_EG_SOC_DEVLINK("noise-src", AST), - OT_EG_SOC_DEVLINK("otp-ctrl", OTP_CTRL) + OT_EG_SOC_DEVLINK("noise-src", AST) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("version", 2) @@ -1408,7 +1408,7 @@ 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) /** @todo to be changed */ + IBEX_DEV_UINT_PROP("num-lines", 1u) /* @todo to be changed */ ) }, [OT_EG_SOC_SPLITTER_LC_ESCALATE] = { @@ -1420,7 +1420,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_FLASH_LC_ESCALATE_EN) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("num-lines", 2u) /** @todo to be changed */ + IBEX_DEV_UINT_PROP("num-lines", 2u) /* @todo to be changed */ ) }, [OT_EG_SOC_SPLITTER_LC_SEED_HW_RD] = { diff --git a/include/hw/opentitan/ot_lc_ctrl.h b/include/hw/opentitan/ot_lc_ctrl.h index 976f302a2cbe0..c620ce787c2b8 100644 --- a/include/hw/opentitan/ot_lc_ctrl.h +++ b/include/hw/opentitan/ot_lc_ctrl.h @@ -29,6 +29,7 @@ #define HW_OPENTITAN_OT_LC_CTRL_H #include "qom/object.h" +#include "hw/sysbus.h" #define TYPE_OT_LC_CTRL "ot-lc_ctrl" OBJECT_DECLARE_TYPE(OtLcCtrlState, OtLcCtrlClass, OT_LC_CTRL) diff --git a/include/hw/opentitan/ot_otp_dj.h b/include/hw/opentitan/ot_otp_dj.h index 0a7b148cc2001..700d364990906 100644 --- a/include/hw/opentitan/ot_otp_dj.h +++ b/include/hw/opentitan/ot_otp_dj.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling One Time Programmable (OTP) memory controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -29,9 +29,7 @@ #ifndef HW_OPENTITAN_OT_OTP_DJ_H #define HW_OPENTITAN_OT_OTP_DJ_H -#include "hw/opentitan/ot_otp.h" - #define TYPE_OT_OTP_DJ "ot-otp-dj" -OBJECT_DECLARE_TYPE(OtOTPDjState, OtOTPClass, OT_OTP_DJ) +OBJECT_DECLARE_TYPE(OtOTPDjState, OtOTPDjClass, OT_OTP_DJ) #endif /* HW_OPENTITAN_OT_OTP_DJ_H */ diff --git a/include/hw/opentitan/ot_otp_eg.h b/include/hw/opentitan/ot_otp_eg.h index 9a7f7759f593c..9431b4be5d9d6 100644 --- a/include/hw/opentitan/ot_otp_eg.h +++ b/include/hw/opentitan/ot_otp_eg.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey One Time Programmable (OTP) memory controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -29,9 +29,7 @@ #ifndef HW_OPENTITAN_OT_OTP_EG_H #define HW_OPENTITAN_OT_OTP_EG_H -#include "hw/opentitan/ot_otp.h" - #define TYPE_OT_OTP_EG "ot-otp-eg" -OBJECT_DECLARE_TYPE(OtOTPEgState, OtOTPClass, OT_OTP_EG) +OBJECT_DECLARE_TYPE(OtOTPEgState, OtOTPEgClass, OT_OTP_EG) #endif /* HW_OPENTITAN_OT_OTP_EG_H */ diff --git a/include/hw/opentitan/ot_otp_engine.h b/include/hw/opentitan/ot_otp_engine.h new file mode 100644 index 0000000000000..07ee708ed7a8f --- /dev/null +++ b/include/hw/opentitan/ot_otp_engine.h @@ -0,0 +1,416 @@ +/* + * QEMU OpenTitan One Time Programmable (OTP) memory controller + * + * Copyright (c) 2023-2025 Rivos, Inc. + * Copyright (c) 2025 lowRISC contributors. + * + * Author(s): + * Emmanuel Blot + * Loïc Lefort + * Alex Jones + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_OPENTITAN_OT_OTP_ENGINE_H +#define HW_OPENTITAN_OT_OTP_ENGINE_H + +#include "qemu/timer.h" +#include "hw/opentitan/ot_edn.h" +#include "hw/opentitan/ot_otp_be_if.h" +#include "hw/opentitan/ot_otp_if.h" +#include "hw/opentitan/ot_otp_impl_if.h" +#include "hw/registerfields.h" +#include "hw/riscv/ibex_irq.h" +#include "hw/sysbus.h" + +#undef OT_OTP_DEBUG + +#define TYPE_OT_OTP_ENGINE "ot-otp_engine" +OBJECT_DECLARE_TYPE(OtOTPEngineState, OtOTPEngineClass, OT_OTP_ENGINE) + +#define OT_OTP_NUM_IRQS 2u +#define OT_OTP_NUM_ALERTS 5u +#define OT_OTP_NUM_DAI_WORDS 2u +#define OT_OTP_NUM_DIGEST_WORDS 2u +#define OT_OTP_NUM_ZER_WORDS 2u + + +#define LC_TRANSITION_CNT_SIZE 48u +#define LC_STATE_SIZE 40u + +#define OT_OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) +#define OT_OTP_ZER_ADDR_MASK (sizeof(uint64_t) - 1u) + +/* Error code (compliant with ERR_CODE registers) */ +typedef enum { + OT_OTP_NO_ERROR, + OT_OTP_MACRO_ERROR, + OT_OTP_MACRO_ECC_CORR_ERROR, /* This is NOT an error */ + OT_OTP_MACRO_ECC_UNCORR_ERROR, + OT_OTP_MACRO_WRITE_BLANK_ERROR, + OT_OTP_ACCESS_ERROR, + OT_OTP_CHECK_FAIL_ERROR, /* Digest error */ + OT_OTP_FSM_STATE_ERROR, + OT_OTP_ERROR_COUNT, +} OtOTPError; + +/* States of an unbuffered partition FSM */ +typedef enum { + OT_OTP_UNBUF_RESET, + OT_OTP_UNBUF_INIT, + OT_OTP_UNBUF_INIT_WAIT, + OT_OTP_UNBUF_IDLE, + OT_OTP_UNBUF_READ, + OT_OTP_UNBUF_READ_WAIT, + OT_OTP_UNBUF_ERROR, +} OtOTPUnbufState; + +/* States of a buffered partition FSM */ +typedef enum { + OT_OTP_BUF_RESET, + OT_OTP_BUF_INIT, + OT_OTP_BUF_INIT_WAIT, + OT_OTP_BUF_INIT_DESCR, + OT_OTP_BUF_INIT_DESCR_WAIT, + OT_OTP_BUF_IDLE, + OT_OTP_BUF_INTEG_SCR, + OT_OTP_BUF_INTEG_SCR_WAIT, + OT_OTP_BUF_INTEG_DIG_CLR, + OT_OTP_BUF_INTEG_DIG, + OT_OTP_BUF_INTEG_DIG_PAD, + OT_OTP_BUF_INTEG_DIG_FIN, + OT_OTP_BUF_INTEG_DIG_WAIT, + OT_OTP_BUF_CNSTY_READ, + OT_OTP_BUF_CNSTY_READ_WAIT, + OT_OTP_BUF_ERROR, +} OtOTPBufState; + +/* Direct Access Interface states */ +typedef enum { + OT_OTP_DAI_RESET, + OT_OTP_DAI_INIT_OTP, + OT_OTP_DAI_INIT_PART, + OT_OTP_DAI_IDLE, + OT_OTP_DAI_ERROR, + OT_OTP_DAI_READ, + OT_OTP_DAI_READ_WAIT, + OT_OTP_DAI_DESCR, + OT_OTP_DAI_DESCR_WAIT, + OT_OTP_DAI_WRITE, + OT_OTP_DAI_WRITE_WAIT, + OT_OTP_DAI_SCR, + OT_OTP_DAI_SCR_WAIT, + OT_OTP_DAI_DIG_CLR, + OT_OTP_DAI_DIG_READ, + OT_OTP_DAI_DIG_READ_WAIT, + OT_OTP_DAI_DIG, + OT_OTP_DAI_DIG_PAD, + OT_OTP_DAI_DIG_FIN, + OT_OTP_DAI_DIG_WAIT, +} OtOTPDAIState; + +typedef enum { + OT_OTP_LCI_RESET, + OT_OTP_LCI_IDLE, + OT_OTP_LCI_WRITE, + OT_OTP_LCI_WRITE_WAIT, + OT_OTP_LCI_ERROR, +} OtOTPLCIState; + +typedef enum { + OT_OTP_DA_REG_REGWEN, + OT_OTP_DA_REG_CMD, + OT_OTP_DA_REG_ADDRESS, + OT_OTP_DA_REG_WDATA_0, + OT_OTP_DA_REG_WDATA_1, + OT_OTP_DA_REG_RDATA_0, + OT_OTP_DA_REG_RDATA_1, +} OtOTPDirectAccessRegister; + +typedef struct { + union { + OtOTPBufState b; + OtOTPUnbufState u; + } state; + struct { + uint32_t *data; /* size, see OtOTPPartDesc w/o digest data */ + uint64_t next_digest; /* computed HW digest to store into OTP cell */ + } buffer; /* only meaningful for buffered partitions */ + uint64_t digest; /* digest as read from OTP back end at init time */ + bool locked; + bool failed; + bool read_lock; + bool write_lock; + /* OTP scrambling key constant, not constant for deriving other keys */ + uint8_t *otp_scramble_key; /* may be NULL */ + uint8_t *inv_default_data; /* may be NULL */ +} OtOTPPartController; + +typedef struct OtOTPDAIController { + QEMUTimer *delay; /* simulate delayed access completion */ + QEMUBH *digest_bh; /* write computed digest to OTP cell */ + OtOTPDAIState state; + int partition; /* current partition being worked on or -1 */ +} OtOTPDAIController; + +typedef struct { + QEMUTimer *prog_delay; /* OTP cell prog delay (use OT_OTP_HW_CLOCK) */ + OtOTPLCIState state; + OtOTPError error; + ot_otp_program_ack_fn ack_fn; + void *ack_data; + uint16_t *data; + unsigned hpos; /* current offset in data */ +} OtOTPLCIController; + +typedef struct { + uint32_t *storage; /* overall buffer for the storage backend */ + uint32_t *data; /* data buffer (all partitions) */ + uint32_t *ecc; /* ecc buffer for date */ + unsigned size; /* overall storage size in bytes */ + unsigned data_size; /* data buffer size in bytes */ + unsigned ecc_size; /* ecc buffer size in bytes */ + unsigned ecc_bit_count; /* count of ECC bit for each data granule */ + unsigned ecc_granule; /* size of a granule in bytes */ +} OtOTPStorage; + +typedef struct { + QEMUBH *bh; + uint16_t signal; /* each bit tells if signal needs to be handled */ + uint16_t level; /* level of the matching signal */ + uint16_t current_level; /* current level of all signals */ +} OtOTPLcBroadcast; + +static_assert(OT_OTP_LC_BROADCAST_COUNT < 8 * sizeof(uint16_t), + "Invalid OT_OTP_LC_BROADCAST_COUNT"); + +typedef struct OtOTPKeyGen_ OtOTPKeyGen; +typedef struct OtOTPScrmblKeyInit_ OtOTPScrmblKeyInit; + +typedef struct { + uint32_t dai_base; /* offset of DIRECT_ACCESS_REGWEN register */ + uint32_t err_code_base; /* offset of ERR_CODE_0 register */ + uint32_t read_lock_base; /* offset of first *_READ_LOCK */ +} OtOTPRegisterOffset; + +struct OtOTPEngineState { + SysBusDevice parent_obj; + + QEMUBH *pwr_otp_bh; + IbexIRQ irqs[OT_OTP_NUM_IRQS]; + IbexIRQ alerts[OT_OTP_NUM_ALERTS]; + IbexIRQ pwc_otp_rsp; + + uint32_t *regs; + uint32_t alert_bm; + + OtOTPLcBroadcast lc_broadcast; + OtOTPDAIController *dai; + OtOTPLCIController *lci; + OtOTPPartController *part_ctrls; + const OtOTPPartDesc *part_descs; + unsigned part_count; + unsigned part_lc_num; + OtOTPKeyGen *keygen; + OtOTPScrmblKeyInit *scrmbl_key_init; + OtOtpBeCharacteristics be_chars; + OtOTPRegisterOffset reg_offset; + uint64_t digest_iv; + uint8_t digest_const[16u]; + uint64_t sram_iv; + uint8_t sram_const[16u]; + /* flash_* are only defined for OtOTPImplIf with has_flash_support set */ + uint64_t flash_data_iv; + uint8_t flash_data_const[16u]; + uint64_t flash_addr_iv; + uint8_t flash_addr_const[16u]; + + OtOTPStorage *otp; + OtOTPHWCfg *hw_cfg; + OtOTPTokens *tokens; + char *hexstr; + + char *ot_id; + BlockBackend *blk; /* OTP host backend */ + OtOtpBeIf *otp_backend; + OtEDNState *edn; + char *scrmbl_key_xstr; + char *digest_const_xstr; + char *digest_iv_xstr; + char *sram_const_xstr; + char *sram_iv_xstr; + /* flash xstrs are only valid for OtOTPImplIf with has_flash_support set */ + char *flash_data_iv_xstr; + char *flash_data_const_xstr; + char *flash_addr_iv_xstr; + char *flash_addr_const_xstr; + char **otp_scramble_key_xstrs; /* some entries may be NULL */ + char **inv_default_part_xstrs; /* some entries may be NULL */ + uint8_t edn_ep; + bool fatal_escalate; +}; + +struct OtOTPEngineClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; + + /* @todo document all those */ + void (*update_irqs)(OtOTPEngineState *s); + void (*update_alerts)(OtOTPEngineState *s); + int (*get_part_from_address)(const OtOTPEngineState *s, hwaddr addr); + uint32_t (*get_part_digest_reg)(OtOTPEngineState *s, uint32_t offset); + bool (*is_readable)(const OtOTPEngineState *s, unsigned part_ix); + void (*set_error)(OtOTPEngineState *s, unsigned part_ix, OtOTPError err); + void (*dai_read)(OtOTPEngineState *s); + void (*dai_write)(OtOTPEngineState *s); + void (*dai_digest)(OtOTPEngineState *s); + const OtOTPHWCfg *(*get_hw_cfg)(const OtOTPIf *dev); + void (*get_otp_key)(OtOTPIf *dev, OtOTPKeyType type, OtOTPKey *key); + bool (*program_req)(OtOTPIf *dev, const uint16_t *lc_tcount, + const uint16_t *lc_state, ot_otp_program_ack_fn ack, + void *opaque); +}; + +#ifdef OT_OTP_COMPORTABLE_REGS + +/* Comportable registers are identical for all OTP variants */ +REG32(INTR_STATE, 0x00u) +SHARED_FIELD(INTR_OTP_OPERATION_DONE, 0u, 1u) +SHARED_FIELD(INTR_OTP_ERROR, 1u, 1u) +REG32(INTR_ENABLE, 0x04u) +REG32(INTR_TEST, 0x08u) +REG32(ALERT_TEST, 0x0cu) +REG32(OTP_FIRST_IMPL_REG, 0x10u) +SHARED_FIELD(ALERT_FATAL_MACRO_ERROR, 0u, 1u) +SHARED_FIELD(ALERT_FATAL_CHECK_ERROR, 1u, 1u) +SHARED_FIELD(ALERT_FATAL_BUS_INTEG_ERROR, 2u, 1u) +SHARED_FIELD(ALERT_FATAL_PRIM_OTP_ALERT, 3u, 1u) +SHARED_FIELD(ALERT_RECOV_PRIM_OTP_ALERT, 4u, 1u) + +#define INTR_WMASK (INTR_OTP_OPERATION_DONE_MASK | INTR_OTP_ERROR_MASK) + +#define ALERT_WMASK \ + (ALERT_FATAL_MACRO_ERROR_MASK | ALERT_FATAL_CHECK_ERROR_MASK | \ + ALERT_FATAL_BUS_INTEG_ERROR_MASK | ALERT_FATAL_PRIM_OTP_ALERT_MASK | \ + ALERT_RECOV_PRIM_OTP_ALERT_MASK) + +#endif /* OT_OTP_COMPORTABLE_REGS */ + +/* REG32 bitfields common to all new OTP versions */ +FIELD(OT_OTP_DIRECT_ACCESS_CMD, RD, 0u, 1u) +FIELD(OT_OTP_DIRECT_ACCESS_CMD, WR, 1u, 1u) +FIELD(OT_OTP_DIRECT_ACCESS_CMD, DIGEST, 2u, 1u) +/* ZEROIZE has been introduced in OTP v2.10, after Earlgrey v1.0 / OTP v2.0 */ +FIELD(OT_OTP_DIRECT_ACCESS_CMD, ZEROIZE, 3u, 1u) + +SHARED_FIELD(OT_OTP_ERR_CODE, 0u, 3u) +static_assert(OT_OTP_ERR_CODE_MASK >= OT_OTP_ERROR_COUNT - 1u, + "Error mask not large enough"); + +SHARED_FIELD(OT_OTP_READ_LOCK, 0u, 1u) + +static inline unsigned +ot_otp_engine_part_data_offset(const OtOTPEngineState *s, unsigned part_ix) +{ + return (unsigned)(s->part_descs[part_ix].offset); +} + +static inline unsigned +ot_otp_engine_part_data_byte_size(const OtOTPEngineState *s, unsigned part_ix) +{ + size_t size = s->part_descs[part_ix].size; + + if (s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest) { + size -= sizeof(uint32_t) * OT_OTP_NUM_DIGEST_WORDS; + } + + if (s->part_descs[part_ix].zeroizable) { + size -= sizeof(uint32_t) * OT_OTP_NUM_ZER_WORDS; + } + + return (unsigned)size; +} + +static inline bool +ot_otp_engine_is_buffered(const OtOTPEngineState *s, unsigned part_ix) +{ + if (part_ix < s->part_count) { + return s->part_descs[part_ix].buffered; + } + + return false; +} + +static inline bool +ot_otp_engine_is_secret(const OtOTPEngineState *s, unsigned part_ix) +{ + if (part_ix < s->part_count) { + return s->part_descs[part_ix].secret; + } + + return false; +} + +static inline bool +ot_otp_engine_is_backend_ecc_enabled(const OtOTPEngineState *s) +{ + OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); + if (!bec->is_ecc_enabled) { + return true; + } + + return bec->is_ecc_enabled(s->otp_backend); +} + +static inline bool ot_otp_engine_is_ecc_enabled(const OtOTPEngineState *s) +{ + return s->otp->ecc_granule == sizeof(uint16_t) && + ot_otp_engine_is_backend_ecc_enabled(s); +} + +static inline bool +ot_otp_engine_has_digest(const OtOTPEngineState *s, unsigned part_ix) +{ + return s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest; +} + +static inline bool ot_otp_engine_is_part_digest_offset( + const OtOTPEngineState *s, unsigned part_ix, hwaddr addr) +{ + uint16_t offset = s->part_descs[part_ix].digest_offset; + + return (offset != UINT16_MAX) && + ((addr & ~OT_OTP_DIGEST_ADDR_MASK) == offset); +} + +static inline bool ot_otp_engine_is_part_zer_offset( + const OtOTPEngineState *s, unsigned part_ix, hwaddr addr) +{ + uint16_t offset = s->part_descs[part_ix].zer_offset; + + return (offset != UINT16_MAX) && ((addr & ~OT_OTP_ZER_ADDR_MASK) == offset); +} + +static inline uint32_t ot_otp_engine_dai_is_busy(const OtOTPEngineState *s) +{ + return s->dai->state != OT_OTP_DAI_IDLE; +} + +#endif /* HW_OPENTITAN_OT_OTP_ENGINE_H */ diff --git a/include/hw/opentitan/ot_otp.h b/include/hw/opentitan/ot_otp_if.h similarity index 79% rename from include/hw/opentitan/ot_otp.h rename to include/hw/opentitan/ot_otp_if.h index 12a9fdc56016d..237bf4eff877a 100644 --- a/include/hw/opentitan/ot_otp.h +++ b/include/hw/opentitan/ot_otp_if.h @@ -1,5 +1,5 @@ /* - * QEMU OpenTitan One Time Programmable (OTP) memory controller + * QEMU OpenTitan One Time Programmable (OTP) memory controller public interface * * Copyright (c) 2023-2025 Rivos, Inc. * @@ -26,15 +26,18 @@ * THE SOFTWARE. */ -#ifndef HW_OPENTITAN_OT_OTP_H -#define HW_OPENTITAN_OT_OTP_H +#ifndef HW_OPENTITAN_OT_OTP_IF_H +#define HW_OPENTITAN_OT_OTP_IF_H #include "qom/object.h" #include "hw/opentitan/ot_common.h" -#include "hw/sysbus.h" -#define TYPE_OT_OTP "ot-otp" -OBJECT_DECLARE_TYPE(OtOTPState, OtOTPClass, OT_OTP) +#define TYPE_OT_OTP_IF "ot-otp_if" +typedef struct OtOTPIfClass OtOTPIfClass; +typedef struct OtOTPIf OtOTPIf; + +DECLARE_CLASS_CHECKERS(OtOTPIfClass, OT_OTP_IF, TYPE_OT_OTP_IF) +#define OT_OTP_IF(_obj_) INTERFACE_CHECK(OtOTPIf, (_obj_), TYPE_OT_OTP_IF) /* Input signals from life cycle */ typedef enum { @@ -75,10 +78,10 @@ typedef struct { } OtOTPHWCfg; typedef enum { - OTP_TOKEN_TEST_UNLOCK, - OTP_TOKEN_TEST_EXIT, - OTP_TOKEN_RMA, - OTP_TOKEN_COUNT + OT_OTP_TOKEN_TEST_UNLOCK, + OT_OTP_TOKEN_TEST_EXIT, + OT_OTP_TOKEN_RMA, + OT_OTP_TOKEN_COUNT } OtOTPToken; typedef struct { @@ -87,16 +90,16 @@ typedef struct { } OtOTPTokenValue; typedef struct { - OtOTPTokenValue values[OTP_TOKEN_COUNT]; + OtOTPTokenValue values[OT_OTP_TOKEN_COUNT]; uint32_t valid_bm; /* OtLcCtrlToken-indexed valid bit flags */ } OtOTPTokens; typedef enum { - OTP_KEY_FLASH_DATA, - OTP_KEY_FLASH_ADDR, - OTP_KEY_OTBN, - OTP_KEY_SRAM, - OTP_KEY_COUNT + OT_OTP_KEY_FLASH_DATA, + OT_OTP_KEY_FLASH_ADDR, + OT_OTP_KEY_OTBN, + OT_OTP_KEY_SRAM, + OT_OTP_KEY_COUNT } OtOTPKeyType; #define OT_OTP_SEED_MAX_SIZE 32u /* 256 bits */ @@ -111,11 +114,11 @@ typedef struct { } OtOTPKey; typedef enum { - OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, - OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1, - OTP_KEYMGR_SECRET_CREATOR_SEED, - OTP_KEYMGR_SECRET_OWNER_SEED, - OTP_KEYMGR_SECRET_COUNT + OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1, + OT_OTP_KEYMGR_SECRET_CREATOR_SEED, + OT_OTP_KEYMGR_SECRET_OWNER_SEED, + OT_OTP_KEYMGR_SECRET_COUNT } OtOTPKeyMgrSecretType; #define OT_OTP_KEYMGR_SECRET_SIZE 32u /* 256 bits (for both keys and seeds) */ @@ -125,20 +128,15 @@ typedef struct { bool valid; /* whether the key/seed data is valid */ } OtOTPKeyMgrSecret; -struct OtOTPState { - SysBusDevice parent_obj; -}; - typedef void (*ot_otp_program_ack_fn)(void *opaque, bool ack); -struct OtOTPClass { - SysBusDeviceClass parent_class; - ResettablePhases parent_phases; +struct OtOTPIfClass { + InterfaceClass parent_class; /* * Provide OTP lifecycle information. * - * @s the OTP device + * @dev the OTP device * @lc_tcount if not NULL, updated with the raw LifeCycle transition count * buffer. * @lc_state if not NULL, updated with the raw LifeCycle state buffer. @@ -147,35 +145,35 @@ struct OtOTPClass { * * @note: lc_valid and secret_valid use OT_MULTIBITBOOL_LC4 encoding */ - void (*get_lc_info)(const OtOTPState *s, uint16_t *lc_state, + void (*get_lc_info)(const OtOTPIf *dev, uint16_t *lc_state, uint16_t *lc_tcount, uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens); /* * Retrieve HW configuration. * - * @s the OTP device + * @dev the OTP device * @return the HW config data (never NULL) */ - const OtOTPHWCfg *(*get_hw_cfg)(const OtOTPState *s); + const OtOTPHWCfg *(*get_hw_cfg)(const OtOTPIf *dev); /* * Retrieve SRAM scrambling key. * - * @s the OTP device + * @dev the OTP device * @type the type of the key to retrieve * @key the key record to update */ - void (*get_otp_key)(OtOTPState *s, OtOTPKeyType type, OtOTPKey *key); + void (*get_otp_key)(OtOTPIf *dev, OtOTPKeyType type, OtOTPKey *key); /* * Retrieve Key Manager secret (key or seeds). * - * @s the OTP device + * @dev the OTP device * @type the type of secret to retrieve * @secret the key manager secret record to update */ - void (*get_keymgr_secret)(OtOTPState *s, OtOTPKeyMgrSecretType type, + void (*get_keymgr_secret)(OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret); /** @@ -185,16 +183,16 @@ struct OtOTPClass { * callback. Conversely, it should always invoke the callback if the request * is accepted. * - * @s the OTP device + * @dev the OTP device * @lc_tcount the raw LifeCycle transition count buffer * @lc_state the raw LifeCycle state buffer * @ack the callback to asynchronously invoke on OTP completion/error * @opaque opaque data to forward to the ot_otp_program_ack_fn function * @return @c true if request is accepted, @c false is rejected. */ - bool (*program_req)(OtOTPState *s, const uint16_t *lc_tcount, + bool (*program_req)(OtOTPIf *dev, const uint16_t *lc_tcount, const uint16_t *lc_state, ot_otp_program_ack_fn ack, void *opaque); }; -#endif /* HW_OPENTITAN_OT_OTP_H */ +#endif /* HW_OPENTITAN_OT_OTP_IF_H */ diff --git a/include/hw/opentitan/ot_otp_impl_if.h b/include/hw/opentitan/ot_otp_impl_if.h new file mode 100644 index 0000000000000..bc54b68c8a42c --- /dev/null +++ b/include/hw/opentitan/ot_otp_impl_if.h @@ -0,0 +1,119 @@ +/* + * QEMU OpenTitan One Time Programmable (OTP) implementer interface. + * An OTP device should implement this API used by the OTP engine. + * + * Copyright (c) 2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_OPENTITAN_OT_OTP_IMPL_IF_H +#define HW_OPENTITAN_OT_OTP_IMPL_IF_H + +#include "qom/object.h" + +#define TYPE_OT_OTP_IMPL_IF "ot-otp_impl_if" +typedef struct OtOTPImplIfClass OtOTPImplIfClass; +typedef struct OtOTPImplIf OtOTPImplIf; +DECLARE_CLASS_CHECKERS(OtOTPImplIfClass, OT_OTP_IMPL_IF, TYPE_OT_OTP_IMPL_IF) +#define OT_OTP_IMPL_IF(_obj_) \ + INTERFACE_CHECK(OtOTPImplIf, (_obj_), TYPE_OT_OTP_IMPL_IF) + +/* @todo WR and RD locks need to be rewritten (not simple boolean) */ +typedef struct { + const char *name; + uint16_t size; + uint16_t offset; + uint16_t digest_offset; + uint16_t zer_offset; + uint16_t hw_digest:1; + uint16_t sw_digest:1; + uint16_t secret:1; + uint16_t buffered:1; + uint16_t write_lock:1; + uint16_t read_lock:1; + uint16_t read_lock_csr:1; + uint16_t integrity:1; + uint16_t zeroizable:1; + uint16_t iskeymgr_creator:1; + uint16_t iskeymgr_owner:1; +} OtOTPPartDesc; + +typedef struct { + unsigned partition; + unsigned offset; /* byte offset in partition */ + unsigned size; /* size in bytes */ +} OtOTPKeySeed; + + +/* Supported statuses */ +typedef enum { + OT_OTP_STATUS_NONE, + OT_OTP_STATUS_DAI, /* Direct Access Interface */ + OT_OTP_STATUS_LCI, /* Life Cycle Interface */ + OT_OTP_STATUS_COUNT +} OtOTPStatus; + +struct OtOTPImplIfClass { + InterfaceClass parent_class; + + /* Array of part_count partition descriptors */ + const OtOTPPartDesc *part_descs; + + /* Array of OTP_KEY_COUNT descriptors */ + const OtOTPKeySeed *key_seeds; + + /* Number of partition */ + unsigned part_count; + + /* Index of the life cycle partition in part_descs */ + unsigned part_lc_num; + + /* Number of SRAM KEY requester slots */ + unsigned sram_key_req_slot_count; + + /* Whether embedded flash support is present */ + bool has_flash_support; + + /* Whether zeroification feature is supported */ + bool has_zer_support; + + /* + * Update the R_STATUS register. + * The OTP concrete device should provide an implementation + * + * @dev the OTP device + * @error the error type to update + * @set whether to set or clear the error + */ + void (*update_status_error)(OtOTPImplIf *dev, OtOTPStatus error, bool set); + + /* + * Signal that power management has triggered the OTP initialization + * sequence. May be left unimplemented (NULL). + * + * @dev the OTP device + */ + void (*signal_pwr_sequence)(OtOTPImplIf *dev); +}; + +#endif /* HW_OPENTITAN_OT_OTP_IMPL_IF_H */ diff --git a/python/qemu/ot/otp/descriptor.py b/python/qemu/ot/otp/descriptor.py index 4712a0afc19a4..68fc818f2b171 100644 --- a/python/qemu/ot/otp/descriptor.py +++ b/python/qemu/ot/otp/descriptor.py @@ -88,9 +88,10 @@ def save(self, kind: str, hjname: str, scriptname: str, cfp: TextIO) \ print(file=cfp) print('/* clang-format off */', file=cfp) print('/* NOLINTBEGIN */', file=cfp) - print('static const OtOTPPartDesc OtOTPPartDescs[] = {', file=cfp) + print('static const OtOTPPartDesc OT_OTP_PART_DESCS[] = {', file=cfp) for part in self._otpmap.enumerate_partitions(): print(f' [OTP_PART_{part.name}] = {{', file=cfp) + print(f' .name = "{part.name}",', file=cfp) print(f' .size = {part.size}u,', file=cfp) print(f' .offset = {part.offset}u,', file=cfp) if part.zer_offset is not None: @@ -122,11 +123,36 @@ def save(self, kind: str, hjname: str, scriptname: str, cfp: TextIO) \ if isinstance(attr_val, bool): attr_val = str(attr_val).lower() print(f' .{attr_name} = {attr_val},', file=cfp) - print(f' }},', file=cfp) # noqa: F541 + print(' },', file=cfp) print('};', file=cfp) print('', file=cfp) - print('#define OTP_PART_COUNT ARRAY_SIZE(OtOTPPartDescs)', file=cfp) + print('#define OTP_PART_COUNT ARRAY_SIZE(OT_OTP_PART_DESCS)', file=cfp) print(file=cfp) + key_seeds: list[tuple[str, str, int, int]] = [] + for kseed in self._otpmap.key_seeds: + if kseed.name == 'SRAM_DATA': + # the OTBN key is derived from the SRAM key + key_seeds.append(('OT_OTP_KEY_OTBN', + f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + key_seeds.append(('OT_OTP_KEY_SRAM', + f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + continue + key_seeds.append((f'OT_OTP_KEY_{kseed.name}', + f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + print('static const OtOTPKeySeed ' + 'OT_OTP_KEY_SEEDS[OT_OTP_KEY_COUNT] = {', + file=cfp) + for ksd in key_seeds: + print(f' [{ksd[0]}] = {{', file=cfp) + print(f' .partition = {ksd[1]},', file=cfp) + print(f' .offset = {ksd[2]},', file=cfp) + print(f' .size = {ksd[3]},', file=cfp) + print(' },', file=cfp) + print('};', file=cfp) + print('', file=cfp) print('/* NOLINTEND */', file=cfp) print('/* clang-format on */', file=cfp) # pylint: enable=f-string-without-interpolation @@ -250,10 +276,10 @@ def _save_qemu(self, topname: Optional[str], hjname: str, scriptname: str, print('static const char *PART_NAMES[] = {', file=cfp) print(' /* clang-format off */', file=cfp) for pname in part_names[:pcount]: - print(f' OTP_NAME_ENTRY({pname}),', file=cfp) + print(f' OT_OTP_NAME_ENTRY({pname}),', file=cfp) print(' /* fake partitions */', file=cfp) - print(' OTP_NAME_ENTRY(OTP_ENTRY_DAI),', file=cfp) - print(' OTP_NAME_ENTRY(OTP_ENTRY_KDI),', file=cfp) + print(' OT_OTP_NAME_ENTRY(OTP_ENTRY_DAI),', file=cfp) + print(' OT_OTP_NAME_ENTRY(OTP_ENTRY_KDI),', file=cfp) print(' /* clang-format on */', file=cfp) print('};', file=cfp) print(file=cfp) diff --git a/python/qemu/ot/otp/map.py b/python/qemu/ot/otp/map.py index cf8feb7781150..bde13c51cad3f 100644 --- a/python/qemu/ot/otp/map.py +++ b/python/qemu/ot/otp/map.py @@ -7,7 +7,7 @@ """ from logging import getLogger -from typing import Any, Iterator, Optional, TextIO +from typing import Any, Iterator, NamedTuple, Optional, TextIO try: # try to load HJSON if available @@ -19,6 +19,22 @@ from ot.util.misc import retrieve_git_version, round_up +class OtpKeySeed(NamedTuple): + """Key seed definition.""" + + name: str + """Name of the key seed.""" + + part_name: str + """Partition name where the key seed is stored.""" + + offset: int + """Offset of the first data byte of the item within the partition.""" + + size: int + """Length in bytes of the item within the partition.""" + + class OtpMap: """OTP configuration. @@ -90,6 +106,23 @@ def find_field(self, field: str) -> list['OtpPartition']: """ return [p for p in self.enumerate_partitions() if p.has_field(field)] + @property + def key_seeds(self) -> list[OtpKeySeed]: + """Return the list of defined key seeds. + + :return: a list of OtpKeySeed + """ + key_seeds: list[OtpKeySeed] = [] + key_seed_suffix = '_KEY_SEED' + for part in self.enumerate_partitions(): + for prop in part.enumerate_properties(): + if not prop.name.endswith(key_seed_suffix): + continue + key_seed = OtpKeySeed(prop.name.removesuffix(key_seed_suffix), + part.name, prop.offset, prop.size) + key_seeds.append(key_seed) + return key_seeds + def _generate_partitions(self) -> None: parts = self._map.get('partitions', []) have_offset = all('offset' in p for p in parts) diff --git a/python/qemu/ot/otp/partition.py b/python/qemu/ot/otp/partition.py index ac42ad0ac3e9c..b4c4cce26a3f8 100644 --- a/python/qemu/ot/otp/partition.py +++ b/python/qemu/ot/otp/partition.py @@ -10,7 +10,8 @@ from io import BytesIO from logging import getLogger from re import IGNORECASE, match -from typing import BinaryIO, NamedTuple, Optional, Sequence, TextIO, Union +from typing import (BinaryIO, Iterator, NamedTuple, Optional, Sequence, TextIO, + Union) from .lifecycle import OtpLifecycle @@ -433,6 +434,18 @@ def has_field(self, field: str) -> bool: except ValueError: return False + def enumerate_properties(self) -> Iterator[OtpPartitionItemProp]: + """Enumerate all partition properties.""" + offset = 0 + itsize = 0 + mubi8 = False + for itname, itdef in self.items.items(): + itsize = itdef['size'] + mubi8 = itsize == 1 and itdef.get('ismubi', False) + yield OtpPartitionItemProp(itname, offset, itsize, offset + itsize, + mubi8) + offset += itsize + def document_fields(self) -> list[str]: """Return the documentation of each 16-bit word. @@ -463,7 +476,7 @@ def document_fields(self) -> list[str]: fields.extend([f'{self.name}_ZER'] * (self.ZER_SIZE // 2)) return fields - def _retrieve_properties(self, field: str) -> tuple[int, int]: + def _retrieve_properties(self, field: str) -> OtpPartitionItemProp: is_digest = self.has_digest and field.upper() == 'DIGEST' if not is_digest: if field not in self.items: diff --git a/scripts/opentitan/clang-tidy.yml b/scripts/opentitan/clang-tidy.yml index 1e40512c7f660..fac54d7bb36b6 100644 --- a/scripts/opentitan/clang-tidy.yml +++ b/scripts/opentitan/clang-tidy.yml @@ -26,7 +26,7 @@ Checks: '*, -hicpp-no-assembler, -hicpp-signed-bitwise, -llvmlibc-*, - -llvm-include-order, + -llvm-*, -misc-include-cleaner, -modernize-macro-to-enum, -performance-no-int-to-ptr,