From 56b9ab21a4dace5b776ba4de29047a620be35e05 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 30 Oct 2025 12:05:39 +0100 Subject: [PATCH 01/14] [ot] scripts/opentitan: clang-tidy.yml: disable invalid rules Disable all LLVM rules as they hard-code paths from LLVM project. llvm-header-guard therefore suggests to replace header guard path with the full absolute path of the current local check out directory path! It appears that llvm-* rules are only to be used for the LLVM project itself, but they are nevertheless automatically added with the default `*` wildcard... Signed-off-by: Emmanuel Blot --- scripts/opentitan/clang-tidy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, From 2a4e89471539ec9dccbee767c356bf6fde06cfb1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 Oct 2025 11:10:22 +0100 Subject: [PATCH 02/14] [ot] python/qemu: ot.otp.descriptor: add name string to partition generator Signed-off-by: Emmanuel Blot --- python/qemu/ot/otp/descriptor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/qemu/ot/otp/descriptor.py b/python/qemu/ot/otp/descriptor.py index 4712a0afc19a4..6284acd248584 100644 --- a/python/qemu/ot/otp/descriptor.py +++ b/python/qemu/ot/otp/descriptor.py @@ -91,6 +91,7 @@ def save(self, kind: str, hjname: str, scriptname: str, cfp: TextIO) \ print('static const OtOTPPartDesc OtOTPPartDescs[] = {', 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: From 65a5a812f8e89eeb5f111fee2d0d9207a470bdc0 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 Oct 2025 10:46:22 +0100 Subject: [PATCH 03/14] [ot] hw/opentitan: ot_otp_dj: prepare unification of OtOTP devices. - move partition names to the OtOTPPartDesc array - allocate arrays of string properties and their matching byte values at runtime, - rename partition-related variables, as several new variables are added - replace statically defined constants with dynamic variables - clean up Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 663 +++++++++++++++++---------------- hw/opentitan/ot_otp_dj_parts.c | 22 ++ 2 files changed, 373 insertions(+), 312 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 85cd469cc60bc..f78b9c682c45c 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -626,6 +626,7 @@ typedef enum { /* TODO: wr and rd lock need to be rewritten (not simple boolean) */ typedef struct { + const char *name; uint16_t size; uint16_t offset; uint16_t digest_offset; @@ -645,8 +646,6 @@ typedef struct { #define OT_OTP_DJ_PARTS -#define OTP_PART_LIFE_CYCLE_SIZE 88u - /* NOLINTNEXTLINE */ #include "ot_otp_dj_parts.c" @@ -665,7 +664,7 @@ typedef struct { OtOTPUnbufState u; } state; struct { - uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ + 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 */ @@ -688,7 +687,7 @@ typedef struct { OtOTPError error; ot_otp_program_ack_fn ack_fn; void *ack_data; - uint16_t data[OTP_PART_LIFE_CYCLE_SIZE / sizeof(uint16_t)]; + uint16_t *data; unsigned hpos; /* current offset in data */ } OtOTPLCIController; @@ -747,7 +746,10 @@ struct OtOTPDjState { OtOTPLcBroadcast lc_broadcast; OtOTPDAIController *dai; OtOTPLCIController *lci; - OtOTPPartController *partctrls; + OtOTPPartController *part_ctrls; + const OtOTPPartDesc *part_descs; + unsigned part_count; + unsigned part_life_cycle; /* index of the Life Cycle partition */ OtOTPKeyGen *keygen; OtOTPScrmblKeyInit *scrmbl_key_init; OtOtpBeCharacteristics be_chars; @@ -756,8 +758,8 @@ struct OtOTPDjState { 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 */ + uint8_t **otp_scramble_keys; /* some entries may be NULL */ + uint8_t **inv_default_parts; /* some entries may be NULL */ OtOTPStorage *otp; OtOTPHWCfg *hw_cfg; @@ -773,8 +775,8 @@ struct OtOTPDjState { 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 */ + 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; }; @@ -926,35 +928,6 @@ static const char *OTP_TOKEN_NAMES[] = { /* 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 */ @@ -991,8 +964,6 @@ static const char *ERR_CODE_NAMES[] = { ((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_)] : \ @@ -1023,26 +994,6 @@ ot_otp_dj_lci_change_state_line(OtOTPDjState *s, OtOTPLCIState state, int line); #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]; @@ -1090,14 +1041,54 @@ static void ot_otp_dj_update_alerts(OtOTPDjState *s) } } -static bool ot_otp_dj_is_wide_granule(unsigned part_ix, unsigned address) +static const char *ot_otp_dj_part_name(const OtOTPDjState *s, unsigned part_ix) +{ + if (part_ix < s->part_count) { + return s->part_descs[part_ix].name; + } + + if (part_ix == s->part_count) { + return "DAI"; + } + + if (part_ix == s->part_count + 1u) { + return "KDI"; + } + + return "?"; +} + +static inline unsigned +ot_otp_dj_part_data_offset(const OtOTPDjState *s, unsigned part_ix) +{ + return (unsigned)(s->part_descs[part_ix].offset); +} + +static inline unsigned +ot_otp_dj_part_data_byte_size(const OtOTPDjState *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) * NUM_DIGEST_WORDS; + } + + if (s->part_descs[part_ix].zeroizable) { + size -= sizeof(uint32_t) * NUM_ZER_WORDS; + } + + return (unsigned)size; +} + +static bool ot_otp_dj_is_wide_granule(const OtOTPDjState *s, unsigned part_ix, + unsigned address) { - if (part_ix < OTP_PART_COUNT) { - if (OtOTPPartDescs[part_ix].secret) { + if (part_ix < s->part_count) { + if (s->part_descs[part_ix].secret) { return true; } - if (OtOTPPartDescs[part_ix].digest_offset == + if (s->part_descs[part_ix].digest_offset == (address & OTP_DIGEST_ADDR_MASK)) { return true; } @@ -1106,19 +1097,19 @@ static bool ot_otp_dj_is_wide_granule(unsigned part_ix, unsigned address) return false; } -static bool ot_otp_dj_is_buffered(unsigned part_ix) +static bool ot_otp_dj_is_buffered(const OtOTPDjState *s, unsigned part_ix) { - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].buffered; + if (part_ix < s->part_count) { + return s->part_descs[part_ix].buffered; } return false; } -static bool ot_otp_dj_is_secret(unsigned part_ix) +static bool ot_otp_dj_is_secret(const OtOTPDjState *s, unsigned part_ix) { - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].secret; + if (part_ix < s->part_count) { + return s->part_descs[part_ix].secret; } return false; @@ -1140,10 +1131,25 @@ static bool ot_otp_dj_is_ecc_enabled(const OtOTPDjState *s) ot_otp_dj_is_backend_ecc_enabled(s); } -static bool ot_otp_dj_has_digest(unsigned partition) +static bool ot_otp_dj_has_digest(const OtOTPDjState *s, unsigned part_ix) +{ + return s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest; +} + +static bool ot_otp_dj_is_part_digest_offset(const OtOTPDjState *s, + unsigned part_ix, hwaddr addr) { - return OtOTPPartDescs[partition].hw_digest || - OtOTPPartDescs[partition].sw_digest; + uint16_t offset = s->part_descs[part_ix].digest_offset; + + return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); +} + +static bool ot_otp_dj_is_part_zer_offset(const OtOTPDjState *s, + unsigned part_ix, hwaddr addr) +{ + uint16_t offset = s->part_descs[part_ix].zer_offset; + + return (offset != UINT16_MAX) && ((addr & ~OTP_ZER_ADDR_MASK) == offset); } static void ot_otp_dj_disable_all_partitions(OtOTPDjState *s) @@ -1151,22 +1157,23 @@ static void ot_otp_dj_disable_all_partitions(OtOTPDjState *s) DAI_CHANGE_STATE(s, OTP_DAI_ERROR); LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - for (unsigned pix = 0; pix < OTP_PART_COUNT; pix++) { - OtOTPPartController *pctrl = &s->partctrls[pix]; + for (unsigned pix = 0; pix < s->part_count; pix++) { + OtOTPPartController *pctrl = &s->part_ctrls[pix]; pctrl->failed = true; } } -static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part, OtOTPError err) +static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part_ix, + OtOTPError err) { - g_assert(part < OTP_ENTRY_COUNT); + g_assert(part_ix < 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); + if (errval || errval != s->regs[R_ERR_CODE_0 + part_ix]) { + trace_ot_otp_set_error(s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix, ERR_CODE_NAME(err), err); } - s->regs[R_ERR_CODE_0 + part] = errval; + s->regs[R_ERR_CODE_0 + part_ix] = errval; switch (err) { case OTP_MACRO_ERROR: @@ -1224,13 +1231,13 @@ static uint32_t ot_otp_dj_get_status(const OtOTPDjState *s) 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]; + 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, PART_NAME(ix), - ix); - return (OtOTPPartitionType)ix; + trace_ot_otp_addr_to_part(s->ot_id, (uint32_t)addr, + ot_otp_dj_part_name(s, part_ix), part_ix); + return (OtOTPPartitionType)part_ix; } } @@ -1382,11 +1389,11 @@ 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); + unsigned start = s->part_descs[part_ix].offset >> 2u; + unsigned end = (ot_otp_dj_is_buffered(s, (int)part_ix) && + ot_otp_dj_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++) { @@ -1403,9 +1410,10 @@ static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned part_ix) */ 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), + trace_ot_otp_ecc_init_error(s->ot_id, + ot_otp_dj_part_name(s, part_ix), part_ix, ix << 2u, *word, ecc); - s->partctrls[part_ix].failed = true; + s->part_ctrls[part_ix].failed = true; return -1; } } @@ -1416,9 +1424,9 @@ static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned part_ix) static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) { - g_assert(!ot_otp_dj_is_buffered(part_ix)); + g_assert(!ot_otp_dj_is_buffered(s, part_ix)); - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; + uint16_t offset = s->part_descs[part_ix].digest_offset; if (offset == UINT16_MAX) { return 0u; @@ -1427,7 +1435,7 @@ static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) 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)) { + if (s->part_descs[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); @@ -1438,20 +1446,6 @@ static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) 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) { /* @@ -1480,8 +1474,8 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) /* 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)) { + for (unsigned part_dig_ix = 0; part_ix < s->part_count; part_ix++) { + if (ot_otp_dj_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 @@ -1499,11 +1493,10 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) * 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); + g_assert(s->part_life_cycle == s->part_count - 1u); + g_assert(part_ix < s->part_count); - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + const OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; uint64_t digest = pctrl->digest; if (hi) { @@ -1513,12 +1506,20 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) return (uint32_t)digest; } -static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) +static uint32_t +ot_otp_dj_get_sw_readlock(const OtOTPDjState *s, unsigned rdlk_ix) +{ + uint32_t reg = R_VENDOR_TEST_READ_LOCK + rdlk_ix; + + return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); +} + +static bool ot_otp_dj_is_readable(const OtOTPDjState *s, unsigned part_ix) { - g_assert(part_ix < OTP_PART_COUNT); + g_assert(part_ix < s->part_count); - const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + 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. */ @@ -1537,7 +1538,7 @@ static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) unsigned roffset = 0; unsigned pix; - for (pix = 0; pix < OTP_PART_COUNT; pix++) { + for (pix = 0; pix < s->part_count; pix++) { if (pix == part_ix) { break; } @@ -1551,21 +1552,19 @@ static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) * 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); + g_assert(!s->part_descs[s->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); + g_assert(pix < s->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); + return ot_otp_dj_get_sw_readlock(s, roffset); } static void @@ -1649,18 +1648,18 @@ static void ot_otp_dj_lc_broadcast_bh(void *opaque) 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; + 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 < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; + 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; @@ -1713,7 +1712,7 @@ static uint64_t ot_otp_dj_compute_partition_digest( static uint64_t ot_otp_dj_load_partition_digest(OtOTPDjState *s, unsigned partition) { - unsigned digoff = (unsigned)OtOTPPartDescs[partition].digest_offset; + 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?", @@ -1735,12 +1734,12 @@ ot_otp_dj_load_partition_digest(OtOTPDjState *s, unsigned partition) return digest; } -static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned ix) +static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_dj_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); @@ -1757,13 +1756,14 @@ static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned ix) g_assert(pctrl->buffer.data != NULL); uint64_t *clear = (uint64_t *)pctrl->buffer.data; - const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; + const uint8_t *scrambling_key = s->otp_scramble_keys[part_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); + trace_ot_otp_unscramble_partition(s->ot_id, ot_otp_dj_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]); @@ -1772,30 +1772,30 @@ static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned ix) ot_present_free(ps); } -static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned ix) +static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; g_assert(pctrl->buffer.data != NULL); - if (OtOTPPartDescs[ix].hw_digest) { - pctrl->digest = ot_otp_dj_load_partition_digest(s, ix); + if (s->part_descs[part_ix].hw_digest) { + pctrl->digest = ot_otp_dj_load_partition_digest(s, part_ix); } else { pctrl->digest = 0; } - if (OtOTPPartDescs[ix].secret) { + 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_dj_unscramble_partition(s, ix); + ot_otp_dj_unscramble_partition(s, part_ix); } } else { - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_dj_part_data_byte_size(s, part_ix); const uint8_t *base = (const uint8_t *)s->otp->data; base += offset; @@ -1805,12 +1805,13 @@ static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned ix) } static void -ot_otp_dj_check_buffered_partition_integrity(OtOTPDjState *s, unsigned ix) +ot_otp_dj_check_buffered_partition_integrity(OtOTPDjState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; if (pctrl->digest == 0) { - trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); + trace_ot_otp_skip_digest(s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix); pctrl->locked = false; return; } @@ -1820,27 +1821,28 @@ ot_otp_dj_check_buffered_partition_integrity(OtOTPDjState *s, unsigned ix) /* * 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); + const uint8_t *part_data = ((const uint8_t *)s->otp->data) + + ot_otp_dj_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_dj_part_data_byte_size(s, part_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_ot_otp_mismatch_digest(s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix, digest, pctrl->digest); TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - PART_NAME(ix), digest, + ot_otp_dj_part_name(s, part_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); + ot_otp_dj_set_error(s, part_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"); + trace_ot_otp_integrity_report(s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix, "digest OK"); } } @@ -1916,7 +1918,9 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) return; } - if (partition >= OTP_PART_LIFE_CYCLE) { + 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", @@ -1925,34 +1929,33 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) return; } - const OtOTPPartController *pctrl = &s->partctrls[partition]; + 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, PART_NAME(partition)); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix)); 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); + bool is_wide = ot_otp_dj_is_wide_granule(s, part_ix, address); + bool is_buffered = ot_otp_dj_is_buffered(s, part_ix); + bool is_secret = ot_otp_dj_is_secret(s, part_ix); + bool is_digest = ot_otp_dj_is_part_digest_offset(s, part_ix, address); + bool is_zer = ot_otp_dj_is_part_zer_offset(s, 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); + s->ot_id, ot_otp_dj_part_name(s, part_ix), 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_offset = address - ot_otp_dj_part_data_offset(s, part_ix); unsigned part_waddr = part_offset >> 2u; bool do_ecc = - OtOTPPartDescs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s); + s->part_descs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s); DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); @@ -1962,14 +1965,14 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) 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)); + data += (ot_otp_dj_part_data_offset(s, part_ix) / 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); + s->part_descs[part_ix].size); data_lo = data[part_waddr]; data_hi = data[part_waddr + 1u]; @@ -1987,8 +1990,7 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) cell_count *= 2u; } else { /* 32-bit request */ - g_assert(part_waddr * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); + g_assert(part_waddr * sizeof(uint32_t) < s->part_descs[part_ix].size); data_lo = data[part_waddr]; data_hi = 0u; @@ -2014,7 +2016,7 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) * 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]; + const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; g_assert(scrambling_key); uint64_t tmp_data = ((uint64_t)data_hi << 32u) | data_lo; OtPresentState *ps = ot_present_new(); @@ -2059,8 +2061,8 @@ static int ot_otp_dj_dai_write_u64(OtOTPDjState *s, unsigned address) 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); + bool is_secret = ot_otp_dj_is_secret(s, part_ix); + bool is_zer = ot_otp_dj_is_part_zer_offset(s, part_ix, address); if (is_secret && !is_zer) { const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; @@ -2117,7 +2119,9 @@ static int ot_otp_dj_dai_write_u64(OtOTPDjState *s, unsigned address) return -1; } - trace_ot_otp_dai_new_dword_ecc(s->ot_id, PART_NAME(s->dai->partition), + trace_ot_otp_dai_new_dword_ecc(s->ot_id, + ot_otp_dj_part_name(s, (unsigned)s->dai + ->partition), s->dai->partition, *dst, *edst); } @@ -2170,7 +2174,9 @@ static int ot_otp_dj_dai_write_u32(OtOTPDjState *s, unsigned address) return -1; } - trace_ot_otp_dai_new_word_ecc(s->ot_id, PART_NAME(s->dai->partition), + trace_ot_otp_dai_new_word_ecc(s->ot_id, + ot_otp_dj_part_name(s, (unsigned)s->dai + ->partition), s->dai->partition, *dst, *edst); } @@ -2212,7 +2218,7 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) unsigned part_ix = (unsigned)partition; - if (part_ix >= OTP_PART_LIFE_CYCLE) { + if (part_ix >= s->part_life_cycle) { qemu_log_mask( LOG_GUEST_ERROR, "%s: %s: Life cycle partition cannot be accessed from DAI\n", @@ -2221,17 +2227,18 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) return; } - OtOTPPartController *pctrl = &s->partctrls[part_ix]; + 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, PART_NAME(part_ix)); + __func__, s->ot_id, ot_otp_dj_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, PART_NAME(part_ix), part_ix); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2239,22 +2246,22 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) 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); + s->ot_id, ot_otp_dj_part_name(s, 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); + bool is_digest = ot_otp_dj_is_part_digest_offset(s, part_ix, address); + bool is_wide = ot_otp_dj_is_wide_granule(s, part_ix, address); if (is_digest) { - if (OtOTPPartDescs[partition].hw_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, PART_NAME(part_ix), part_ix); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix), part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2320,7 +2327,9 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) return; } - if (partition >= OTP_PART_LIFE_CYCLE) { + unsigned part_ix = (unsigned)partition; + + if (part_ix >= s->part_life_cycle) { qemu_log_mask( LOG_GUEST_ERROR, "%s: %s: Life cycle partition cannot be accessed from DAI\n", @@ -2329,25 +2338,27 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) return; } - if (!OtOTPPartDescs[partition].hw_digest) { + 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, PART_NAME(partition), partition); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } - OtOTPPartController *pctrl = &s->partctrls[partition]; + 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, PART_NAME(partition)); + __func__, s->ot_id, ot_otp_dj_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, PART_NAME(partition), partition); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2355,16 +2366,16 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) 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); + s->ot_id, ot_otp_dj_part_name(s, part_ix), part_ix); 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); + const uint8_t *data = ((const uint8_t *)s->otp->data) + + ot_otp_dj_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_dj_part_data_byte_size(s, part_ix); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -2387,12 +2398,14 @@ 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)); + g_assert((s->dai->partition >= 0) && (s->dai->partition < s->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 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; @@ -2436,8 +2449,8 @@ static void ot_otp_dj_dai_write_digest(void *opaque) return; } - trace_ot_otp_dai_new_digest_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); + trace_ot_otp_dai_new_digest_ecc(s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix, *dst, *edst); DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); @@ -2454,8 +2467,10 @@ static void ot_otp_dj_dai_complete(void *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, + trace_ot_otp_dai_read(s->ot_id, + ot_otp_dj_part_name(s, + (unsigned)s->dai->partition), + (unsigned)s->dai->partition, s->regs[R_DIRECT_ACCESS_RDATA_1], s->regs[R_DIRECT_ACCESS_RDATA_1]); s->dai->partition = -1; @@ -2839,10 +2854,9 @@ 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); + (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); @@ -2855,7 +2869,7 @@ 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_dj_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); @@ -2864,8 +2878,8 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( } 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_digest = ot_otp_dj_is_part_digest_offset(s, part_ix, addr); + bool is_zer = ot_otp_dj_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"); @@ -2905,15 +2919,15 @@ static void ot_otp_dj_get_lc_info( } if (lc_valid) { - *lc_valid = !(ds->partctrls[OTP_PART_SECRET0].failed || - ds->partctrls[OTP_PART_SECRET2].failed || - ds->partctrls[OTP_PART_LIFE_CYCLE].failed) ? + *lc_valid = !(ds->part_ctrls[OTP_PART_SECRET0].failed || + ds->part_ctrls[OTP_PART_SECRET2].failed || + ds->part_ctrls[ds->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) ? + *secret_valid = (!ds->part_ctrls[OTP_PART_SECRET2].failed && + ds->part_ctrls[OTP_PART_SECRET2].locked) ? OT_MULTIBITBOOL_LC4_TRUE : OT_MULTIBITBOOL_LC4_FALSE; } @@ -3043,10 +3057,10 @@ static void ot_otp_dj_generate_scrambling_key( 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)); + OtOTPPartController *pctrl = &s->part_ctrls[OTP_PART_SECRET1]; + g_assert(ot_otp_dj_is_buffered(s, OTP_PART_SECRET1)); uint32_t poffset = - OtOTPPartDescs[OTP_PART_SECRET1].offset / sizeof(uint32_t); + s->part_descs[OTP_PART_SECRET1].offset / sizeof(uint32_t); const uint32_t *key_seed = &pctrl->buffer.data[key_reg - poffset]; /* check the key seed's validity */ @@ -3148,20 +3162,20 @@ static void ot_otp_dj_get_keymgr_secret( case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: partition = OTP_PART_SECRET2; offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - - OtOTPPartDescs[partition].offset; + ds->part_descs[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; + ds->part_descs[partition].offset; break; case OTP_KEYMGR_SECRET_CREATOR_SEED: partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_SEED - OtOTPPartDescs[partition].offset; + offset = A_SECRET2_CREATOR_SEED - ds->part_descs[partition].offset; break; case OTP_KEYMGR_SECRET_OWNER_SEED: partition = OTP_PART_SECRET3; - offset = A_SECRET3_OWNER_SEED - OtOTPPartDescs[partition].offset; + offset = A_SECRET3_OWNER_SEED - ds->part_descs[partition].offset; break; default: error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, @@ -3172,17 +3186,17 @@ static void ot_otp_dj_get_keymgr_secret( } unsigned part_ix = (unsigned)partition; - g_assert(ot_otp_dj_is_buffered(part_ix)); + g_assert(ot_otp_dj_is_buffered(ds, 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; + data_ptr = (const uint8_t *)ds->part_ctrls[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; + secret->valid = ds->part_ctrls[part_ix].digest != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3220,7 +3234,7 @@ static bool ot_otp_dj_program_req(OtOTPState *s, const uint16_t *lc_tcount, 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)); + ds->part_descs[ds->part_life_cycle].size / sizeof(uint16_t)); /* current position in LC buffer to write to backend */ lci->hpos = 0u; @@ -3245,7 +3259,7 @@ static void ot_otp_dj_lci_write_complete(OtOTPDjState *s, bool success) * 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]; + const OtOTPPartDesc *lcdesc = &s->part_descs[s->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], @@ -3282,7 +3296,7 @@ static void ot_otp_dj_lci_write_complete(OtOTPDjState *s, bool success) lci->hpos = 0u; if (!success && lci->error != OTP_NO_ERROR) { - ot_otp_dj_set_error(s, OTP_PART_LIFE_CYCLE, lci->error); + ot_otp_dj_set_error(s, s->part_life_cycle, lci->error); } (*ack_fn)(ack_data, success); @@ -3292,7 +3306,7 @@ 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]; + const OtOTPPartDesc *lcdesc = &s->part_descs[s->part_life_cycle]; /* should not be called if already in error */ if (lci->state == OTP_LCI_ERROR) { @@ -3434,8 +3448,8 @@ static void ot_otp_dj_pwr_load(OtOTPDjState *s) 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; + 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 */ @@ -3563,28 +3577,28 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) switch (tkx) { case OTP_TOKEN_TEST_UNLOCK: - partition = OTP_PART_SECRET0; + partition = (unsigned)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; + secret_addr = (unsigned)A_SECRET0_TEST_EXIT_TOKEN; break; case OTP_TOKEN_RMA: partition = OTP_PART_SECRET2; - secret_addr = A_SECRET2_RMA_TOKEN; + secret_addr = (unsigned)A_SECRET2_RMA_TOKEN; break; default: g_assert_not_reached(); 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_dj_part_data_offset(s, partition); g_assert(secret_offset + sizeof(OtOTPTokenValue) <= OtOTPPartDescs[partition].size); @@ -3592,7 +3606,7 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) 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; } @@ -3605,25 +3619,25 @@ 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++) { + for (unsigned ix = 0; ix < s->part_count; ix++) { /* sanity check: all secret partitions are also buffered */ - g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); + g_assert(!s->part_descs[ix].secret || s->part_descs[ix].buffered); - if (ot_otp_dj_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { + if (ot_otp_dj_is_ecc_enabled(s) && s->part_descs[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; + if (s->part_descs[ix].sw_digest) { + s->part_ctrls[ix].digest = ot_otp_dj_get_part_digest(s, ix); + s->part_ctrls[ix].locked = s->part_ctrls[ix].digest != 0; continue; } - if (OtOTPPartDescs[ix].buffered) { + if (s->part_descs[ix].buffered) { ot_otp_dj_bufferize_partition(s, ix); - if (OtOTPPartDescs[ix].hw_digest) { + if (s->part_descs[ix].hw_digest) { ot_otp_dj_check_buffered_partition_integrity(s, ix); } continue; @@ -3799,54 +3813,65 @@ static void ot_otp_dj_configure_sram(OtOTPDjState *s) static void ot_otp_dj_configure_part_scramble_keys(OtOTPDjState *s) { - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { continue; } - if (!s->otp_scramble_key_xstrs[ix]) { + if (!s->otp_scramble_key_xstrs[part_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); + __func__, s->ot_id, ot_otp_dj_part_name(s, part_ix), + part_ix); return; } continue; } - size_t len = strlen(s->otp_scramble_key_xstrs[ix]); + size_t len = strlen(s->otp_scramble_key_xstrs[part_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); + __func__, s->ot_id, len, ot_otp_dj_part_name(s, part_ix), + part_ix); return; } - g_assert(!s->otp_scramble_keys[ix]); + g_assert(!s->otp_scramble_keys[part_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], + s->otp_scramble_keys[part_ix] = + g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); + if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], + s->otp_scramble_key_xstrs[part_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)); + __func__, s->ot_id, part_ix, + ot_otp_dj_part_name(s, part_ix)); return; } - TRACE_OTP("otp_scramble_keys[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->otp_scramble_keys[ix], + TRACE_OTP("otp_scramble_keys[%s] %s", ot_otp_dj_part_name(s, part_ix), + ot_otp_hexdump(s, s->otp_scramble_keys[part_ix], OTP_SCRAMBLING_KEY_BYTES)); } } -static void ot_otp_dj_class_add_scramble_key_props(OtOTPClass *odc) +static void ot_otp_dj_add_scramble_key_props(OtOTPDjState *s) { + /* + * @todo: we know the number of secret partitions, so use it rather than + * whole partition count + */ + s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); + s->otp_scramble_key_xstrs = g_new0(char *, s->part_count); + unsigned secret_ix = 0u; - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { continue; } @@ -3858,68 +3883,79 @@ static void ot_otp_dj_class_add_scramble_key_props(OtOTPClass *odc) */ 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; + /* + * 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[part_ix] - (intptr_t)s; - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); + object_property_add(OBJECT(s), 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]) { + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + if (!s->inv_default_part_xstrs[part_ix]) { continue; } - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; + const OtOTPPartDesc *part = &s->part_descs[part_ix]; size_t len; - len = strlen(s->inv_default_part_xstrs[ix]); + 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, ix); + s->ot_id, part_ix); return; } - g_assert(!s->inv_default_parts[ix]); + g_assert(!s->inv_default_parts[part_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)) { + s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); + if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], + 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, ix); + __func__, s->ot_id, part_ix); return; } - TRACE_OTP("inv_default_part[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->inv_default_parts[ix], part->size)); + TRACE_OTP("inv_default_part[%s] %s", ot_otp_dj_part_name(s, part_ix), + ot_otp_hexdump(s, s->inv_default_parts[part_ix], part->size)); } } -static void ot_otp_dj_class_add_inv_def_props(OtOTPClass *odc) +static void ot_otp_dj_add_inv_def_props(OtOTPDjState *s) { - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].buffered) { + s->inv_default_parts = g_new0(uint8_t *, 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", ix); + prop->name = g_strdup_printf("inv_default_part_%u", part_ix); prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPDjState, inv_default_part_xstrs) + - sizeof(char *) * ix; + /* + * 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_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); + object_property_add(OBJECT(s), prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, prop); } } @@ -4026,21 +4062,21 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) ot_otp_dj_update_alerts(s); ibex_irq_set(&s->pwc_otp_rsp, 0); - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { /* TODO: initialize with actual default partition data once known */ - if (OtOTPPartDescs[ix].buffered) { - s->partctrls[ix].state.b = OTP_BUF_IDLE; + if (s->part_descs[part_ix].buffered) { + s->part_ctrls[part_ix].state.b = OTP_BUF_IDLE; } else { - s->partctrls[ix].state.u = OTP_UNBUF_IDLE; + s->part_ctrls[part_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; + unsigned part_size = ot_otp_dj_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, OTP_DAI_RESET); @@ -4136,23 +4172,29 @@ static void ot_otp_dj_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_dj_lc_broadcast_recv, OT_LC_BROADCAST, OT_OTP_LC_BROADCAST_COUNT); + s->part_descs = OtOTPPartDescs; + s->part_count = ARRAY_SIZE(OtOTPPartDescs); + s->part_life_cycle = OTP_PART_LIFE_CYCLE; + 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->part_ctrls = g_new0(OtOTPPartController, s->part_count); 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_life_cycle].size / + sizeof(uint16_t)); - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (!OtOTPPartDescs[ix].buffered) { + 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_dj_part_data_byte_size(ix) / sizeof(uint32_t); - s->partctrls[ix].buffer.data = g_new0(uint32_t, part_words); + ot_otp_dj_part_data_byte_size(s, part_ix) / sizeof(uint32_t); + s->part_ctrls[part_ix].buffer.data = g_new0(uint32_t, part_words); } ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT); @@ -4170,6 +4212,9 @@ static void ot_otp_dj_init(Object *obj) int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); ot_prng_reseed(s->keygen->prng, (uint32_t)now); + ot_otp_dj_add_scramble_key_props(s); + ot_otp_dj_add_inv_def_props(s); + #ifdef OT_OTP_DEBUG s->hexstr = g_new0(char, OT_OTP_HEXSTR_SIZE); #endif @@ -4180,9 +4225,6 @@ 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); @@ -4198,9 +4240,6 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) oc->get_otp_key = &ot_otp_dj_get_otp_key; oc->get_keymgr_secret = &ot_otp_dj_get_keymgr_secret; oc->program_req = &ot_otp_dj_program_req; - - ot_otp_dj_class_add_scramble_key_props(oc); - ot_otp_dj_class_add_inv_def_props(oc); } static const TypeInfo ot_otp_dj_info = { diff --git a/hw/opentitan/ot_otp_dj_parts.c b/hw/opentitan/ot_otp_dj_parts.c index 411802ead16f5..2073bb692219d 100644 --- a/hw/opentitan/ot_otp_dj_parts.c +++ b/hw/opentitan/ot_otp_dj_parts.c @@ -10,6 +10,7 @@ /* NOLINTBEGIN */ static const OtOTPPartDesc OtOTPPartDescs[] = { [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, From 094bd8ff4bd3f26177d4848b028a24d84e57a50e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 Oct 2025 15:30:01 +0100 Subject: [PATCH 04/14] [ot] hw/opentitan: ot_otp_eg: prepare unification of OtOTP devices. - move partition names to the OtOTPPartDesc array - allocate arrays of string properties and their matching byte values at runtime, - rename partition-related variables, as several new variables are added - replace statically defined constants with dynamic variables - clean up Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_eg.c | 616 ++++++++++++++++++--------------- hw/opentitan/ot_otp_eg_parts.c | 11 + 2 files changed, 346 insertions(+), 281 deletions(-) diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 068cc8e933c3b..c76f5ba29489b 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -563,6 +563,7 @@ typedef enum { /* TODO: wr and rd lock need to be rewritten (not simple boolean) */ typedef struct { + const char *name; uint16_t size; uint16_t offset; uint16_t digest_offset; @@ -599,7 +600,7 @@ typedef struct { OtOTPUnbufState u; } state; struct { - uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ + 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 */ @@ -622,7 +623,7 @@ typedef struct { OtOTPError error; ot_otp_program_ack_fn ack_fn; void *ack_data; - uint16_t data[OTP_PART_LIFE_CYCLE_SIZE / sizeof(uint16_t)]; + uint16_t *data; unsigned hpos; /* current offset in data */ } OtOTPLCIController; @@ -681,7 +682,10 @@ struct OtOTPEgState { OtOTPLcBroadcast lc_broadcast; OtOTPDAIController *dai; OtOTPLCIController *lci; - OtOTPPartController *partctrls; + OtOTPPartController *part_ctrls; + const OtOTPPartDesc *part_descs; + unsigned part_count; + unsigned part_life_cycle; /* index of the Life Cycle partition */ OtOTPKeyGen *keygen; OtOTPScrmblKeyInit *scrmbl_key_init; OtOtpBeCharacteristics be_chars; @@ -694,8 +698,8 @@ struct OtOTPEgState { 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 */ + uint8_t **otp_scramble_keys; /* some entries may be NULL */ + uint8_t **inv_default_parts; /* some entries may be NULL */ OtOTPStorage *otp; OtOTPHWCfg *hw_cfg; @@ -715,8 +719,8 @@ struct OtOTPEgState { 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 */ + 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; }; @@ -829,24 +833,9 @@ static const char *OTP_TOKEN_NAMES[] = { /* 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), +/* clang-format off */ /* fake partitions */ - OTP_NAME_ENTRY(OTP_ENTRY_DAI), - OTP_NAME_ENTRY(OTP_ENTRY_KDI), - /* clang-format on */ -}; +/* clang-format on */ static const char *ERR_CODE_NAMES[] = { /* clang-format off */ @@ -883,8 +872,6 @@ static const char *ERR_CODE_NAMES[] = { ((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_)] : \ @@ -915,21 +902,6 @@ ot_otp_eg_lci_change_state_line(OtOTPEgState *s, OtOTPLCIState state, int line); #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) { @@ -978,14 +950,50 @@ static void ot_otp_eg_update_alerts(OtOTPEgState *s) } } -static bool ot_otp_eg_is_wide_granule(unsigned part_ix, unsigned address) +static const char *ot_otp_eg_part_name(const OtOTPEgState *s, unsigned part_ix) { - if (part_ix < OTP_PART_COUNT) { - if (OtOTPPartDescs[part_ix].secret) { + if (part_ix < s->part_count) { + return s->part_descs[part_ix].name; + } + + if (part_ix == s->part_count) { + return "DAI"; + } + + if (part_ix == s->part_count + 1u) { + return "KDI"; + } + + return "?"; +} + +static inline unsigned +ot_otp_eg_part_data_offset(const OtOTPEgState *s, unsigned part_ix) +{ + return (unsigned)(s->part_descs[part_ix].offset); +} + +static inline unsigned +ot_otp_eg_part_data_byte_size(const OtOTPEgState *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) * NUM_DIGEST_WORDS; + } + + return (unsigned)size; +} + +static bool ot_otp_eg_is_wide_granule(const OtOTPEgState *s, unsigned part_ix, + unsigned address) +{ + if (part_ix < s->part_count) { + if (s->part_descs[part_ix].secret) { return true; } - if (OtOTPPartDescs[part_ix].digest_offset == + if (s->part_descs[part_ix].digest_offset == (address & OTP_DIGEST_ADDR_MASK)) { return true; } @@ -994,19 +1002,19 @@ static bool ot_otp_eg_is_wide_granule(unsigned part_ix, unsigned address) return false; } -static bool ot_otp_eg_is_buffered(unsigned part_ix) +static bool ot_otp_eg_is_buffered(const OtOTPEgState *s, unsigned part_ix) { - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].buffered; + if (part_ix < s->part_count) { + return s->part_descs[part_ix].buffered; } return false; } -static bool ot_otp_eg_is_secret(unsigned part_ix) +static bool ot_otp_eg_is_secret(const OtOTPEgState *s, unsigned part_ix) { - if (part_ix < OTP_PART_COUNT) { - return OtOTPPartDescs[part_ix].secret; + if (part_ix < s->part_count) { + return s->part_descs[part_ix].secret; } return false; @@ -1028,33 +1036,42 @@ static bool ot_otp_eg_is_ecc_enabled(const OtOTPEgState *s) ot_otp_eg_is_backend_ecc_enabled(s); } -static bool ot_otp_eg_has_digest(unsigned partition) +static bool ot_otp_eg_has_digest(const OtOTPEgState *s, unsigned part_ix) { - return OtOTPPartDescs[partition].hw_digest || - OtOTPPartDescs[partition].sw_digest; + return s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest; } +static bool ot_otp_eg_is_part_digest_offset(const OtOTPEgState *s, + unsigned part_ix, hwaddr addr) +{ + uint16_t offset = s->part_descs[part_ix].digest_offset; + + return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); +} + + static void ot_otp_eg_disable_all_partitions(OtOTPEgState *s) { DAI_CHANGE_STATE(s, OTP_DAI_ERROR); LCI_CHANGE_STATE(s, OTP_LCI_ERROR); - for (unsigned pix = 0; pix < OTP_PART_COUNT; pix++) { - OtOTPPartController *pctrl = &s->partctrls[pix]; + for (unsigned pix = 0; pix < s->part_count; pix++) { + OtOTPPartController *pctrl = &s->part_ctrls[pix]; pctrl->failed = true; } } -static void ot_otp_eg_set_error(OtOTPEgState *s, unsigned part, OtOTPError err) +static void ot_otp_eg_set_error(OtOTPEgState *s, unsigned part_ix, + OtOTPError err) { - g_assert(part < OTP_ENTRY_COUNT); + g_assert(part_ix < 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); + if (errval || errval != s->regs[R_ERR_CODE_0 + part_ix]) { + trace_ot_otp_set_error(s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix, ERR_CODE_NAME(err), err); } - s->regs[R_ERR_CODE_0 + part] = errval; + s->regs[R_ERR_CODE_0 + part_ix] = errval; switch (err) { case OTP_MACRO_ERROR: @@ -1112,13 +1129,13 @@ static uint32_t ot_otp_eg_get_status(const OtOTPEgState *s) 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]; + 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, PART_NAME(ix), - ix); - return (OtOTPPartitionType)ix; + trace_ot_otp_addr_to_part(s->ot_id, (uint32_t)addr, + ot_otp_eg_part_name(s, part_ix), part_ix); + return (OtOTPPartitionType)part_ix; } } @@ -1270,11 +1287,11 @@ 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); + unsigned start = s->part_descs[part_ix].offset >> 2u; + unsigned end = (ot_otp_eg_is_buffered(s, (int)part_ix) && + ot_otp_eg_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++) { @@ -1291,9 +1308,10 @@ static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned part_ix) */ 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), + trace_ot_otp_ecc_init_error(s->ot_id, + ot_otp_eg_part_name(s, part_ix), part_ix, ix << 2u, *word, ecc); - s->partctrls[part_ix].failed = true; + s->part_ctrls[part_ix].failed = true; return -1; } } @@ -1304,9 +1322,9 @@ static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned part_ix) static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) { - g_assert(!ot_otp_eg_is_buffered(part_ix)); + g_assert(!ot_otp_eg_is_buffered(s, part_ix)); - uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; + uint16_t offset = s->part_descs[part_ix].digest_offset; if (offset == UINT16_MAX) { return 0u; @@ -1315,7 +1333,7 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) 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)) { + if (s->part_descs[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); @@ -1326,13 +1344,6 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) 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) { /* @@ -1361,8 +1372,8 @@ static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) /* 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)) { + for (unsigned part_dig_ix = 0; part_ix < s->part_count; part_ix++) { + if (ot_otp_eg_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 @@ -1380,11 +1391,10 @@ static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) * 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); + g_assert(s->part_life_cycle == s->part_count - 1u); + g_assert(part_ix < s->part_count); - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + const OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; uint64_t digest = pctrl->digest; if (hi) { @@ -1394,12 +1404,20 @@ static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) return (uint32_t)digest; } -static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) +static uint32_t +ot_otp_eg_get_sw_readlock(const OtOTPEgState *s, unsigned rdlk_ix) { - g_assert(part_ix < OTP_PART_COUNT); + uint32_t reg = R_VENDOR_TEST_READ_LOCK + rdlk_ix; - const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); +} + +static bool ot_otp_eg_is_readable(const OtOTPEgState *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. */ @@ -1418,7 +1436,7 @@ static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) unsigned roffset = 0; unsigned pix; - for (pix = 0; pix < OTP_PART_COUNT; pix++) { + for (pix = 0; pix < s->part_count; pix++) { if (pix == part_ix) { break; } @@ -1432,21 +1450,19 @@ static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) * 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); + g_assert(!s->part_descs[s->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); + g_assert(pix < s->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); + return ot_otp_eg_get_sw_readlock(s, roffset); } static void @@ -1530,18 +1546,18 @@ static void ot_otp_eg_lc_broadcast_bh(void *opaque) 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; + 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 < OTP_PART_COUNT; ix++) { - if (OtOTPPartDescs[ix].iskeymgr_owner) { - s->partctrls[ix].read_lock = !level; - s->partctrls[ix].write_lock = !level; + 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; @@ -1594,7 +1610,7 @@ static uint64_t ot_otp_eg_compute_partition_digest( static uint64_t ot_otp_eg_load_partition_digest(OtOTPEgState *s, unsigned partition) { - unsigned digoff = (unsigned)OtOTPPartDescs[partition].digest_offset; + 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?", @@ -1616,12 +1632,12 @@ ot_otp_eg_load_partition_digest(OtOTPEgState *s, unsigned partition) return digest; } -static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned ix) +static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_eg_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); @@ -1638,13 +1654,14 @@ static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned ix) g_assert(pctrl->buffer.data != NULL); uint64_t *clear = (uint64_t *)pctrl->buffer.data; - const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; + const uint8_t *scrambling_key = s->otp_scramble_keys[part_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); + trace_ot_otp_unscramble_partition(s->ot_id, ot_otp_eg_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]); @@ -1653,30 +1670,30 @@ static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned ix) ot_present_free(ps); } -static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) +static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; g_assert(pctrl->buffer.data != NULL); - if (OtOTPPartDescs[ix].hw_digest) { - pctrl->digest = ot_otp_eg_load_partition_digest(s, ix); + if (s->part_descs[part_ix].hw_digest) { + pctrl->digest = ot_otp_eg_load_partition_digest(s, part_ix); } else { pctrl->digest = 0; } - if (OtOTPPartDescs[ix].secret) { + 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_eg_unscramble_partition(s, ix); + ot_otp_eg_unscramble_partition(s, part_ix); } } else { - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); + unsigned offset = (unsigned)s->part_descs[part_ix].offset; + unsigned part_size = ot_otp_eg_part_data_byte_size(s, part_ix); const uint8_t *base = (const uint8_t *)s->otp->data; base += offset; @@ -1686,12 +1703,13 @@ static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) } static void -ot_otp_eg_check_buffered_partition_integrity(OtOTPEgState *s, unsigned ix) +ot_otp_eg_check_buffered_partition_integrity(OtOTPEgState *s, unsigned part_ix) { - OtOTPPartController *pctrl = &s->partctrls[ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; if (pctrl->digest == 0) { - trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); + trace_ot_otp_skip_digest(s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix); pctrl->locked = false; return; } @@ -1701,27 +1719,28 @@ ot_otp_eg_check_buffered_partition_integrity(OtOTPEgState *s, unsigned ix) /* * 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); + const uint8_t *part_data = ((const uint8_t *)s->otp->data) + + ot_otp_eg_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_eg_part_data_byte_size(s, part_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_ot_otp_mismatch_digest(s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix, digest, pctrl->digest); TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - PART_NAME(ix), digest, + ot_otp_eg_part_name(s, part_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); + ot_otp_eg_set_error(s, part_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"); + trace_ot_otp_integrity_report(s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix, "digest OK"); } } @@ -1797,7 +1816,8 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) return; } - if (partition >= OTP_PART_LIFE_CYCLE) { + 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", @@ -1806,33 +1826,32 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) return; } - const OtOTPPartController *pctrl = &s->partctrls[partition]; + 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, PART_NAME(partition)); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix)); 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); + bool is_wide = ot_otp_eg_is_wide_granule(s, part_ix, address); + bool is_buffered = ot_otp_eg_is_buffered(s, part_ix); + bool is_secret = ot_otp_eg_is_secret(s, part_ix); + bool is_digest = ot_otp_eg_is_part_digest_offset(s, 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); + s->ot_id, ot_otp_eg_part_name(s, part_ix), 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_offset = address - ot_otp_eg_part_data_offset(s, part_ix); unsigned part_waddr = part_offset >> 2u; bool do_ecc = - OtOTPPartDescs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s); + s->part_descs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s); DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); @@ -1842,14 +1861,14 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) 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)); + data += (ot_otp_eg_part_data_offset(s, part_ix) / 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); + s->part_descs[part_ix].size); data_lo = data[part_waddr]; data_hi = data[part_waddr + 1u]; @@ -1867,8 +1886,7 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) cell_count *= 2u; } else { /* 32-bit request */ - g_assert(part_waddr * sizeof(uint32_t) < - OtOTPPartDescs[partition].size); + g_assert(part_waddr * sizeof(uint32_t) < s->part_descs[part_ix].size); data_lo = data[part_waddr]; data_hi = 0u; @@ -1894,7 +1912,7 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) * 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]; + const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; g_assert(scrambling_key); uint64_t tmp_data = ((uint64_t)data_hi << 32u) | data_lo; OtPresentState *ps = ot_present_new(); @@ -1939,7 +1957,7 @@ static int ot_otp_eg_dai_write_u64(OtOTPEgState *s, unsigned address) 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); + bool is_secret = ot_otp_eg_is_secret(s, part_ix); if (is_secret) { const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; @@ -1996,7 +2014,9 @@ static int ot_otp_eg_dai_write_u64(OtOTPEgState *s, unsigned address) return -1; } - trace_ot_otp_dai_new_dword_ecc(s->ot_id, PART_NAME(s->dai->partition), + trace_ot_otp_dai_new_dword_ecc(s->ot_id, + ot_otp_eg_part_name(s, (unsigned)s->dai + ->partition), s->dai->partition, *dst, *edst); } @@ -2049,7 +2069,9 @@ static int ot_otp_eg_dai_write_u32(OtOTPEgState *s, unsigned address) return -1; } - trace_ot_otp_dai_new_word_ecc(s->ot_id, PART_NAME(s->dai->partition), + trace_ot_otp_dai_new_word_ecc(s->ot_id, + ot_otp_eg_part_name(s, (unsigned)s->dai + ->partition), s->dai->partition, *dst, *edst); } @@ -2091,7 +2113,7 @@ static void ot_otp_eg_dai_write(OtOTPEgState *s) unsigned part_ix = (unsigned)partition; - if (part_ix >= OTP_PART_LIFE_CYCLE) { + if (part_ix >= s->part_life_cycle) { qemu_log_mask( LOG_GUEST_ERROR, "%s: %s: Life cycle partition cannot be accessed from DAI\n", @@ -2100,17 +2122,18 @@ static void ot_otp_eg_dai_write(OtOTPEgState *s) return; } - OtOTPPartController *pctrl = &s->partctrls[part_ix]; + 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, PART_NAME(part_ix)); + __func__, s->ot_id, ot_otp_eg_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, PART_NAME(part_ix), part_ix); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2118,22 +2141,22 @@ static void ot_otp_eg_dai_write(OtOTPEgState *s) 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); + s->ot_id, ot_otp_eg_part_name(s, 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); + bool is_digest = ot_otp_eg_is_part_digest_offset(s, part_ix, address); + bool is_wide = ot_otp_eg_is_wide_granule(s, part_ix, address); if (is_digest) { - if (OtOTPPartDescs[partition].hw_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, PART_NAME(part_ix), part_ix); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix), part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2199,7 +2222,9 @@ static void ot_otp_eg_dai_digest(OtOTPEgState *s) return; } - if (partition >= OTP_PART_LIFE_CYCLE) { + unsigned part_ix = (unsigned)partition; + + if (part_ix >= s->part_life_cycle) { qemu_log_mask( LOG_GUEST_ERROR, "%s: %s: Life cycle partition cannot be accessed from DAI\n", @@ -2208,25 +2233,27 @@ static void ot_otp_eg_dai_digest(OtOTPEgState *s) return; } - if (!OtOTPPartDescs[partition].hw_digest) { + 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, PART_NAME(partition), partition); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } - OtOTPPartController *pctrl = &s->partctrls[partition]; + 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, PART_NAME(partition)); + __func__, s->ot_id, ot_otp_eg_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, PART_NAME(partition), partition); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2234,16 +2261,16 @@ static void ot_otp_eg_dai_digest(OtOTPEgState *s) 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); + s->ot_id, ot_otp_eg_part_name(s, part_ix), part_ix); 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); + const uint8_t *data = ((const uint8_t *)s->otp->data) + + ot_otp_eg_part_data_offset(s, part_ix); + unsigned part_size = ot_otp_eg_part_data_byte_size(s, part_ix); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -2266,12 +2293,13 @@ 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)); + g_assert((s->dai->partition >= 0) && (s->dai->partition < s->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 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; @@ -2315,8 +2343,8 @@ static void ot_otp_eg_dai_write_digest(void *opaque) return; } - trace_ot_otp_dai_new_digest_ecc(s->ot_id, PART_NAME(s->dai->partition), - s->dai->partition, *dst, *edst); + trace_ot_otp_dai_new_digest_ecc(s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix, *dst, *edst); DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); @@ -2333,8 +2361,10 @@ static void ot_otp_eg_dai_complete(void *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, + trace_ot_otp_dai_read(s->ot_id, + ot_otp_eg_part_name(s, + (unsigned)s->dai->partition), + (unsigned)s->dai->partition, s->regs[R_DIRECT_ACCESS_RDATA_1], s->regs[R_DIRECT_ACCESS_RDATA_1]); s->dai->partition = -1; @@ -2716,10 +2746,9 @@ 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)size; (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); @@ -2732,7 +2761,7 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( unsigned part_ix = (unsigned)partition; - if (ot_otp_eg_is_buffered(part_ix)) { + if (ot_otp_eg_is_buffered(s, 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); @@ -2741,7 +2770,7 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( } bool is_readable = ot_otp_eg_is_readable(s, part_ix); - bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, addr); + bool is_digest = ot_otp_eg_is_part_digest_offset(s, part_ix, addr); if (!is_readable && !is_digest) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); @@ -2781,15 +2810,15 @@ static void ot_otp_eg_get_lc_info( } if (lc_valid) { - *lc_valid = !(es->partctrls[OTP_PART_SECRET0].failed || - es->partctrls[OTP_PART_SECRET2].failed || - es->partctrls[OTP_PART_LIFE_CYCLE].failed) ? + *lc_valid = !(es->part_ctrls[OTP_PART_SECRET0].failed || + es->part_ctrls[OTP_PART_SECRET2].failed || + es->part_ctrls[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) ? + *secret_valid = (!es->part_ctrls[OTP_PART_SECRET2].failed && + es->part_ctrls[OTP_PART_SECRET2].locked) ? OT_MULTIBITBOOL_LC4_TRUE : OT_MULTIBITBOOL_LC4_FALSE; } @@ -2919,10 +2948,10 @@ static void ot_otp_eg_generate_scrambling_key( 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)); + OtOTPPartController *pctrl = &s->part_ctrls[OTP_PART_SECRET1]; + g_assert(ot_otp_eg_is_buffered(s, OTP_PART_SECRET1)); uint32_t poffset = - OtOTPPartDescs[OTP_PART_SECRET1].offset / sizeof(uint32_t); + s->part_descs[OTP_PART_SECRET1].offset / sizeof(uint32_t); const uint32_t *key_seed = &pctrl->buffer.data[key_reg - poffset]; /* check the key seed's validity */ @@ -3039,12 +3068,12 @@ static void ot_otp_eg_get_keymgr_secret( case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: partition = OTP_PART_SECRET2; offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - - OtOTPPartDescs[partition].offset; + es->part_descs[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; + es->part_descs[partition].offset; break; case OTP_KEYMGR_SECRET_CREATOR_SEED: case OTP_KEYMGR_SECRET_OWNER_SEED: @@ -3057,17 +3086,17 @@ static void ot_otp_eg_get_keymgr_secret( } unsigned part_ix = (unsigned)partition; - g_assert(ot_otp_eg_is_buffered(part_ix)); + g_assert(ot_otp_eg_is_buffered(es, 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; + data_ptr = (const uint8_t *)es->part_ctrls[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; + secret->valid = es->part_ctrls[part_ix].digest != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3105,7 +3134,7 @@ static bool ot_otp_eg_program_req(OtOTPState *s, const uint16_t *lc_tcount, 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)); + es->part_descs[OTP_PART_LIFE_CYCLE].size / sizeof(uint16_t)); /* current position in LC buffer to write to backend */ lci->hpos = 0u; @@ -3130,7 +3159,7 @@ static void ot_otp_eg_lci_write_complete(OtOTPEgState *s, bool success) * 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]; + const OtOTPPartDesc *lcdesc = &s->part_descs[s->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], @@ -3167,7 +3196,7 @@ static void ot_otp_eg_lci_write_complete(OtOTPEgState *s, bool success) lci->hpos = 0u; if (!success && lci->error != OTP_NO_ERROR) { - ot_otp_eg_set_error(s, OTP_PART_LIFE_CYCLE, lci->error); + ot_otp_eg_set_error(s, s->part_life_cycle, lci->error); } (*ack_fn)(ack_data, success); @@ -3177,7 +3206,7 @@ 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]; + const OtOTPPartDesc *lcdesc = &s->part_descs[s->part_life_cycle]; /* should not be called if already in error */ if (lci->state == OTP_LCI_ERROR) { @@ -3319,8 +3348,8 @@ static void ot_otp_eg_pwr_load(OtOTPEgState *s) 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; + 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 */ @@ -3446,15 +3475,15 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) switch (tkx) { case OTP_TOKEN_TEST_UNLOCK: - partition = OTP_PART_SECRET0; + partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; break; case OTP_TOKEN_TEST_EXIT: - partition = OTP_PART_SECRET0; + partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_EXIT_TOKEN; break; case OTP_TOKEN_RMA: - partition = OTP_PART_SECRET2; + partition = (unsigned)OTP_PART_SECRET2; secret_addr = A_SECRET2_RMA_TOKEN; break; default: @@ -3462,12 +3491,12 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *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_eg_part_data_offset(partition); + secret_addr - ot_otp_eg_part_data_offset(s, partition); g_assert(secret_offset + sizeof(OtOTPTokenValue) <= OtOTPPartDescs[partition].size); @@ -3475,7 +3504,7 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) 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; } @@ -3488,25 +3517,25 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) static void ot_otp_eg_pwr_initialize_partitions(OtOTPEgState *s) { - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { + for (unsigned ix = 0; ix < s->part_count; ix++) { /* sanity check: all secret partitions are also buffered */ - g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); + g_assert(!s->part_descs[ix].secret || s->part_descs[ix].buffered); - if (ot_otp_eg_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { + if (ot_otp_eg_is_ecc_enabled(s) && s->part_descs[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; + if (s->part_descs[ix].sw_digest) { + s->part_ctrls[ix].digest = ot_otp_eg_get_part_digest(s, ix); + s->part_ctrls[ix].locked = s->part_ctrls[ix].digest != 0; continue; } - if (OtOTPPartDescs[ix].buffered) { + if (s->part_descs[ix].buffered) { ot_otp_eg_bufferize_partition(s, ix); - if (OtOTPPartDescs[ix].hw_digest) { + if (s->part_descs[ix].hw_digest) { ot_otp_eg_check_buffered_partition_integrity(s, ix); } continue; @@ -3773,54 +3802,65 @@ static void ot_otp_eg_configure_sram(OtOTPEgState *s) static void ot_otp_eg_configure_part_scramble_keys(OtOTPEgState *s) { - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { continue; } - if (!s->otp_scramble_key_xstrs[ix]) { + if (!s->otp_scramble_key_xstrs[part_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); + __func__, s->ot_id, ot_otp_eg_part_name(s, part_ix), + part_ix); return; } continue; } - size_t len = strlen(s->otp_scramble_key_xstrs[ix]); + size_t len = strlen(s->otp_scramble_key_xstrs[part_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); + __func__, s->ot_id, len, ot_otp_eg_part_name(s, part_ix), + part_ix); return; } - g_assert(!s->otp_scramble_keys[ix]); + g_assert(!s->otp_scramble_keys[part_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], + s->otp_scramble_keys[part_ix] = + g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); + if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], + s->otp_scramble_key_xstrs[part_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)); + __func__, s->ot_id, part_ix, + ot_otp_eg_part_name(s, part_ix)); return; } - TRACE_OTP("otp_scramble_keys[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->otp_scramble_keys[ix], + TRACE_OTP("otp_scramble_keys[%s] %s", ot_otp_eg_part_name(s, part_ix), + ot_otp_hexdump(s, s->otp_scramble_keys[part_ix], OTP_SCRAMBLING_KEY_BYTES)); } } -static void ot_otp_eg_class_add_scramble_key_props(OtOTPClass *odc) +static void ot_otp_eg_add_scramble_key_props(OtOTPEgState *s) { + /* + * @todo: we know the number of secret partitions, so use it rather than + * whole partition count + */ + s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); + s->otp_scramble_key_xstrs = g_new0(char *, s->part_count); + unsigned secret_ix = 0u; - for (unsigned ix = 0u; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].secret) { + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { continue; } @@ -3832,68 +3872,79 @@ static void ot_otp_eg_class_add_scramble_key_props(OtOTPClass *odc) */ 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; + /* + * 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[part_ix] - (intptr_t)s; - object_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); + object_property_add(OBJECT(s), prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, prop); } } 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]) { + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { + if (!s->inv_default_part_xstrs[part_ix]) { continue; } - const OtOTPPartDesc *part = &OtOTPPartDescs[ix]; + const OtOTPPartDesc *part = &s->part_descs[part_ix]; size_t len; - len = strlen(s->inv_default_part_xstrs[ix]); + 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, ix); + s->ot_id, part_ix); return; } - g_assert(!s->inv_default_parts[ix]); + g_assert(!s->inv_default_parts[part_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)) { + s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); + if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], + 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, ix); + __func__, s->ot_id, part_ix); return; } - TRACE_OTP("inv_default_part[%s] %s", PART_NAME(ix), - ot_otp_hexdump(s, s->inv_default_parts[ix], part->size)); + TRACE_OTP("inv_default_part[%s] %s", ot_otp_eg_part_name(s, part_ix), + ot_otp_hexdump(s, s->inv_default_parts[part_ix], part->size)); } } -static void ot_otp_eg_class_add_inv_def_props(OtOTPClass *odc) +static void ot_otp_eg_add_inv_def_props(OtOTPEgState *s) { - for (unsigned ix = 0; ix < ARRAY_SIZE(OtOTPPartDescs); ix++) { - if (!OtOTPPartDescs[ix].buffered) { + s->inv_default_parts = g_new0(uint8_t *, 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", ix); + prop->name = g_strdup_printf("inv_default_part_%u", part_ix); prop->info = &qdev_prop_string; - prop->offset = offsetof(OtOTPEgState, inv_default_part_xstrs) + - sizeof(char *) * ix; + /* + * 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_class_property_add(OBJECT_CLASS(odc), prop->name, - prop->info->name, prop->info->get, - prop->info->set, prop->info->release, prop); + object_property_add(OBJECT(s), prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, prop); } } @@ -3994,21 +4045,21 @@ static void ot_otp_eg_reset_enter(Object *obj, ResetType type) ot_otp_eg_update_alerts(s); ibex_irq_set(&s->pwc_otp_rsp, 0); - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { + for (unsigned part_ix = 0; part_ix < s->part_count; part_ix++) { /* TODO: initialize with actual default partition data once known */ - if (OtOTPPartDescs[ix].buffered) { - s->partctrls[ix].state.b = OTP_BUF_IDLE; + if (s->part_descs[part_ix].buffered) { + s->part_ctrls[part_ix].state.b = OTP_BUF_IDLE; } else { - s->partctrls[ix].state.u = OTP_UNBUF_IDLE; + s->part_ctrls[part_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; + unsigned part_size = ot_otp_eg_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, OTP_DAI_RESET); @@ -4105,23 +4156,29 @@ static void ot_otp_eg_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), &ot_otp_eg_lc_broadcast_recv, OT_LC_BROADCAST, OT_OTP_LC_BROADCAST_COUNT); + s->part_descs = OtOTPPartDescs; + s->part_count = ARRAY_SIZE(OtOTPPartDescs); + s->part_life_cycle = OTP_PART_LIFE_CYCLE; + 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->part_ctrls = g_new0(OtOTPPartController, s->part_count); 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_life_cycle].size / + sizeof(uint16_t)); - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (!OtOTPPartDescs[ix].buffered) { + 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_eg_part_data_byte_size(ix) / sizeof(uint32_t); - s->partctrls[ix].buffer.data = g_new0(uint32_t, part_words); + ot_otp_eg_part_data_byte_size(s, part_ix) / sizeof(uint32_t); + s->part_ctrls[part_ix].buffer.data = g_new0(uint32_t, part_words); } ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT); @@ -4139,6 +4196,9 @@ static void ot_otp_eg_init(Object *obj) int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); ot_prng_reseed(s->keygen->prng, (uint32_t)now); + ot_otp_eg_add_scramble_key_props(s); + ot_otp_eg_add_inv_def_props(s); + #ifdef OT_OTP_DEBUG s->hexstr = g_new0(char, OT_OTP_HEXSTR_SIZE); #endif @@ -4149,9 +4209,6 @@ 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); @@ -4167,9 +4224,6 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) oc->get_otp_key = &ot_otp_eg_get_otp_key; oc->get_keymgr_secret = &ot_otp_eg_get_keymgr_secret; oc->program_req = &ot_otp_eg_program_req; - - ot_otp_eg_class_add_scramble_key_props(oc); - ot_otp_eg_class_add_inv_def_props(oc); } static const TypeInfo ot_otp_eg_info = { diff --git a/hw/opentitan/ot_otp_eg_parts.c b/hw/opentitan/ot_otp_eg_parts.c index dfdc986d00f4c..05e3a75a5b29a 100644 --- a/hw/opentitan/ot_otp_eg_parts.c +++ b/hw/opentitan/ot_otp_eg_parts.c @@ -7,6 +7,7 @@ /* NOLINTBEGIN */ static const OtOTPPartDesc OtOTPPartDescs[] = { [OTP_PART_VENDOR_TEST] = { + .name = "VENDOR_TEST", .size = 64u, .offset = 0u, .digest_offset = 56u, @@ -20,6 +21,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = false, }, [OTP_PART_CREATOR_SW_CFG] = { + .name = "CREATOR_SW_CFG", .size = 368u, .offset = 64u, .digest_offset = 424u, @@ -33,6 +35,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_OWNER_SW_CFG] = { + .name = "OWNER_SW_CFG", .size = 712u, .offset = 432u, .digest_offset = 1136u, @@ -46,6 +49,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_ROT_CREATOR_AUTH_CODESIGN] = { + .name = "ROT_CREATOR_AUTH_CODESIGN", .size = 472u, .offset = 1144u, .digest_offset = 1608u, @@ -59,6 +63,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_ROT_CREATOR_AUTH_STATE] = { + .name = "ROT_CREATOR_AUTH_STATE", .size = 40u, .offset = 1616u, .digest_offset = 1648u, @@ -72,6 +77,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_HW_CFG0] = { + .name = "HW_CFG0", .size = 72u, .offset = 1656u, .digest_offset = 1720u, @@ -84,6 +90,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_HW_CFG1] = { + .name = "HW_CFG1", .size = 16u, .offset = 1728u, .digest_offset = 1736u, @@ -96,6 +103,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_SECRET0] = { + .name = "SECRET0", .size = 40u, .offset = 1744u, .digest_offset = 1776u, @@ -108,6 +116,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_SECRET1] = { + .name = "SECRET1", .size = 88u, .offset = 1784u, .digest_offset = 1864u, @@ -120,6 +129,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .integrity = true, }, [OTP_PART_SECRET2] = { + .name = "SECRET2", .size = 88u, .offset = 1872u, .digest_offset = 1952u, @@ -133,6 +143,7 @@ static const OtOTPPartDesc OtOTPPartDescs[] = { .iskeymgr_creator = true, }, [OTP_PART_LIFE_CYCLE] = { + .name = "LIFE_CYCLE", .size = 88u, .offset = 1960u, .digest_offset = UINT16_MAX, From dd61833ef1156c4b613d503a26aa72171dd078d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 Oct 2025 16:44:52 +0100 Subject: [PATCH 05/14] [ot] hw/opentitan: ot_otp: replace OtOTPState device with OtOTPIf interface. This is the second step towards unification of OtOTP* implementations. It is better to expose the OtOTPState feature as an interface, so that OtOTPState can later implement the common feature for all OtOTP Top-specialized implementation. Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 16 ++++-- hw/opentitan/meson.build | 2 +- hw/opentitan/ot_csrng.c | 17 +++--- hw/opentitan/ot_entropy_src.c | 7 +-- hw/opentitan/ot_keymgr.c | 27 +++++----- hw/opentitan/ot_keymgr_dpe.c | 36 +++++++------ hw/opentitan/ot_lc_ctrl.c | 27 ++++++---- hw/opentitan/ot_otp_dj.c | 52 ++++++++++++------- hw/opentitan/ot_otp_eg.c | 52 ++++++++++++------- hw/opentitan/{ot_otp.c => ot_otp_if.c} | 23 ++++---- hw/opentitan/ot_otp_ot_be.c | 2 +- hw/opentitan/ot_sram_ctrl.c | 23 ++++---- hw/riscv/Kconfig | 2 + hw/riscv/ot_darjeeling.c | 4 +- hw/riscv/ot_earlgrey.c | 8 +-- include/hw/opentitan/ot_otp_dj.h | 6 +-- include/hw/opentitan/ot_otp_eg.h | 6 +-- .../hw/opentitan/{ot_otp.h => ot_otp_if.h} | 46 ++++++++-------- 18 files changed, 200 insertions(+), 156 deletions(-) rename hw/opentitan/{ot_otp.c => ot_otp_if.c} (72%) rename include/hw/opentitan/{ot_otp.h => ot_otp_if.h} (86%) diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index ad2b5dd61001f..d4c89b620571f 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,25 @@ 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_IF select OT_PRESENT bool config OT_OTP_EG - select OT_OTP + select OT_OTP_IF + select OT_PRESENT + bool + +config OT_OTP_IF bool config OT_OTP_OT_BE select OT_OTP_BE_IF + select OT_OTP_IF bool config OT_PINMUX_DJ @@ -184,6 +189,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..655e9e49c06f9 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -34,10 +34,10 @@ 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_IF', if_true: files('ot_otp_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..1cfe56e1ef85c 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 Earlgrey 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..7eddcf2588416 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, OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + share0); + oc->get_keymgr_secret(oi, 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..780cbb8becf77 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, 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, 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, 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, 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..d2c79edeea2d0 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); @@ -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 f78b9c682c45c..ea6a2251f239c 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -42,6 +42,7 @@ #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otp_be_if.h" #include "hw/opentitan/ot_otp_dj.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_present.h" #include "hw/opentitan/ot_prng.h" #include "hw/opentitan/ot_pwrmgr.h" @@ -726,7 +727,7 @@ typedef struct { } OtOTPScrmblKeyInit; struct OtOTPDjState { - OtOTPState parent_obj; + SysBusDevice parent_obj; struct { MemoryRegion ctrl; @@ -781,6 +782,11 @@ struct OtOTPDjState { bool fatal_escalate; }; +struct OtOTPDjClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) static const char *REG_NAMES[REGS_COUNT] = { /* clang-format off */ @@ -2903,10 +2909,10 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( } static void ot_otp_dj_get_lc_info( - const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, + const OtOTPIf *dev, 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 OtOTPDjState *ds = OT_OTP_DJ(dev); const OtOTPStorage *otp = ds->otp; if (lc_tcount) { @@ -2936,9 +2942,9 @@ static void ot_otp_dj_get_lc_info( } } -static const OtOTPHWCfg *ot_otp_dj_get_hw_cfg(const OtOTPState *s) +static const OtOTPHWCfg *ot_otp_dj_get_hw_cfg(const OtOTPIf *dev) { - const OtOTPDjState *ds = OT_OTP_DJ(s); + const OtOTPDjState *ds = OT_OTP_DJ(dev); return (const OtOTPHWCfg *)ds->hw_cfg; } @@ -3104,10 +3110,10 @@ static void ot_otp_dj_generate_scrambling_key( } } -static void ot_otp_dj_get_otp_key(OtOTPState *s, OtOTPKeyType type, +static void ot_otp_dj_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, OtOTPKey *key) { - OtOTPDjState *ds = OT_OTP_DJ(s); + OtOTPDjState *ds = OT_OTP_DJ(dev); hwaddr key_offset; @@ -3152,9 +3158,9 @@ static void ot_otp_dj_get_otp_key(OtOTPState *s, OtOTPKeyType type, } static void ot_otp_dj_get_keymgr_secret( - OtOTPState *s, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) + OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) { - OtOTPDjState *ds = OT_OTP_DJ(s); + OtOTPDjState *ds = OT_OTP_DJ(dev); int partition; size_t offset; @@ -3200,11 +3206,11 @@ static void ot_otp_dj_get_keymgr_secret( 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, +static bool ot_otp_dj_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, const uint16_t *lc_state, ot_otp_program_ack_fn ack, void *opaque) { - OtOTPDjState *ds = OT_OTP_DJ(s); + OtOTPDjState *ds = OT_OTP_DJ(dev); OtOTPLCIController *lci = ds->lci; switch (lci->state) { @@ -3992,7 +3998,7 @@ 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); + OtOTPDjClass *c = OT_OTP_DJ_GET_CLASS(obj); OtOTPDjState *s = OT_OTP_DJ(obj); /* @@ -4085,7 +4091,7 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) static void ot_otp_dj_reset_exit(Object *obj, ResetType type) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPDjClass *c = OT_OTP_DJ_GET_CLASS(obj); OtOTPDjState *s = OT_OTP_DJ(obj); trace_ot_otp_reset(s->ot_id, "exit"); @@ -4143,17 +4149,17 @@ static void ot_otp_dj_init(Object *obj) * - "swcfg", software config window * offset SW_CFG_WINDOW, size SW_CFG_WINDOW_SIZE */ - memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP "-ctrl", + memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP_DJ "-ctrl", SW_CFG_WINDOW + 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 */ memory_region_init_io(&s->mmio.sub.swcfg, obj, &ot_otp_dj_swcfg_ops, s, - TYPE_OT_OTP "-swcfg", SW_CFG_WINDOW_SIZE); + TYPE_OT_OTP_DJ "-swcfg", SW_CFG_WINDOW_SIZE); memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, &s->mmio.sub.swcfg); @@ -4230,11 +4236,12 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); ResettableClass *rc = RESETTABLE_CLASS(klass); - OtOTPClass *oc = OT_OTP_CLASS(klass); + OtOTPDjClass *djc = OT_OTP_DJ_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_otp_dj_reset_enter, NULL, &ot_otp_dj_reset_exit, - &oc->parent_phases); + &djc->parent_phases); + 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; @@ -4244,11 +4251,16 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) static const TypeInfo ot_otp_dj_info = { .name = TYPE_OT_OTP_DJ, - .parent = TYPE_OT_OTP, + .parent = TYPE_SYS_BUS_DEVICE, .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 }, + {}, + }, }; static void ot_otp_dj_register_types(void) diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index c76f5ba29489b..f1bc11bb65ca9 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -41,6 +41,7 @@ #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otp_be_if.h" #include "hw/opentitan/ot_otp_eg.h" +#include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_present.h" #include "hw/opentitan/ot_prng.h" #include "hw/opentitan/ot_pwrmgr.h" @@ -662,7 +663,7 @@ typedef struct { } OtOTPScrmblKeyInit; struct OtOTPEgState { - OtOTPState parent_obj; + SysBusDevice parent_obj; struct { MemoryRegion ctrl; @@ -725,6 +726,11 @@ struct OtOTPEgState { bool fatal_escalate; }; +struct OtOTPEgClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) static const char *REG_NAMES[REGS_COUNT] = { /* clang-format off */ @@ -2794,10 +2800,10 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( } static void ot_otp_eg_get_lc_info( - const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, + const OtOTPIf *dev, 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 OtOTPEgState *es = OT_OTP_EG(dev); const OtOTPStorage *otp = es->otp; if (lc_tcount) { @@ -2827,9 +2833,9 @@ static void ot_otp_eg_get_lc_info( } } -static const OtOTPHWCfg *ot_otp_eg_get_hw_cfg(const OtOTPState *s) +static const OtOTPHWCfg *ot_otp_eg_get_hw_cfg(const OtOTPIf *dev) { - const OtOTPEgState *es = OT_OTP_EG(s); + const OtOTPEgState *es = OT_OTP_EG(dev); return (const OtOTPHWCfg *)es->hw_cfg; } @@ -2995,10 +3001,10 @@ static void ot_otp_eg_generate_scrambling_key( } } -static void ot_otp_eg_get_otp_key(OtOTPState *s, OtOTPKeyType type, +static void ot_otp_eg_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, OtOTPKey *key) { - OtOTPEgState *es = OT_OTP_EG(s); + OtOTPEgState *es = OT_OTP_EG(dev); hwaddr key_offset; @@ -3058,9 +3064,9 @@ static void ot_otp_eg_get_otp_key(OtOTPState *s, OtOTPKeyType type, } static void ot_otp_eg_get_keymgr_secret( - OtOTPState *s, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) + OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) { - OtOTPEgState *es = OT_OTP_EG(s); + OtOTPEgState *es = OT_OTP_EG(dev); int partition; size_t offset; @@ -3100,11 +3106,11 @@ static void ot_otp_eg_get_keymgr_secret( 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, +static bool ot_otp_eg_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, const uint16_t *lc_state, ot_otp_program_ack_fn ack, void *opaque) { - OtOTPEgState *es = OT_OTP_EG(s); + OtOTPEgState *es = OT_OTP_EG(dev); OtOTPLCIController *lci = es->lci; switch (lci->state) { @@ -3985,7 +3991,7 @@ 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); + OtOTPEgClass *c = OT_OTP_EG_GET_CLASS(obj); OtOTPEgState *s = OT_OTP_EG(obj); /* @@ -4068,7 +4074,7 @@ static void ot_otp_eg_reset_enter(Object *obj, ResetType type) static void ot_otp_eg_reset_exit(Object *obj, ResetType type) { - OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPEgClass *c = OT_OTP_EG_GET_CLASS(obj); OtOTPEgState *s = OT_OTP_EG(obj); trace_ot_otp_reset(s->ot_id, "exit"); @@ -4127,17 +4133,17 @@ static void ot_otp_eg_init(Object *obj) * - "swcfg", software config window * offset SW_CFG_WINDOW, size SW_CFG_WINDOW_SIZE */ - memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP "-ctrl", + memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP_EG "-ctrl", SW_CFG_WINDOW + 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 */ memory_region_init_io(&s->mmio.sub.swcfg, obj, &ot_otp_eg_swcfg_ops, s, - TYPE_OT_OTP "-swcfg", SW_CFG_WINDOW_SIZE); + TYPE_OT_OTP_EG "-swcfg", SW_CFG_WINDOW_SIZE); memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, &s->mmio.sub.swcfg); @@ -4214,11 +4220,12 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); ResettableClass *rc = RESETTABLE_CLASS(klass); - OtOTPClass *oc = OT_OTP_CLASS(klass); + OtOTPEgClass *edc = OT_OTP_EG_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_otp_eg_reset_enter, NULL, &ot_otp_eg_reset_exit, - &oc->parent_phases); + &edc->parent_phases); + 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; @@ -4228,11 +4235,16 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) static const TypeInfo ot_otp_eg_info = { .name = TYPE_OT_OTP_EG, - .parent = TYPE_OT_OTP, + .parent = TYPE_SYS_BUS_DEVICE, .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 }, + {}, + }, }; static void ot_otp_eg_register_types(void) 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_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..bff06f3770a7d 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, 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_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.h b/include/hw/opentitan/ot_otp_if.h similarity index 86% rename from include/hw/opentitan/ot_otp.h rename to include/hw/opentitan/ot_otp_if.h index 12a9fdc56016d..9e314f0cb3790 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 { @@ -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 */ From cb7b7a98a199c2a84cf4984aeca9c724b5480943 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 30 Oct 2025 11:36:42 +0100 Subject: [PATCH 06/14] [ot] hw/opentitan: ot_otp_impl_if: define a new OtOTPImplIf API for OTP devices Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 3 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_otp_impl_if.c | 43 ++++++++++ include/hw/opentitan/ot_otp_impl_if.h | 116 ++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 hw/opentitan/ot_otp_impl_if.c create mode 100644 include/hw/opentitan/ot_otp_impl_if.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index d4c89b620571f..e78feb4e3b4a9 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -136,6 +136,9 @@ config OT_OTP_EG 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 diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 655e9e49c06f9..4d3f4d0c70254 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -38,6 +38,7 @@ 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_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_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/include/hw/opentitan/ot_otp_impl_if.h b/include/hw/opentitan/ot_otp_impl_if.h new file mode 100644 index 0000000000000..b5c47005cb4f2 --- /dev/null +++ b/include/hw/opentitan/ot_otp_impl_if.h @@ -0,0 +1,116 @@ +/* + * 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; + + /* Count of partition */ + unsigned part_count; + + /* Index of the life cycle partition in part_descs */ + unsigned part_lc_num; + + /* 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 */ From 79c2f2feb7e5407ef06417487ff7d4af16a2c6d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 30 Oct 2025 15:55:36 +0100 Subject: [PATCH 07/14] [ot] python/qemu: ot.otp: add key seed extraction and generation to C file Signed-off-by: Emmanuel Blot --- python/qemu/ot/otp/descriptor.py | 27 +++++++++++++++++++++--- python/qemu/ot/otp/map.py | 35 +++++++++++++++++++++++++++++++- python/qemu/ot/otp/partition.py | 17 ++++++++++++++-- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/python/qemu/ot/otp/descriptor.py b/python/qemu/ot/otp/descriptor.py index 6284acd248584..1237f5dd8f761 100644 --- a/python/qemu/ot/otp/descriptor.py +++ b/python/qemu/ot/otp/descriptor.py @@ -88,7 +88,7 @@ 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) @@ -123,11 +123,32 @@ 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': + key_seeds.append(('OTP_KEY_OTBN', f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + key_seeds.append(('OTP_KEY_SRAM', f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + continue + key_seeds.append((f'OTP_KEY_{kseed.name}', + f'OTP_PART_{kseed.part_name}', + kseed.offset, kseed.size)) + print('static const OtOTPKeySeed OT_OTP_KEY_SEEDS[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 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: From 49ac2a375bcfafe09d238b3d5b8f18649f272bf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Oct 2025 18:02:08 +0100 Subject: [PATCH 08/14] [ot] hw/opentitan: ot_otp: merge OTP implementation from EarlGrey and Darjeeling - merge and adapt common code into OtOTPEngine - keep only OTP top-specific implementation in ot_otp_eg.c and ot_otp_dj.c, which implement the OtOTPImplIf interface (specialization for Tops) - regenerate OTP partition descriptions and key seed definition with otptool.py Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 11 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_entropy_src.c | 2 +- hw/opentitan/ot_otp_dj.c | 3429 ++-------------------------- hw/opentitan/ot_otp_dj_parts.c | 17 +- hw/opentitan/ot_otp_eg.c | 3479 ++--------------------------- hw/opentitan/ot_otp_eg_parts.c | 54 +- hw/opentitan/ot_otp_engine.c | 2886 ++++++++++++++++++++++++ hw/opentitan/ot_otp_engine.h | 468 ++++ include/hw/opentitan/ot_lc_ctrl.h | 1 + 10 files changed, 3890 insertions(+), 6458 deletions(-) create mode 100644 hw/opentitan/ot_otp_engine.c create mode 100644 hw/opentitan/ot_otp_engine.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index e78feb4e3b4a9..d38dc28224af0 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -124,12 +124,23 @@ config OT_OTP_BE_IF bool config OT_OTP_DJ + select OT_OTP_ENGINE select OT_OTP_IF + select OT_OTP_IMPL_IF select OT_PRESENT bool config OT_OTP_EG + 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 diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 4d3f4d0c70254..3acdfbc2ae9ba 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -37,6 +37,7 @@ system_ss.add(when: 'CONFIG_OT_OTBN', if_true: files('ot_otbn.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')) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 1cfe56e1ef85c..8cedd569bc3bd 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1,5 +1,5 @@ /* - * QEMU OpenTitan Earlgrey Entropy Source device + * QEMU OpenTitan Entropy Source device * * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index ea6a2251f239c..b0d6585deaef6 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 @@ -30,59 +31,23 @@ */ #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_otp_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 "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_impl_if.h" +#include "ot_otp_engine.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) @@ -116,7 +81,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) @@ -143,10 +107,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) @@ -164,7 +124,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) @@ -218,7 +177,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) @@ -338,9 +297,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 @@ -375,7 +332,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 @@ -393,18 +349,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 @@ -428,51 +381,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 R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) +#define SW_CFG_WINDOW_OFFSET 0x4000u +#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) #define R_LAST_REG (R_SECRET3_DIGEST_1) #define REGS_COUNT (R_LAST_REG + 1u) @@ -480,44 +406,8 @@ 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) +static_assert(SRAM_KEY_SEED_WIDTH == SECRET1_SRAM_DATA_KEY_SEED_SIZE * 8u, + "SRAM key seed size does not match OTP field size"); typedef enum { OTP_PART_VENDOR_TEST, @@ -549,185 +439,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 { - 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; - #define OT_OTP_DJ_PARTS /* 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 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; -} 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; - 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 { - SysBusDevice parent_obj; + OtOTPEngineState parent_obj; struct { MemoryRegion ctrl; @@ -736,54 +460,10 @@ 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 *part_ctrls; - const OtOTPPartDesc *part_descs; - unsigned part_count; - unsigned part_life_cycle; /* index of the Life Cycle partition */ - 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; /* some entries may be NULL */ - uint8_t **inv_default_parts; /* some entries 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; /* some entries may be NULL */ - char **inv_default_part_xstrs; /* some entries may be NULL */ - uint8_t edn_ep; - bool fatal_escalate; }; struct OtOTPDjClass { - SysBusDeviceClass parent_class; + OtOTPEngineClass parent_class; ResettablePhases parent_phases; }; @@ -889,43 +569,6 @@ 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 */ -}; - static const char *OTP_TOKEN_NAMES[] = { /* clang-format off */ OTP_NAME_ENTRY(OTP_TOKEN_TEST_UNLOCK), @@ -934,1676 +577,122 @@ static const char *OTP_TOKEN_NAMES[] = { /* 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 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 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 const char *ot_otp_dj_part_name(const OtOTPDjState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[part_ix].name; - } - - if (part_ix == s->part_count) { - return "DAI"; - } - - if (part_ix == s->part_count + 1u) { - return "KDI"; - } - - return "?"; -} - -static inline unsigned -ot_otp_dj_part_data_offset(const OtOTPDjState *s, unsigned part_ix) -{ - return (unsigned)(s->part_descs[part_ix].offset); -} - -static inline unsigned -ot_otp_dj_part_data_byte_size(const OtOTPDjState *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) * NUM_DIGEST_WORDS; - } - - if (s->part_descs[part_ix].zeroizable) { - size -= sizeof(uint32_t) * NUM_ZER_WORDS; - } - - return (unsigned)size; -} - -static bool ot_otp_dj_is_wide_granule(const OtOTPDjState *s, unsigned part_ix, - unsigned address) -{ - if (part_ix < s->part_count) { - if (s->part_descs[part_ix].secret) { - return true; - } - - if (s->part_descs[part_ix].digest_offset == - (address & OTP_DIGEST_ADDR_MASK)) { - return true; - } - } - - return false; -} - -static bool ot_otp_dj_is_buffered(const OtOTPDjState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[part_ix].buffered; - } - - return false; -} - -static bool ot_otp_dj_is_secret(const OtOTPDjState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[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) -{ - return s->otp->ecc_granule == sizeof(uint16_t) && - ot_otp_dj_is_backend_ecc_enabled(s); -} - -static bool ot_otp_dj_has_digest(const OtOTPDjState *s, unsigned part_ix) -{ - return s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest; -} - -static bool ot_otp_dj_is_part_digest_offset(const OtOTPDjState *s, - unsigned part_ix, hwaddr addr) +static uint32_t ot_otp_dj_get_status(const OtOTPEngineState *s) { - uint16_t offset = s->part_descs[part_ix].digest_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); -} - -static bool ot_otp_dj_is_part_zer_offset(const OtOTPDjState *s, - unsigned part_ix, hwaddr addr) -{ - uint16_t offset = s->part_descs[part_ix].zer_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_ZER_ADDR_MASK) == offset); -} + 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 < s->part_count; pix++) { - OtOTPPartController *pctrl = &s->part_ctrls[pix]; - pctrl->failed = true; - } + return status; } -static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part_ix, - OtOTPError err) +static uint64_t ot_otp_dj_reg_read(void *opaque, hwaddr addr, unsigned size) { - g_assert(part_ix < OTP_ENTRY_COUNT); - - uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; - if (errval || errval != s->regs[R_ERR_CODE_0 + part_ix]) { - trace_ot_otp_set_error(s->ot_id, ot_otp_dj_part_name(s, part_ix), - part_ix, ERR_CODE_NAME(err), err); - } - s->regs[R_ERR_CODE_0 + part_ix] = 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 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_dj_part_name(s, part_ix), part_ix); - return (OtOTPPartitionType)part_ix; - } - } - - 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 = s->part_descs[part_ix].offset >> 2u; - unsigned end = (ot_otp_dj_is_buffered(s, (int)part_ix) && - ot_otp_dj_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_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, - ot_otp_dj_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_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) -{ - g_assert(!ot_otp_dj_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_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 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 < s->part_count; part_ix++) { - if (ot_otp_dj_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_life_cycle == 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_dj_get_sw_readlock(const OtOTPDjState *s, unsigned rdlk_ix) -{ - uint32_t reg = R_VENDOR_TEST_READ_LOCK + rdlk_ix; - - return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); -} - -static bool ot_otp_dj_is_readable(const OtOTPDjState *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_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 < s->part_life_cycle); - - /* - * now that the count of read_lock_csr is known, use it to access - * the register for the selected partition - */ - return ot_otp_dj_get_sw_readlock(s, roffset); -} - -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 < 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_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)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_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 part_ix) -{ - OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; - - unsigned offset = (unsigned)s->part_descs[part_ix].offset; - unsigned part_size = ot_otp_dj_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; - - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; - g_assert(scrambling_key); - - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - - trace_ot_otp_unscramble_partition(s->ot_id, ot_otp_dj_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_dj_bufferize_partition(OtOTPDjState *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_dj_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_dj_unscramble_partition(s, part_ix); - } - } else { - unsigned offset = (unsigned)s->part_descs[part_ix].offset; - unsigned part_size = ot_otp_dj_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_dj_check_buffered_partition_integrity(OtOTPDjState *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_dj_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_dj_part_data_offset(s, part_ix); - unsigned part_size = ot_otp_dj_part_data_byte_size(s, part_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, ot_otp_dj_part_name(s, part_ix), - part_ix, digest, pctrl->digest); - - TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - ot_otp_dj_part_name(s, part_ix), digest, - ot_otp_hexdump(s, part_data, part_size)); - - pctrl->failed = true; - /* this is a fatal error */ - ot_otp_dj_set_error(s, part_ix, OTP_CHECK_FAIL_ERROR); - /* TODO: revert buffered part to default */ - } else { - trace_ot_otp_integrity_report(s->ot_id, ot_otp_dj_part_name(s, part_ix), - part_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; - } - - 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; - } - - 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_dj_part_name(s, part_ix)); - return; - } - - bool is_readable = ot_otp_dj_is_readable(s, part_ix); - bool is_wide = ot_otp_dj_is_wide_granule(s, part_ix, address); - bool is_buffered = ot_otp_dj_is_buffered(s, part_ix); - bool is_secret = ot_otp_dj_is_secret(s, part_ix); - bool is_digest = ot_otp_dj_is_part_digest_offset(s, part_ix, address); - bool is_zer = ot_otp_dj_is_part_zer_offset(s, 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, ot_otp_dj_part_name(s, part_ix), address); - ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_offset = address - ot_otp_dj_part_data_offset(s, part_ix); - unsigned part_waddr = part_offset >> 2u; - bool do_ecc = - s->part_descs[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(s, part_ix) / sizeof(uint32_t)); - - if (is_wide || is_digest) { - /* 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_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) < 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_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[part_ix]; - 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(s, part_ix); - bool is_zer = ot_otp_dj_is_part_zer_offset(s, 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, - ot_otp_dj_part_name(s, (unsigned)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, - ot_otp_dj_part_name(s, (unsigned)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 >= s->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->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_dj_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_dj_part_name(s, 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, ot_otp_dj_part_name(s, 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(s, part_ix, address); - bool is_wide = ot_otp_dj_is_wide_granule(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_dj_part_name(s, 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; - } - - unsigned part_ix = (unsigned)partition; - - if (part_ix >= s->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 (!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_dj_part_name(s, part_ix), - part_ix); - ot_otp_dj_dai_set_error(s, 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_dj_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_dj_part_name(s, 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: Partition %s (%u) is write locked\n", __func__, - s->ot_id, ot_otp_dj_part_name(s, part_ix), part_ix); - 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(s, part_ix); - unsigned part_size = ot_otp_dj_part_data_byte_size(s, part_ix); - - 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 < s->part_count)); - - DAI_CHANGE_STATE(s, 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_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, ot_otp_dj_part_name(s, part_ix), - part_ix, *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, - ot_otp_dj_part_name(s, - (unsigned)s->dai->partition), - (unsigned)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; + 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: @@ -2644,22 +733,22 @@ static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, 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; @@ -2667,12 +756,13 @@ static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, break; case R_DIRECT_ACCESS_CMD: if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { - ot_otp_dj_dai_read(s); + c->dai_read(s); } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { - ot_otp_dj_dai_write(s); + c->dai_write(s); } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { - ot_otp_dj_dai_digest(s); + c->dai_digest(s); } + /* @todo implement ZEROIZE command */ break; case R_DIRECT_ACCESS_ADDRESS: val32 &= R_DIRECT_ACCESS_ADDRESS_ADDRESS_MASK; @@ -2859,12 +949,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; 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"); @@ -2875,27 +966,27 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( unsigned part_ix = (unsigned)partition; - if (ot_otp_dj_is_buffered(s, 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, 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(s, part_ix, addr); - bool is_zer = ot_otp_dj_is_part_zer_offset(s, 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, 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); + c->set_error(s, part_ix, OTP_NO_ERROR); uint64_t pc; @@ -2912,8 +1003,8 @@ 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 OtOTPDjState *ds = OT_OTP_DJ(dev); - const OtOTPStorage *otp = ds->otp; + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); + const OtOTPStorage *otp = s->otp; if (lc_tcount) { memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], @@ -2925,630 +1016,98 @@ static void ot_otp_dj_get_lc_info( } if (lc_valid) { - *lc_valid = !(ds->part_ctrls[OTP_PART_SECRET0].failed || - ds->part_ctrls[OTP_PART_SECRET2].failed || - ds->part_ctrls[ds->part_life_cycle].failed) ? + *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 = (!ds->part_ctrls[OTP_PART_SECRET2].failed && - ds->part_ctrls[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 OtOTPIf *dev) -{ - const OtOTPDjState *ds = OT_OTP_DJ(dev); - - 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->part_ctrls[OTP_PART_SECRET1]; - g_assert(ot_otp_dj_is_buffered(s, OTP_PART_SECRET1)); - uint32_t poffset = - s->part_descs[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(OtOTPIf *dev, OtOTPKeyType type, - OtOTPKey *key) -{ - OtOTPDjState *ds = OT_OTP_DJ(dev); - - 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( - OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) -{ - OtOTPDjState *ds = OT_OTP_DJ(dev); - 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 - - ds->part_descs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - - ds->part_descs[partition].offset; - break; - case OTP_KEYMGR_SECRET_CREATOR_SEED: - partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_SEED - ds->part_descs[partition].offset; - break; - case OTP_KEYMGR_SECRET_OWNER_SEED: - partition = OTP_PART_SECRET3; - offset = A_SECRET3_OWNER_SEED - ds->part_descs[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(ds, 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->part_ctrls[part_ix].buffer.data; - } else { - /* source data from PartInvDefault instead of real buffer */ - data_ptr = ds->inv_default_parts[part_ix]; - } - - secret->valid = ds->part_ctrls[part_ix].digest != 0; - memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); -} - -static bool ot_otp_dj_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, - const uint16_t *lc_state, - ot_otp_program_ack_fn ack, void *opaque) -{ - OtOTPDjState *ds = OT_OTP_DJ(dev); - 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 == - ds->part_descs[ds->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 = &s->part_descs[s->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, s->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 = &s->part_descs[s->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); + 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; } } -static void ot_otp_dj_pwr_load(OtOTPDjState *s) +static void ot_otp_dj_get_keymgr_secret( + OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) { - /* - * 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; + OtOTPEngineState *s = OT_OTP_ENGINE(dev); + int partition; + size_t offset; - 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); + switch (type) { + case 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 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 OTP_KEYMGR_SECRET_CREATOR_SEED: + partition = OTP_PART_SECRET2; + offset = A_SECRET2_CREATOR_SEED - s->part_descs[partition].offset; + break; + case 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; } - size_t otp_size = header_size + data_size + ecc_size; - - otp_size = ROUND_UP(otp_size, 4096u); - OtOTPStorage *otp = s->otp; + unsigned part_ix = (unsigned)partition; + g_assert(ot_otp_engine_is_buffered(s, part_ix)); - /* 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); + 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 */ + data_ptr = s->inv_default_parts[part_ix]; } - 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_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; OtOTPHWCfg *hw_cfg = s->hw_cfg; @@ -3569,7 +1128,7 @@ static void ot_otp_dj_pwr_load_hw_cfg(OtOTPDjState *s) 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)); @@ -3587,12 +1146,12 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; break; case OTP_TOKEN_TEST_EXIT: - partition = OTP_PART_SECRET0; - secret_addr = (unsigned)A_SECRET0_TEST_EXIT_TOKEN; + partition = (unsigned)OTP_PART_SECRET0; + secret_addr = A_SECRET0_TEST_EXIT_TOKEN; break; case OTP_TOKEN_RMA: - partition = OTP_PART_SECRET2; - secret_addr = (unsigned)A_SECRET2_RMA_TOKEN; + partition = (unsigned)OTP_PART_SECRET2; + secret_addr = A_SECRET2_RMA_TOKEN; break; default: g_assert_not_reached(); @@ -3604,9 +1163,9 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) /* byte offset of the secret within the partition */ unsigned secret_offset = - secret_addr - ot_otp_dj_part_data_offset(s, 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)], @@ -3623,364 +1182,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 < 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_dj_is_ecc_enabled(s) && s->part_descs[ix].integrity) { - if (ot_otp_dj_apply_ecc(s, ix)) { - continue; - } - } - - if (s->part_descs[ix].sw_digest) { - s->part_ctrls[ix].digest = ot_otp_dj_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_dj_bufferize_partition(s, ix); - if (s->part_descs[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 part_ix = 0u; part_ix < s->part_count; part_ix++) { - if (!s->part_descs[part_ix].secret) { - continue; - } - - if (!s->otp_scramble_key_xstrs[part_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_dj_part_name(s, part_ix), - part_ix); - return; - } - continue; - } - - size_t len = strlen(s->otp_scramble_key_xstrs[part_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_dj_part_name(s, part_ix), - part_ix); - return; - } - - g_assert(!s->otp_scramble_keys[part_ix]); - - s->otp_scramble_keys[part_ix] = - g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], - s->otp_scramble_key_xstrs[part_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_dj_part_name(s, part_ix)); - return; - } - - TRACE_OTP("otp_scramble_keys[%s] %s", ot_otp_dj_part_name(s, part_ix), - ot_otp_hexdump(s, s->otp_scramble_keys[part_ix], - OTP_SCRAMBLING_KEY_BYTES)); - } -} - -static void ot_otp_dj_add_scramble_key_props(OtOTPDjState *s) -{ - /* - * @todo: we know the number of secret partitions, so use it rather than - * whole partition count - */ - s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); - s->otp_scramble_key_xstrs = g_new0(char *, s->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, SECRET3 etc. - */ - 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[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 void ot_otp_dj_configure_inv_default_parts(OtOTPDjState *s) -{ - 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; - } - - g_assert(!s->inv_default_parts[part_ix]); - - s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); - if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], - 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_dj_part_name(s, part_ix), - ot_otp_hexdump(s, s->inv_default_parts[part_ix], part->size)); - } -} - -static void ot_otp_dj_add_inv_def_props(OtOTPDjState *s) -{ - s->inv_default_parts = g_new0(uint8_t *, 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_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, @@ -3998,47 +1207,10 @@ static const MemoryRegionOps ot_otp_dj_swcfg_ops = { static void ot_otp_dj_reset_enter(Object *obj, ResetType type) { - OtOTPDjClass *c = OT_OTP_DJ_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; @@ -4058,207 +1230,86 @@ 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 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 = OTP_BUF_IDLE; - } else { - s->part_ctrls[part_ix].state.u = OTP_UNBUF_IDLE; - continue; - } - unsigned part_size = ot_otp_dj_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; - } + 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) { - OtOTPDjClass *c = OT_OTP_DJ_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_DJ "-ctrl", - SW_CFG_WINDOW + SW_CFG_WINDOW_SIZE); + 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_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_DJ "-swcfg", SW_CFG_WINDOW_SIZE); - memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, + 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->part_descs = OtOTPPartDescs; - s->part_count = ARRAY_SIZE(OtOTPPartDescs); - s->part_life_cycle = OTP_PART_LIFE_CYCLE; - - 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->part_ctrls = g_new0(OtOTPPartController, s->part_count); - 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_life_cycle].size / - sizeof(uint16_t)); - - 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_dj_part_data_byte_size(s, part_ix) / sizeof(uint32_t); - s->part_ctrls[part_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); - - ot_otp_dj_add_scramble_key_props(s); - ot_otp_dj_add_inv_def_props(s); - -#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; - 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); - OtOTPDjClass *djc = OT_OTP_DJ_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_otp_dj_reset_enter, NULL, - &ot_otp_dj_reset_exit, - &djc->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->key_seeds = OT_OTP_KEY_SEEDS; + ic->has_flash_support = false; + ic->has_zer_support = true; } static const TypeInfo ot_otp_dj_info = { .name = TYPE_OT_OTP_DJ, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_OT_OTP_ENGINE, .instance_size = sizeof(OtOTPDjState), .instance_init = &ot_otp_dj_init, .class_size = sizeof(OtOTPDjClass), .class_init = &ot_otp_dj_class_init, .interfaces = (InterfaceInfo[]){ - { TYPE_OT_OTP_IF }, + { TYPE_OT_OTP_IF }, /* public OTP API */ + { TYPE_OT_OTP_IMPL_IF }, /* private OTP API for OTP engine */ {}, }, }; diff --git a/hw/opentitan/ot_otp_dj_parts.c b/hw/opentitan/ot_otp_dj_parts.c index 2073bb692219d..217edfb0abdd1 100644 --- a/hw/opentitan/ot_otp_dj_parts.c +++ b/hw/opentitan/ot_otp_dj_parts.c @@ -8,7 +8,7 @@ /* clang-format off */ /* NOLINTBEGIN */ -static const OtOTPPartDesc OtOTPPartDescs[] = { +static const OtOTPPartDesc OT_OTP_PART_DESCS[] = { [OTP_PART_VENDOR_TEST] = { .name = "VENDOR_TEST", .size = 64u, @@ -357,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[OTP_KEY_COUNT] = { + [OTP_KEY_OTBN] = { + .partition = OTP_PART_SECRET1, + .offset = 0, + .size = 16, + }, + [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 f1bc11bb65ca9..a4f0f569e25e2 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. @@ -29,59 +29,23 @@ */ #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_otp_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 "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_impl_if.h" +#include "ot_otp_engine.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 #define NUM_PART_UNBUF 5u -#define NUM_SRAM_KEY_REQ_SLOTS 4u #define NUM_SW_CFG_WINDOW_WORDS 512u #define OTP_BYTE_ADDR_WIDTH 11u /* 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) @@ -104,7 +68,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) @@ -120,9 +83,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) @@ -140,7 +100,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) @@ -166,7 +125,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) @@ -383,31 +342,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 R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) +#define SW_CFG_WINDOW_OFFSET 0x800u +#define SW_CFG_WINDOW_SIZE (NUM_SW_CFG_WINDOW_WORDS * sizeof(uint32_t)) #define R_LAST_REG (R_SECRET2_DIGEST_1) #define REGS_COUNT (R_LAST_REG + 1u) @@ -415,34 +355,6 @@ 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, @@ -450,23 +362,6 @@ static_assert(FLASH_KEY_SEED_WIDTH == SECRET1_FLASH_DATA_KEY_SEED_SIZE * 8u, 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) - typedef enum { OTP_PART_VENDOR_TEST, OTP_PART_CREATOR_SW_CFG, @@ -487,183 +382,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 { - const char *name; - 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 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; -} 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; - 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 { - SysBusDevice parent_obj; + OtOTPEngineState parent_obj; struct { MemoryRegion ctrl; @@ -672,62 +402,10 @@ 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 *part_ctrls; - const OtOTPPartDesc *part_descs; - unsigned part_count; - unsigned part_life_cycle; /* index of the Life Cycle partition */ - 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; /* some entries may be NULL */ - uint8_t **inv_default_parts; /* some entries 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; /* some entries may be NULL */ - char **inv_default_part_xstrs; /* some entries may be NULL */ - uint8_t edn_ep; - bool fatal_escalate; }; struct OtOTPEgClass { - SysBusDeviceClass parent_class; + OtOTPEngineClass parent_class; ResettablePhases parent_phases; }; @@ -794,43 +472,6 @@ 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 */ -}; - static const char *OTP_TOKEN_NAMES[] = { /* clang-format off */ OTP_NAME_ENTRY(OTP_TOKEN_TEST_UNLOCK), @@ -839,1665 +480,122 @@ static const char *OTP_TOKEN_NAMES[] = { /* clang-format on */ }; -/* clang-format off */ - /* fake partitions */ -/* 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 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 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 const char *ot_otp_eg_part_name(const OtOTPEgState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[part_ix].name; - } - - if (part_ix == s->part_count) { - return "DAI"; - } - - if (part_ix == s->part_count + 1u) { - return "KDI"; - } - - return "?"; -} - -static inline unsigned -ot_otp_eg_part_data_offset(const OtOTPEgState *s, unsigned part_ix) -{ - return (unsigned)(s->part_descs[part_ix].offset); -} - -static inline unsigned -ot_otp_eg_part_data_byte_size(const OtOTPEgState *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) * NUM_DIGEST_WORDS; - } - - return (unsigned)size; -} - -static bool ot_otp_eg_is_wide_granule(const OtOTPEgState *s, unsigned part_ix, - unsigned address) -{ - if (part_ix < s->part_count) { - if (s->part_descs[part_ix].secret) { - return true; - } - - if (s->part_descs[part_ix].digest_offset == - (address & OTP_DIGEST_ADDR_MASK)) { - return true; - } - } - - return false; -} - -static bool ot_otp_eg_is_buffered(const OtOTPEgState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[part_ix].buffered; - } - - return false; -} - -static bool ot_otp_eg_is_secret(const OtOTPEgState *s, unsigned part_ix) -{ - if (part_ix < s->part_count) { - return s->part_descs[part_ix].secret; - } - - return false; -} -static bool ot_otp_eg_is_backend_ecc_enabled(const OtOTPEgState *s) +static uint32_t ot_otp_eg_get_status(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 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(const OtOTPEgState *s, unsigned part_ix) -{ - return s->part_descs[part_ix].hw_digest || s->part_descs[part_ix].sw_digest; -} - -static bool ot_otp_eg_is_part_digest_offset(const OtOTPEgState *s, - unsigned part_ix, hwaddr addr) -{ - uint16_t offset = s->part_descs[part_ix].digest_offset; - - return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); -} - + 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 < s->part_count; pix++) { - OtOTPPartController *pctrl = &s->part_ctrls[pix]; - pctrl->failed = true; - } + return status; } -static void ot_otp_eg_set_error(OtOTPEgState *s, unsigned part_ix, - OtOTPError err) +static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) { - g_assert(part_ix < OTP_ENTRY_COUNT); - - uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; - if (errval || errval != s->regs[R_ERR_CODE_0 + part_ix]) { - trace_ot_otp_set_error(s->ot_id, ot_otp_eg_part_name(s, part_ix), - part_ix, ERR_CODE_NAME(err), err); - } - s->regs[R_ERR_CODE_0 + part_ix] = 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 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_eg_part_name(s, part_ix), part_ix); - return (OtOTPPartitionType)part_ix; - } - } - - 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 = s->part_descs[part_ix].offset >> 2u; - unsigned end = (ot_otp_eg_is_buffered(s, (int)part_ix) && - ot_otp_eg_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_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, - ot_otp_eg_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_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) -{ - g_assert(!ot_otp_eg_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_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 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 < s->part_count; part_ix++) { - if (ot_otp_eg_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_life_cycle == 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_eg_get_sw_readlock(const OtOTPEgState *s, unsigned rdlk_ix) -{ - uint32_t reg = R_VENDOR_TEST_READ_LOCK + rdlk_ix; - - return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); -} - -static bool ot_otp_eg_is_readable(const OtOTPEgState *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_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 < s->part_life_cycle); - - /* - * now that the count of read_lock_csr is known, use it to access - * the register for the selected partition - */ - return ot_otp_eg_get_sw_readlock(s, roffset); -} - -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 < 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_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)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_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 part_ix) -{ - OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; - - unsigned offset = (unsigned)s->part_descs[part_ix].offset; - unsigned part_size = ot_otp_eg_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; - - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; - g_assert(scrambling_key); - - OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); - - trace_ot_otp_unscramble_partition(s->ot_id, ot_otp_eg_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_eg_bufferize_partition(OtOTPEgState *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_eg_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_eg_unscramble_partition(s, part_ix); - } - } else { - unsigned offset = (unsigned)s->part_descs[part_ix].offset; - unsigned part_size = ot_otp_eg_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_eg_check_buffered_partition_integrity(OtOTPEgState *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_eg_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_eg_part_data_offset(s, part_ix); - unsigned part_size = ot_otp_eg_part_data_byte_size(s, part_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, ot_otp_eg_part_name(s, part_ix), - part_ix, digest, pctrl->digest); - - TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", - ot_otp_eg_part_name(s, part_ix), digest, - ot_otp_hexdump(s, part_data, part_size)); - - pctrl->failed = true; - /* this is a fatal error */ - ot_otp_eg_set_error(s, part_ix, OTP_CHECK_FAIL_ERROR); - /* TODO: revert buffered part to default */ - } else { - trace_ot_otp_integrity_report(s->ot_id, ot_otp_eg_part_name(s, part_ix), - part_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; - } - - 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; - } - - 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_eg_part_name(s, part_ix)); - return; - } - - bool is_readable = ot_otp_eg_is_readable(s, part_ix); - bool is_wide = ot_otp_eg_is_wide_granule(s, part_ix, address); - bool is_buffered = ot_otp_eg_is_buffered(s, part_ix); - bool is_secret = ot_otp_eg_is_secret(s, part_ix); - bool is_digest = ot_otp_eg_is_part_digest_offset(s, 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, ot_otp_eg_part_name(s, part_ix), address); - ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); - return; - } - - unsigned part_offset = address - ot_otp_eg_part_data_offset(s, part_ix); - unsigned part_waddr = part_offset >> 2u; - bool do_ecc = - s->part_descs[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(s, part_ix) / sizeof(uint32_t)); - - if (is_wide || is_digest) { - /* 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_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) < 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_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[part_ix]; - 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(s, 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, - ot_otp_eg_part_name(s, (unsigned)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, - ot_otp_eg_part_name(s, (unsigned)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 >= s->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->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_eg_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_eg_part_name(s, 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, ot_otp_eg_part_name(s, 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(s, part_ix, address); - bool is_wide = ot_otp_eg_is_wide_granule(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_eg_part_name(s, 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; - } - - unsigned part_ix = (unsigned)partition; - - if (part_ix >= s->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 (!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_eg_part_name(s, part_ix), - part_ix); - ot_otp_eg_dai_set_error(s, 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_eg_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_eg_part_name(s, 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: Partition %s (%u) is write locked\n", __func__, - s->ot_id, ot_otp_eg_part_name(s, part_ix), part_ix); - 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(s, part_ix); - unsigned part_size = ot_otp_eg_part_data_byte_size(s, part_ix); - - 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 < s->part_count)); - - DAI_CHANGE_STATE(s, 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_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, ot_otp_eg_part_name(s, part_ix), - part_ix, *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, - ot_otp_eg_part_name(s, - (unsigned)s->dai->partition), - (unsigned)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; + 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: @@ -2538,22 +636,22 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, case R_INTR_STATE: val32 &= INTR_WMASK; s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ - ot_otp_eg_update_irqs(s); + c->update_irqs(s); break; case R_INTR_ENABLE: val32 &= INTR_WMASK; s->regs[R_INTR_ENABLE] = val32; - ot_otp_eg_update_irqs(s); + c->update_irqs(s); break; case R_INTR_TEST: val32 &= INTR_WMASK; s->regs[R_INTR_STATE] = val32; - ot_otp_eg_update_irqs(s); + c->update_irqs(s); break; case R_ALERT_TEST: val32 &= ALERT_WMASK; s->regs[reg] = val32; - ot_otp_eg_update_alerts(s); + c->update_alerts(s); break; case R_DIRECT_ACCESS_REGWEN: val32 &= R_DIRECT_ACCESS_REGWEN_REGWEN_MASK; @@ -2561,11 +659,11 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, break; case R_DIRECT_ACCESS_CMD: if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { - ot_otp_eg_dai_read(s); + c->dai_read(s); } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { - ot_otp_eg_dai_write(s); + c->dai_write(s); } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { - ot_otp_eg_dai_digest(s); + c->dai_digest(s); } break; case R_DIRECT_ACCESS_ADDRESS: @@ -2751,12 +849,13 @@ static const char *ot_otp_eg_swcfg_reg_name(unsigned swreg) 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); + OtOTPEngineState *s = OT_OTP_ENGINE(opaque); + OtOTPEngineClass *c = OT_OTP_ENGINE_GET_CLASS(s); (void)size; (void)attrs; hwaddr reg = R32_OFF(addr); - int partition = ot_otp_eg_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"); @@ -2767,26 +866,27 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( unsigned part_ix = (unsigned)partition; - if (ot_otp_eg_is_buffered(s, 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_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); + c->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(s, 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) { + if (!is_readable && !(is_digest || is_zer)) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); - ot_otp_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); + c->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); + c->set_error(s, part_ix, OTP_NO_ERROR); uint64_t pc; @@ -2803,8 +903,8 @@ 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) { - const OtOTPEgState *es = OT_OTP_EG(dev); - const OtOTPStorage *otp = es->otp; + const OtOTPEngineState *s = OT_OTP_ENGINE(dev); + const OtOTPStorage *otp = s->otp; if (lc_tcount) { memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], @@ -2816,639 +916,91 @@ static void ot_otp_eg_get_lc_info( } if (lc_valid) { - *lc_valid = !(es->part_ctrls[OTP_PART_SECRET0].failed || - es->part_ctrls[OTP_PART_SECRET2].failed || - es->part_ctrls[OTP_PART_LIFE_CYCLE].failed) ? + *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 = (!es->part_ctrls[OTP_PART_SECRET2].failed && - es->part_ctrls[OTP_PART_SECRET2].locked) ? + *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 = es->tokens; - } -} - -static const OtOTPHWCfg *ot_otp_eg_get_hw_cfg(const OtOTPIf *dev) -{ - const OtOTPEgState *es = OT_OTP_EG(dev); - - 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->part_ctrls[OTP_PART_SECRET1]; - g_assert(ot_otp_eg_is_buffered(s, OTP_PART_SECRET1)); - uint32_t poffset = - s->part_descs[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(OtOTPIf *dev, OtOTPKeyType type, - OtOTPKey *key) -{ - OtOTPEgState *es = OT_OTP_EG(dev); - - 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; + *tokens = s->tokens; } } - static void ot_otp_eg_get_keymgr_secret( OtOTPIf *dev, OtOTPKeyMgrSecretType type, OtOTPKeyMgrSecret *secret) { - OtOTPEgState *es = OT_OTP_EG(dev); + OtOTPEngineState *s = OT_OTP_ENGINE(dev); 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 - - es->part_descs[partition].offset; + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE0 - s->part_descs[partition].offset; break; case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: partition = OTP_PART_SECRET2; - offset = A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - - es->part_descs[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(es, 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->part_ctrls[part_ix].buffer.data; - } else { - /* source data from PartInvDefault instead of real buffer */ - data_ptr = es->inv_default_parts[part_ix]; - } - - secret->valid = es->part_ctrls[part_ix].digest != 0; - memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); -} - -static bool ot_otp_eg_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, - const uint16_t *lc_state, - ot_otp_program_ack_fn ack, void *opaque) -{ - OtOTPEgState *es = OT_OTP_EG(dev); - 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(); + offset = + A_SECRET2_CREATOR_ROOT_KEY_SHARE1 - s->part_descs[partition].offset; 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 == - es->part_descs[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 = &s->part_descs[s->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, s->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 = &s->part_descs[s->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 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) { - 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; - } + case OTP_KEYMGR_SECRET_CREATOR_SEED: + case 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; + } - 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 */ + data_ptr = s->inv_default_parts[part_ix]; + } - 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); - } + 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_eg_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_eg_pwr_load_hw_cfg(OtOTPEgState *s) +static void ot_otp_eg_pwr_load_hw_cfg(OtOTPEngineState *s) { OtOTPStorage *otp = s->otp; OtOTPHWCfg *hw_cfg = s->hw_cfg; @@ -3467,7 +1019,7 @@ static void ot_otp_eg_pwr_load_hw_cfg(OtOTPEgState *s) OT_MULTIBITBOOL8_TRUE; } -static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) +static void ot_otp_eg_pwr_load_tokens(OtOTPEngineState *s) { memset(s->tokens, 0, sizeof(*s->tokens)); @@ -3502,9 +1054,9 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) /* byte offset of the secret within the partition */ unsigned secret_offset = - secret_addr - ot_otp_eg_part_data_offset(s, 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)], @@ -3521,459 +1073,14 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEgState *s) } } -static void ot_otp_eg_pwr_initialize_partitions(OtOTPEgState *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_eg_is_ecc_enabled(s) && s->part_descs[ix].integrity) { - if (ot_otp_eg_apply_ecc(s, ix)) { - continue; - } - } - - if (s->part_descs[ix].sw_digest) { - s->part_ctrls[ix].digest = ot_otp_eg_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_eg_bufferize_partition(s, ix); - if (s->part_descs[ix].hw_digest) { - ot_otp_eg_check_buffered_partition_integrity(s, ix); - } - continue; - } - } -} - -static void ot_otp_eg_pwr_otp_bh(void *opaque) +static void ot_otp_eg_signal_pwr_sequence(OtOTPImplIf *dev) { - OtOTPEgState *s = opaque; + OtOTPEngineState *s = OT_OTP_ENGINE(dev); - /* - * 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); -} - -static void ot_otp_eg_configure_scrmbl_key(OtOTPEgState *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_eg_configure_digest(OtOTPEgState *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_eg_configure_flash(OtOTPEgState *s) -{ - 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_eg_configure_sram(OtOTPEgState *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_eg_configure_part_scramble_keys(OtOTPEgState *s) -{ - for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { - if (!s->part_descs[part_ix].secret) { - continue; - } - - if (!s->otp_scramble_key_xstrs[part_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_eg_part_name(s, part_ix), - part_ix); - return; - } - continue; - } - - size_t len = strlen(s->otp_scramble_key_xstrs[part_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_eg_part_name(s, part_ix), - part_ix); - return; - } - - g_assert(!s->otp_scramble_keys[part_ix]); - - s->otp_scramble_keys[part_ix] = - g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], - s->otp_scramble_key_xstrs[part_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_eg_part_name(s, part_ix)); - return; - } - - TRACE_OTP("otp_scramble_keys[%s] %s", ot_otp_eg_part_name(s, part_ix), - ot_otp_hexdump(s, s->otp_scramble_keys[part_ix], - OTP_SCRAMBLING_KEY_BYTES)); - } -} - -static void ot_otp_eg_add_scramble_key_props(OtOTPEgState *s) -{ - /* - * @todo: we know the number of secret partitions, so use it rather than - * whole partition count - */ - s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); - s->otp_scramble_key_xstrs = g_new0(char *, s->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, etc. - */ - 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[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 void ot_otp_eg_configure_inv_default_parts(OtOTPEgState *s) -{ - 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; - } - - g_assert(!s->inv_default_parts[part_ix]); - - s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); - if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], - 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_eg_part_name(s, part_ix), - ot_otp_hexdump(s, s->inv_default_parts[part_ix], part->size)); - } -} - -static void ot_otp_eg_add_inv_def_props(OtOTPEgState *s) -{ - s->inv_default_parts = g_new0(uint8_t *, 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_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, @@ -3991,46 +1098,10 @@ static const MemoryRegionOps ot_otp_eg_swcfg_ops = { static void ot_otp_eg_reset_enter(Object *obj, ResetType type) { - OtOTPEgClass *c = OT_OTP_EG_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; @@ -4041,208 +1112,90 @@ 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 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 = OTP_BUF_IDLE; - } else { - s->part_ctrls[part_ix].state.u = OTP_UNBUF_IDLE; - continue; - } - unsigned part_size = ot_otp_eg_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; - } + 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) { - OtOTPEgClass *c = OT_OTP_EG_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_EG "-ctrl", - SW_CFG_WINDOW + SW_CFG_WINDOW_SIZE); + 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_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_EG "-swcfg", SW_CFG_WINDOW_SIZE); - memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, + 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->part_descs = OtOTPPartDescs; - s->part_count = ARRAY_SIZE(OtOTPPartDescs); - s->part_life_cycle = OTP_PART_LIFE_CYCLE; - - 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->part_ctrls = g_new0(OtOTPPartController, s->part_count); - 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_life_cycle].size / - sizeof(uint16_t)); - - 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_eg_part_data_byte_size(s, part_ix) / sizeof(uint32_t); - s->part_ctrls[part_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); - - ot_otp_eg_add_scramble_key_props(s); - ot_otp_eg_add_inv_def_props(s); - -#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; - 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); - OtOTPEgClass *edc = OT_OTP_EG_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_otp_eg_reset_enter, NULL, - &ot_otp_eg_reset_exit, - &edc->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->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); + } } static const TypeInfo ot_otp_eg_info = { .name = TYPE_OT_OTP_EG, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_OT_OTP_ENGINE, .instance_size = sizeof(OtOTPEgState), .instance_init = &ot_otp_eg_init, .class_size = sizeof(OtOTPEgClass), .class_init = &ot_otp_eg_class_init, .interfaces = (InterfaceInfo[]){ - { TYPE_OT_OTP_IF }, + { TYPE_OT_OTP_IF }, /* public OTP API */ + { TYPE_OT_OTP_IMPL_IF }, /* private OTP API for OTP engine */ {}, }, }; diff --git a/hw/opentitan/ot_otp_eg_parts.c b/hw/opentitan/ot_otp_eg_parts.c index 05e3a75a5b29a..d3069987c7b3a 100644 --- a/hw/opentitan/ot_otp_eg_parts.c +++ b/hw/opentitan/ot_otp_eg_parts.c @@ -1,15 +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, @@ -19,11 +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, @@ -33,11 +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, @@ -47,11 +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, @@ -61,11 +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, @@ -75,11 +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, @@ -88,11 +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, @@ -101,11 +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, @@ -114,11 +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, @@ -127,11 +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, @@ -141,11 +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, @@ -154,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[OTP_KEY_COUNT] = { + [OTP_KEY_FLASH_ADDR] = { + .partition = OTP_PART_SECRET1, + .offset = 0, + .size = 32, + }, + [OTP_KEY_FLASH_DATA] = { + .partition = OTP_PART_SECRET1, + .offset = 32, + .size = 32, + }, + [OTP_KEY_OTBN] = { + .partition = OTP_PART_SECRET1, + .offset = 64, + .size = 16, + }, + [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..7c06afc9ec836 --- /dev/null +++ b/hw/opentitan/ot_otp_engine.c @@ -0,0 +1,2886 @@ +/* + * 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. + */ + +#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_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 "ot_otp_engine.h" +#include "sysemu/block-backend.h" +#include "trace.h" + +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 */ +}; + +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 */ +}; + +#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)(DA_REG_##_reg_)]) +#define ERR_CODE_PART_REG(_s_, _pix_) \ + ((_s_)->regs[(_s_)->reg_offset.err_code_base + (_pix_)]) + +#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); + +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, OTP_DAI_ERROR); + LCI_CHANGE_STATE(s, 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) & 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 OTP_MACRO_ERROR: + case OTP_MACRO_ECC_UNCORR_ERROR: + s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; + ot_otp_engine_update_alerts(s); + break; + /* NOLINTNEXTLINE */ + case OTP_MACRO_ECC_CORR_ERROR: + /* + * "The corresponding controller automatically recovers from this error + * when issuing a new command." + */ + break; + case OTP_MACRO_WRITE_BLANK_ERROR: + break; + case OTP_ACCESS_ERROR: + ic->update_status_error(OT_OTP_IMPL_IF(s), OT_OTP_STATUS_DAI, true); + break; + case OTP_CHECK_FAIL_ERROR: + case 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 != 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) ? 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_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) ? 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_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 / 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], 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, 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 < 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; + + const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; + g_assert(scrambling_key); + + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, scrambling_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, 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, 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 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_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, 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, 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, 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, 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, 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 + */ + const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; + 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); + } + + DIRECT_ACCESS_REG(s, RDATA_0) = data_lo; + DIRECT_ACCESS_REG(s, RDATA_1) = data_hi; + + if (err) { + OtOTPError otp_err = + (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : 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, 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) { + 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_engine_set_error(s, OTP_ENTRY_DAI(s), + 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, 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), + 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, 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), + 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, 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), + 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, 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, OTP_MACRO_ERROR); + return; + } + + DAI_CHANGE_STATE(s, 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, 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, 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, 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, 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, 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, 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, OTP_MACRO_ERROR); + return; + } + + DAI_CHANGE_STATE(s, 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, 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, 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, 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, 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, OTP_ACCESS_ERROR); + return; + } + + DAI_CHANGE_STATE(s, 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, 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, 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, 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), + 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, 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), + 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, 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, 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 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, 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_engine_lci_init(OtOTPEngineState *s) +{ + LCI_CHANGE_STATE(s, 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 < 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 */ + switch (type) { + case OTP_KEY_FLASH_DATA: + if (!ic->has_flash_support) { + break; + } + memcpy(key->seed, s->scrmbl_key_init->key, FLASH_KEY_BYTES); + memcpy(key->nonce, s->scrmbl_key_init->nonce, FLASH_NONCE_BYTES); + key->seed_size = FLASH_KEY_BYTES; + key->nonce_size = FLASH_NONCE_BYTES; + key->seed_valid = false; + ot_otp_engine_generate_scrambling_key(s, key, type, s->flash_data_iv, + s->flash_data_const, true, false); + return; + case OTP_KEY_FLASH_ADDR: + if (!ic->has_flash_support) { + break; + } + memcpy(key->seed, s->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; + ot_otp_engine_generate_scrambling_key(s, key, type, s->flash_addr_iv, + s->flash_addr_const, true, false); + return; + case OTP_KEY_OTBN: + memcpy(key->seed, s->scrmbl_key_init->key, OTBN_KEY_BYTES); + memcpy(key->nonce, s->scrmbl_key_init->nonce, OTBN_NONCE_BYTES); + key->seed_size = OTBN_KEY_BYTES; + key->nonce_size = OTBN_NONCE_BYTES; + key->seed_valid = false; + ot_otp_engine_generate_scrambling_key(s, key, type, s->sram_iv, + s->sram_const, true, true); + return; + case OTP_KEY_SRAM: + memcpy(key->seed, s->scrmbl_key_init->key, SRAM_KEY_BYTES); + memcpy(key->nonce, s->scrmbl_key_init->nonce, SRAM_NONCE_BYTES); + key->seed_size = SRAM_KEY_BYTES; + key->nonce_size = SRAM_NONCE_BYTES; + key->seed_valid = false; + ot_otp_engine_generate_scrambling_key(s, key, type, s->sram_iv, + s->sram_const, true, true); + return; + default: + return; + } + + error_report("%s: %s: invalid OTP key type: %d", __func__, s->ot_id, type); +} +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 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 == 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 == OTP_NO_ERROR) { + lci->error = OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, 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 == 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_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 == OTP_LCI_ERROR) { + lci->error = 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 = OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, 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 == OTP_NO_ERROR) { + LCI_CHANGE_STATE(s, OTP_LCI_IDLE); + ot_otp_engine_lci_write_complete(s, true); + } else { + LCI_CHANGE_STATE(s, OTP_LCI_ERROR); + ot_otp_engine_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_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 == 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_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) +{ + 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_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); + + for (unsigned part_ix = 0u; part_ix < s->part_count; part_ix++) { + if (!s->part_descs[part_ix].secret) { + continue; + } + + if (!s->otp_scramble_key_xstrs[part_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; + } + continue; + } + + size_t len = strlen(s->otp_scramble_key_xstrs[part_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; + } + + g_assert(!s->otp_scramble_keys[part_ix]); + + s->otp_scramble_keys[part_ix] = + g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); + if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], + s->otp_scramble_key_xstrs[part_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, s->otp_scramble_keys[part_ix], + OTP_SCRAMBLING_KEY_BYTES)); + } +} + +static void ot_otp_engine_add_scramble_key_props(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + /* + * @todo we know the number of secret partitions, so use it rather than + * whole partition count + */ + s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); + s->otp_scramble_key_xstrs = g_new0(char *, s->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[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 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; + } + + g_assert(!s->inv_default_parts[part_ix]); + + s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); + if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], + 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, s->inv_default_parts[part_ix], part->size)); + } +} + +static void ot_otp_engine_add_inv_def_props(OtOTPEngineState *s) +{ + g_assert(s->part_count); + + s->inv_default_parts = g_new0(uint8_t *, 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 = OTP_BUF_IDLE; + } else { + s->part_ctrls[part_ix].state.u = 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, OTP_DAI_RESET); + LCI_CHANGE_STATE(s, 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); + + /* + * 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); + 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_engine.h b/hw/opentitan/ot_otp_engine.h new file mode 100644 index 0000000000000..862c17fc2bf58 --- /dev/null +++ b/hw/opentitan/ot_otp_engine.h @@ -0,0 +1,468 @@ +/* + * 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 NUM_IRQS 2u +#define NUM_ALERTS 5u +#define NUM_DAI_WORDS 2u +#define NUM_DIGEST_WORDS 2u +#define NUM_ZER_WORDS 2u +#define NUM_SRAM_KEY_REQ_SLOTS 4u + +/* + * 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 */ +#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 + +#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) + +#define LC_TRANSITION_CNT_SIZE 48u +#define LC_STATE_SIZE 40u + +/* 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) + +#define OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) +#define OTP_ZER_ADDR_MASK (sizeof(uint64_t) - 1u) + +/* 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, + OTP_ERROR_COUNT, +} 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; + +/* Direct Access Interface states */ +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; + +typedef enum { + DA_REG_REGWEN, + DA_REG_CMD, + DA_REG_ADDRESS, + DA_REG_WDATA_0, + DA_REG_WDATA_1, + DA_REG_RDATA_0, + 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; +} 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; + 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; + +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[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 *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]; + /* OTP scrambling key constants, not constants for deriving other keys */ + uint8_t **otp_scramble_keys; /* some entries may be NULL */ + uint8_t **inv_default_parts; /* some entries 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; + /* 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); +}; + +/* Comportable registers are identical for all OTP variants */ +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) + +#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) + +/* REG32 bitfields common to all new OTP versions */ +FIELD(DIRECT_ACCESS_CMD, RD, 0u, 1u) +FIELD(DIRECT_ACCESS_CMD, WR, 1u, 1u) +FIELD(DIRECT_ACCESS_CMD, DIGEST, 2u, 1u) +/* ZEROIZE has been introduced in OTP v2.10, after Earlgrey v1.0 / OTP v2.0 */ +FIELD(DIRECT_ACCESS_CMD, ZEROIZE, 3u, 1u) + +SHARED_FIELD(ERR_CODE, 0u, 3u) +static_assert(ERR_CODE_MASK >= OTP_ERROR_COUNT - 1u, + "Error mask not large enough"); + +SHARED_FIELD(READ_LOCK, 0u, 1u) + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) +#define OTP_NAME_ENTRY(_st_) [_st_] = stringify(_st_) + +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) * NUM_DIGEST_WORDS; + } + + if (s->part_descs[part_ix].zeroizable) { + size -= sizeof(uint32_t) * 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 & ~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 & ~OTP_ZER_ADDR_MASK) == offset); +} + +static inline uint32_t ot_otp_engine_dai_is_busy(const OtOTPEngineState *s) +{ + return s->dai->state != OTP_DAI_IDLE; +} + +#endif /* HW_OPENTITAN_OT_OTP_ENGINE_H */ 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) From e7325305973ecb5471f90f4a9210f78ab13e6806 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Oct 2025 12:56:22 +0100 Subject: [PATCH 09/14] [ot] hw/opentitan: ot_otp: rework key management - use Top defined constants - simplify stack of macro calls - remove definitions from ot_otp_engine.h Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 9 +- hw/opentitan/ot_otp_eg.c | 17 +-- hw/opentitan/ot_otp_engine.c | 144 ++++++++++++++++++++------ hw/opentitan/ot_otp_engine.h | 72 ++----------- include/hw/opentitan/ot_otp_impl_if.h | 5 +- 5 files changed, 138 insertions(+), 109 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index b0d6585deaef6..f9daea95b9880 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -42,6 +42,7 @@ #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 OTP_BYTE_ADDR_WIDTH 14u @@ -406,9 +407,6 @@ REG32(LC_STATE, 16344u) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") -static_assert(SRAM_KEY_SEED_WIDTH == SECRET1_SRAM_DATA_KEY_SEED_SIZE * 8u, - "SRAM key seed size does not match OTP field size"); - typedef enum { OTP_PART_VENDOR_TEST, OTP_PART_CREATOR_SW_CFG, @@ -1294,9 +1292,14 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) 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; + + g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_SRAM].size == + SECRET1_SRAM_DATA_KEY_SEED_SIZE); } static const TypeInfo ot_otp_dj_info = { diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index a4f0f569e25e2..9cdcf9099c252 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -40,6 +40,7 @@ #define NUM_PART 11u #define NUM_PART_BUF 6u #define NUM_PART_UNBUF 5u +#define NUM_SRAM_KEY_REQ_SLOTS 4u #define NUM_SW_CFG_WINDOW_WORDS 512u #define OTP_BYTE_ADDR_WIDTH 11u @@ -355,13 +356,6 @@ REG32(LC_STATE, 2008u) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") -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"); - typedef enum { OTP_PART_VENDOR_TEST, OTP_PART_CREATOR_SW_CFG, @@ -1176,6 +1170,8 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) 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; @@ -1183,6 +1179,13 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) g_assert(!ic->part_descs[part_ix].zeroizable && ic->part_descs[part_ix].zer_offset == UINT16_MAX); } + + g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_FLASH_ADDR].size == + SECRET1_FLASH_ADDR_KEY_SEED_SIZE); + g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_FLASH_DATA].size == + SECRET1_FLASH_DATA_KEY_SEED_SIZE); + g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_SRAM].size == + SECRET1_SRAM_DATA_KEY_SEED_SIZE); } static const TypeInfo ot_otp_eg_info = { diff --git a/hw/opentitan/ot_otp_engine.c b/hw/opentitan/ot_otp_engine.c index 7c06afc9ec836..9b919611b5e50 100644 --- a/hw/opentitan/ot_otp_engine.c +++ b/hw/opentitan/ot_otp_engine.c @@ -45,6 +45,28 @@ #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 + static const char *DAI_STATE_NAMES[] = { /* clang-format off */ OTP_NAME_ENTRY(OTP_DAI_RESET), @@ -124,6 +146,29 @@ static const char *ERR_CODE_NAMES[] = { #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[OTP_KEY_SRAM].size) +#define SRAM_NONCE_BYTES(_ic_) ((_ic_)->key_seeds[OTP_KEY_SRAM].size) +#define OTBN_KEY_BYTES(_ic_) ((_ic_)->key_seeds[OTP_KEY_OTBN].size) +#define OTBN_NONCE_BYTES(_ic_) (((_ic_)->key_seeds[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__); @@ -146,6 +191,19 @@ static void ot_otp_engine_dai_change_state_line(OtOTPEngineState *s, 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]; @@ -1763,54 +1821,67 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, 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 OTP_KEY_FLASH_DATA: if (!ic->has_flash_support) { + iv = NULL; break; } - memcpy(key->seed, s->scrmbl_key_init->key, FLASH_KEY_BYTES); - memcpy(key->nonce, s->scrmbl_key_init->nonce, FLASH_NONCE_BYTES); - key->seed_size = FLASH_KEY_BYTES; - key->nonce_size = FLASH_NONCE_BYTES; - key->seed_valid = false; - ot_otp_engine_generate_scrambling_key(s, key, type, s->flash_data_iv, - s->flash_data_const, true, false); + 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; return; case OTP_KEY_FLASH_ADDR: if (!ic->has_flash_support) { + iv = NULL; break; } - memcpy(key->seed, s->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; - ot_otp_engine_generate_scrambling_key(s, key, type, s->flash_addr_iv, - s->flash_addr_const, true, false); + 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; return; case OTP_KEY_OTBN: - memcpy(key->seed, s->scrmbl_key_init->key, OTBN_KEY_BYTES); - memcpy(key->nonce, s->scrmbl_key_init->nonce, OTBN_NONCE_BYTES); - key->seed_size = OTBN_KEY_BYTES; - key->nonce_size = OTBN_NONCE_BYTES; - key->seed_valid = false; - ot_otp_engine_generate_scrambling_key(s, key, type, s->sram_iv, - s->sram_const, true, true); + 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; return; case OTP_KEY_SRAM: - memcpy(key->seed, s->scrmbl_key_init->key, SRAM_KEY_BYTES); - memcpy(key->nonce, s->scrmbl_key_init->nonce, SRAM_NONCE_BYTES); - key->seed_size = SRAM_KEY_BYTES; - key->nonce_size = SRAM_NONCE_BYTES; - key->seed_valid = false; - ot_otp_engine_generate_scrambling_key(s, key, type, s->sram_iv, - s->sram_const, true, true); + 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; return; default: - return; + iv = NULL; + ingest_entropy = false; + break; } - error_report("%s: %s: invalid OTP key type: %d", __func__, s->ot_id, type); + 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) @@ -2222,20 +2293,24 @@ static void ot_otp_engine_pwr_otp_bh(void *opaque) 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) { + 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, + &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); @@ -2243,8 +2318,8 @@ static void ot_otp_engine_configure_scrmbl_key(OtOTPEngineState *s) } if (ot_common_parse_hexa_str(s->scrmbl_key_init->nonce, - &s->scrmbl_key_xstr[SRAM_KEY_BYTES * 2u], - SRAM_NONCE_BYTES, false, true)) { + &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; @@ -2770,6 +2845,7 @@ static void ot_otp_engine_init(Object *obj) 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 @@ -2806,7 +2882,7 @@ static void ot_otp_engine_init(Object *obj) 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); + ot_fifo32_create(&s->keygen->entropy_buf, OTP_ENTROPY_BUF_COUNT(ic)); s->keygen->present = ot_present_new(); s->keygen->prng = ot_prng_allocate(); diff --git a/hw/opentitan/ot_otp_engine.h b/hw/opentitan/ot_otp_engine.h index 862c17fc2bf58..deacfce858a44 100644 --- a/hw/opentitan/ot_otp_engine.h +++ b/hw/opentitan/ot_otp_engine.h @@ -45,61 +45,15 @@ #define TYPE_OT_OTP_ENGINE "ot-otp_engine" OBJECT_DECLARE_TYPE(OtOTPEngineState, OtOTPEngineClass, OT_OTP_ENGINE) -#define NUM_IRQS 2u -#define NUM_ALERTS 5u -#define NUM_DAI_WORDS 2u -#define NUM_DIGEST_WORDS 2u -#define NUM_ZER_WORDS 2u -#define NUM_SRAM_KEY_REQ_SLOTS 4u - -/* - * 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 */ -#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 - -#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) +#define NUM_IRQS 2u +#define NUM_ALERTS 5u +#define NUM_DAI_WORDS 2u +#define NUM_DIGEST_WORDS 2u +#define NUM_ZER_WORDS 2u #define LC_TRANSITION_CNT_SIZE 48u #define LC_STATE_SIZE 40u -/* 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) - #define OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) #define OTP_ZER_ADDR_MASK (sizeof(uint64_t) - 1u) @@ -205,7 +159,7 @@ typedef struct { bool write_lock; } OtOTPPartController; -typedef struct { +typedef struct OtOTPDAIController { QEMUTimer *delay; /* simulate delayed access completion */ QEMUBH *digest_bh; /* write computed digest to OTP cell */ OtOTPDAIState state; @@ -243,18 +197,8 @@ typedef struct { 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; +typedef struct OtOTPKeyGen_ OtOTPKeyGen; +typedef struct OtOTPScrmblKeyInit_ OtOTPScrmblKeyInit; typedef struct { uint32_t dai_base; /* offset of DIRECT_ACCESS_REGWEN register */ diff --git a/include/hw/opentitan/ot_otp_impl_if.h b/include/hw/opentitan/ot_otp_impl_if.h index b5c47005cb4f2..bc54b68c8a42c 100644 --- a/include/hw/opentitan/ot_otp_impl_if.h +++ b/include/hw/opentitan/ot_otp_impl_if.h @@ -82,12 +82,15 @@ struct OtOTPImplIfClass { /* Array of OTP_KEY_COUNT descriptors */ const OtOTPKeySeed *key_seeds; - /* Count of partition */ + /* 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; From 9b0357433aac28d9f6724795dc80d492fc2cffd8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Oct 2025 13:01:34 +0100 Subject: [PATCH 10/14] [ot] hw/opentitan: ot_otp: move ot_otp_engine.h to public header directory This is needed to implement other OTP top-specific implementations. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 2 +- hw/opentitan/ot_otp_eg.c | 2 +- hw/opentitan/ot_otp_engine.c | 2 +- {hw => include/hw}/opentitan/ot_otp_engine.h | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename {hw => include/hw}/opentitan/ot_otp_engine.h (100%) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index f9daea95b9880..13c0ee1246d51 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -34,8 +34,8 @@ #include "qemu/log.h" #include "qom/object.h" #include "hw/opentitan/ot_otp_dj.h" +#include "hw/opentitan/ot_otp_engine.h" #include "hw/opentitan/ot_otp_impl_if.h" -#include "ot_otp_engine.h" #include "trace.h" #define NUM_ERROR_ENTRIES 24u diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 9cdcf9099c252..97ea53ae9e257 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -32,8 +32,8 @@ #include "qemu/log.h" #include "qom/object.h" #include "hw/opentitan/ot_otp_eg.h" +#include "hw/opentitan/ot_otp_engine.h" #include "hw/opentitan/ot_otp_impl_if.h" -#include "ot_otp_engine.h" #include "trace.h" #define NUM_ERROR_ENTRIES 13u diff --git a/hw/opentitan/ot_otp_engine.c b/hw/opentitan/ot_otp_engine.c index 9b919611b5e50..04a2151167f14 100644 --- a/hw/opentitan/ot_otp_engine.c +++ b/hw/opentitan/ot_otp_engine.c @@ -35,13 +35,13 @@ #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 "ot_otp_engine.h" #include "sysemu/block-backend.h" #include "trace.h" diff --git a/hw/opentitan/ot_otp_engine.h b/include/hw/opentitan/ot_otp_engine.h similarity index 100% rename from hw/opentitan/ot_otp_engine.h rename to include/hw/opentitan/ot_otp_engine.h From bd5ef025ae9c67dfe968bd6cc749dddab683ec0d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Oct 2025 15:23:30 +0100 Subject: [PATCH 11/14] [ot] hw/opentitan: ot_otp_engine: rework partition "readconfig" properties - secret_scrambling_keys: the exact number of such properties is created. this avoid allocated useless memory, and ensure QEMU property subsystem may report any attempt to define values for keys that do not exist - secret_scrambling_key and inv_default_data values are now tied to the partition controller, rather than to be allocated as separate arrays Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 3 +- hw/opentitan/ot_otp_eg.c | 3 +- hw/opentitan/ot_otp_engine.c | 77 +++++++++++++++------------- include/hw/opentitan/ot_otp_engine.h | 6 +-- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 13c0ee1246d51..7e022c0dd8301 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1073,7 +1073,8 @@ static void ot_otp_dj_get_keymgr_secret( data_ptr = (const uint8_t *)s->part_ctrls[part_ix].buffer.data; } else { /* source data from PartInvDefault instead of real buffer */ - data_ptr = s->inv_default_parts[part_ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + data_ptr = pctrl->inv_default_data; } secret->valid = s->part_ctrls[part_ix].digest != 0; diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 97ea53ae9e257..7b04e956b2eb1 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -962,7 +962,8 @@ static void ot_otp_eg_get_keymgr_secret( data_ptr = (const uint8_t *)s->part_ctrls[part_ix].buffer.data; } else { /* source data from PartInvDefault instead of real buffer */ - data_ptr = s->inv_default_parts[part_ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + data_ptr = pctrl->inv_default_data; } secret->valid = s->part_ctrls[part_ix].digest != 0; diff --git a/hw/opentitan/ot_otp_engine.c b/hw/opentitan/ot_otp_engine.c index 04a2151167f14..def3852ccf399 100644 --- a/hw/opentitan/ot_otp_engine.c +++ b/hw/opentitan/ot_otp_engine.c @@ -864,11 +864,10 @@ ot_otp_engine_unscramble_partition(OtOTPEngineState *s, unsigned part_ix) g_assert(pctrl->buffer.data != NULL); uint64_t *clear = (uint64_t *)pctrl->buffer.data; - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; - g_assert(scrambling_key); + g_assert(pctrl->otp_scramble_key); OtPresentState *ps = ot_present_new(); - ot_present_init(ps, scrambling_key); + ot_present_init(ps, pctrl->otp_scramble_key); trace_ot_otp_unscramble_partition(s->ot_id, ot_otp_engine_part_name(s, part_ix), @@ -1133,11 +1132,10 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) * 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[part_ix]; - g_assert(scrambling_key); + 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, scrambling_key); + 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; @@ -1182,11 +1180,12 @@ static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) bool is_zer = ot_otp_engine_is_part_zer_offset(s, part_ix, address); if (is_secret && !is_zer) { - const uint8_t *scrambling_key = s->otp_scramble_keys[part_ix]; + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + g_assert(pctrl->otp_scramble_key); + 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_init(ps, pctrl->otp_scramble_key); ot_present_encrypt(ps, data, &data); lo = (uint32_t)data; hi = (uint32_t)(data >> 32u); @@ -1835,7 +1834,7 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, iv = &s->flash_data_iv; constant = s->flash_data_const; ingest_entropy = false; - return; + break; case OTP_KEY_FLASH_ADDR: if (!ic->has_flash_support) { iv = NULL; @@ -1846,21 +1845,21 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, iv = &s->flash_addr_iv; constant = s->flash_addr_const; ingest_entropy = false; - return; + break; case 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; - return; + break; case 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; - return; + break; default: iv = NULL; ingest_entropy = false; @@ -2527,12 +2526,17 @@ 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; } - if (!s->otp_scramble_key_xstrs[part_ix]) { + /* + * 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, @@ -2541,10 +2545,11 @@ static void ot_otp_engine_configure_part_scramble_keys(OtOTPEngineState *s) ot_otp_engine_part_name(s, part_ix), part_ix); return; } + secret_ix += 1u; continue; } - size_t len = strlen(s->otp_scramble_key_xstrs[part_ix]); + size_t len = strlen(s->otp_scramble_key_xstrs[secret_ix]); if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { error_setg( &error_fatal, @@ -2554,12 +2559,11 @@ static void ot_otp_engine_configure_part_scramble_keys(OtOTPEngineState *s) return; } - g_assert(!s->otp_scramble_keys[part_ix]); + OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; + pctrl->otp_scramble_key = g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - s->otp_scramble_keys[part_ix] = - g_new0(uint8_t, OTP_SCRAMBLING_KEY_BYTES); - if (ot_common_parse_hexa_str(s->otp_scramble_keys[part_ix], - s->otp_scramble_key_xstrs[part_ix], + 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", @@ -2570,8 +2574,10 @@ static void ot_otp_engine_configure_part_scramble_keys(OtOTPEngineState *s) TRACE_OTP("otp_scramble_keys[%s] %s", ot_otp_engine_part_name(s, part_ix), - ot_otp_hexdump(s, s->otp_scramble_keys[part_ix], + ot_otp_hexdump(s, pctrl->otp_scramble_key, OTP_SCRAMBLING_KEY_BYTES)); + + secret_ix += 1u; } } @@ -2579,13 +2585,14 @@ static void ot_otp_engine_add_scramble_key_props(OtOTPEngineState *s) { g_assert(s->part_count); - /* - * @todo we know the number of secret partitions, so use it rather than - * whole partition count - */ - s->otp_scramble_keys = g_new0(uint8_t *, s->part_count); - s->otp_scramble_key_xstrs = g_new0(char *, 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) { @@ -2598,18 +2605,20 @@ static void ot_otp_engine_add_scramble_key_props(OtOTPEngineState *s) * Assumes secret partitions are sequentially ordered and named * SECRET0, SECRET1, SECRET2, ... */ - prop->name = g_strdup_printf("secret%u_scramble_key", secret_ix++); + 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[part_ix] - (intptr_t)s; + (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++; } } @@ -2634,10 +2643,9 @@ static void ot_otp_engine_configure_inv_default_parts(OtOTPEngineState *s) return; } - g_assert(!s->inv_default_parts[part_ix]); - - s->inv_default_parts[part_ix] = g_new0(uint8_t, part->size + 1u); - if (ot_common_parse_hexa_str(s->inv_default_parts[part_ix], + 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, @@ -2648,7 +2656,7 @@ static void ot_otp_engine_configure_inv_default_parts(OtOTPEngineState *s) TRACE_OTP("inv_default_part[%s] %s", ot_otp_engine_part_name(s, part_ix), - ot_otp_hexdump(s, s->inv_default_parts[part_ix], part->size)); + ot_otp_hexdump(s, pctrl->inv_default_data, part->size)); } } @@ -2656,7 +2664,6 @@ static void ot_otp_engine_add_inv_def_props(OtOTPEngineState *s) { g_assert(s->part_count); - s->inv_default_parts = g_new0(uint8_t *, 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++) { diff --git a/include/hw/opentitan/ot_otp_engine.h b/include/hw/opentitan/ot_otp_engine.h index deacfce858a44..d3870dea84827 100644 --- a/include/hw/opentitan/ot_otp_engine.h +++ b/include/hw/opentitan/ot_otp_engine.h @@ -157,6 +157,9 @@ typedef struct { 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 { @@ -237,9 +240,6 @@ struct OtOTPEngineState { 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; /* some entries may be NULL */ - uint8_t **inv_default_parts; /* some entries may be NULL */ OtOTPStorage *otp; OtOTPHWCfg *hw_cfg; From 5aedf4829da2f3b1e78013f994fc214c1f2089b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 3 Nov 2025 14:57:30 +0100 Subject: [PATCH 12/14] [ot] hw/opentitan: ot_otp: make OTP definitions use an OT_OTP prefix Should start with OT_OTP, OtOtp, ot_otp to prevent from unexpected conflicts Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_keymgr.c | 4 +- hw/opentitan/ot_keymgr_dpe.c | 8 +- hw/opentitan/ot_lc_ctrl.c | 4 +- hw/opentitan/ot_otp_dj.c | 48 +++-- hw/opentitan/ot_otp_dj_parts.c | 6 +- hw/opentitan/ot_otp_eg.c | 52 +++-- hw/opentitan/ot_otp_eg_parts.c | 10 +- hw/opentitan/ot_otp_engine.c | 297 ++++++++++++++------------- hw/opentitan/ot_sram_ctrl.c | 2 +- include/hw/opentitan/ot_otp_engine.h | 188 ++++++++--------- include/hw/opentitan/ot_otp_if.h | 30 +-- 11 files changed, 339 insertions(+), 310 deletions(-) diff --git a/hw/opentitan/ot_keymgr.c b/hw/opentitan/ot_keymgr.c index 7eddcf2588416..ee5f3aa87ddcd 100644 --- a/hw/opentitan/ot_keymgr.c +++ b/hw/opentitan/ot_keymgr.c @@ -1196,9 +1196,9 @@ static void ot_keymgr_get_root_key(OtKeyMgrState *s, OtOTPKeyMgrSecret *share0, { OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); - oc->get_keymgr_secret(oi, OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, share0); - oc->get_keymgr_secret(oi, 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_DUMP_CREATOR_ROOT_KEY)) { diff --git a/hw/opentitan/ot_keymgr_dpe.c b/hw/opentitan/ot_keymgr_dpe.c index 780cbb8becf77..9ed3b22f796c0 100644 --- a/hw/opentitan/ot_keymgr_dpe.c +++ b/hw/opentitan/ot_keymgr_dpe.c @@ -1029,7 +1029,7 @@ ot_keymgr_dpe_kdf_append_creator_seed(OtKeyMgrDpeState *s, bool *dvalid) OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); - oc->get_keymgr_secret(oi, OTP_KEYMGR_SECRET_CREATOR_SEED, &secret); + 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, @@ -1109,7 +1109,7 @@ ot_keymgr_dpe_kdf_append_owner_seed(OtKeyMgrDpeState *s, bool *dvalid) OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); - oc->get_keymgr_secret(oi, OTP_KEYMGR_SECRET_OWNER_SEED, &secret); + 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, @@ -1449,9 +1449,9 @@ static void ot_keymgr_dpe_get_root_key( { OtOTPIfClass *oc = OT_OTP_IF_GET_CLASS(s->otp_ctrl); OtOTPIf *oi = OT_OTP_IF(s->otp_ctrl); - oc->get_keymgr_secret(oi, OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, + oc->get_keymgr_secret(oi, OT_OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0, share0); - oc->get_keymgr_secret(oi, 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)) { diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index d2c79edeea2d0..93d8aa8179cd2 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -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) { diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 7e022c0dd8301..fa11aeaf94d43 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -30,6 +30,8 @@ * Based on OpenTitan 5fe6fe8605 */ +#define OT_OTP_COMPORTABLE_REGS + #include "qemu/osdep.h" #include "qemu/log.h" #include "qom/object.h" @@ -401,12 +403,18 @@ REG32(LC_STATE, 16344u) #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)) + #define R_LAST_REG (R_SECRET3_DIGEST_1) #define REGS_COUNT (R_LAST_REG + 1u) #define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") +/* 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, OTP_PART_CREATOR_SW_CFG, @@ -567,11 +575,13 @@ static const char *REG_NAMES[REGS_COUNT] = { }; #undef REG_NAME_ENTRY +#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), + OT_OTP_NAME_ENTRY(TOKEN_TEST_UNLOCK), + OT_OTP_NAME_ENTRY(TOKEN_TEST_EXIT), + OT_OTP_NAME_ENTRY(TOKEN_RMA), /* clang-format on */ }; @@ -753,11 +763,11 @@ static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, s->regs[reg] &= val32; /* RW0C */ break; case R_DIRECT_ACCESS_CMD: - if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { + if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, RD)) { c->dai_read(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, WR)) { c->dai_write(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, DIGEST)) { c->dai_digest(s); } /* @todo implement ZEROIZE command */ @@ -771,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: @@ -966,7 +976,7 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( 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, 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; @@ -978,13 +988,13 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( 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, OTP_ACCESS_ERROR); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); return MEMTX_DECODE_ERROR; } uint32_t val32 = s->otp->data[reg]; - c->set_error(s, part_ix, OTP_NO_ERROR); + c->set_error(s, part_ix, OT_OTP_NO_ERROR); uint64_t pc; @@ -1039,21 +1049,21 @@ static void ot_otp_dj_get_keymgr_secret( size_t offset; switch (type) { - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: + 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 OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: + 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 OTP_KEYMGR_SECRET_CREATOR_SEED: + case OT_OTP_KEYMGR_SECRET_CREATOR_SEED: partition = OTP_PART_SECRET2; offset = A_SECRET2_CREATOR_SEED - s->part_descs[partition].offset; break; - case OTP_KEYMGR_SECRET_OWNER_SEED: + case OT_OTP_KEYMGR_SECRET_OWNER_SEED: partition = OTP_PART_SECRET3; offset = A_SECRET3_OWNER_SEED - s->part_descs[partition].offset; break; @@ -1135,20 +1145,20 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPEngineState *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: + case OT_OTP_TOKEN_TEST_UNLOCK: partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; break; - case OTP_TOKEN_TEST_EXIT: + case OT_OTP_TOKEN_TEST_EXIT: partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_EXIT_TOKEN; break; - case OTP_TOKEN_RMA: + case OT_OTP_TOKEN_RMA: partition = (unsigned)OTP_PART_SECRET2; secret_addr = A_SECRET2_RMA_TOKEN; break; @@ -1299,7 +1309,7 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) ic->has_flash_support = false; ic->has_zer_support = true; - g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_SRAM].size == + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_SRAM].size == SECRET1_SRAM_DATA_KEY_SEED_SIZE); } diff --git a/hw/opentitan/ot_otp_dj_parts.c b/hw/opentitan/ot_otp_dj_parts.c index 217edfb0abdd1..dda4c81752dff 100644 --- a/hw/opentitan/ot_otp_dj_parts.c +++ b/hw/opentitan/ot_otp_dj_parts.c @@ -359,13 +359,13 @@ static const OtOTPPartDesc OT_OTP_PART_DESCS[] = { #define OTP_PART_COUNT ARRAY_SIZE(OT_OTP_PART_DESCS) -static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OTP_KEY_COUNT] = { - [OTP_KEY_OTBN] = { +static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OT_OTP_KEY_COUNT] = { + [OT_OTP_KEY_OTBN] = { .partition = OTP_PART_SECRET1, .offset = 0, .size = 16, }, - [OTP_KEY_SRAM] = { + [OT_OTP_KEY_SRAM] = { .partition = OTP_PART_SECRET1, .offset = 0, .size = 16, diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 7b04e956b2eb1..84a6b68a520aa 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -28,6 +28,8 @@ * THE SOFTWARE. */ +#define OT_OTP_COMPORTABLE_REGS + #include "qemu/osdep.h" #include "qemu/log.h" #include "qom/object.h" @@ -350,12 +352,18 @@ REG32(LC_STATE, 2008u) #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)) + #define R_LAST_REG (R_SECRET2_DIGEST_1) #define REGS_COUNT (R_LAST_REG + 1u) #define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) #define REG_NAME(_reg_) \ ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") +/* 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, OTP_PART_CREATOR_SW_CFG, @@ -466,11 +474,13 @@ static const char *REG_NAMES[REGS_COUNT] = { }; #undef REG_NAME_ENTRY +#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), + OT_OTP_NAME_ENTRY(TOKEN_TEST_UNLOCK), + OT_OTP_NAME_ENTRY(TOKEN_TEST_EXIT), + OT_OTP_NAME_ENTRY(TOKEN_RMA), /* clang-format on */ }; @@ -652,11 +662,11 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, s->regs[reg] &= val32; /* RW0C */ break; case R_DIRECT_ACCESS_CMD: - if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { + if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, RD)) { c->dai_read(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, WR)) { + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, WR)) { c->dai_write(s); - } else if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, DIGEST)) { + } else if (FIELD_EX32(val32, OT_OTP_DIRECT_ACCESS_CMD, DIGEST)) { c->dai_digest(s); } break; @@ -669,7 +679,7 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, s->regs[reg] = val32; break; case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: - val32 &= READ_LOCK_MASK; + val32 &= OT_OTP_READ_LOCK_MASK; s->regs[reg] &= val32; /* RW0C */ break; case R_CHECK_TRIGGER_REGWEN: @@ -862,7 +872,7 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( 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, 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; @@ -874,13 +884,13 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( 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, OTP_ACCESS_ERROR); + c->set_error(s, part_ix, OT_OTP_ACCESS_ERROR); return MEMTX_DECODE_ERROR; } uint32_t val32 = s->otp->data[reg]; - c->set_error(s, part_ix, OTP_NO_ERROR); + c->set_error(s, part_ix, OT_OTP_NO_ERROR); uint64_t pc; @@ -934,18 +944,18 @@ static void ot_otp_eg_get_keymgr_secret( size_t offset; switch (type) { - case OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE0: + 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 OTP_KEYMGR_SECRET_CREATOR_ROOT_KEY_SHARE1: + 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 OTP_KEYMGR_SECRET_CREATOR_SEED: - case OTP_KEYMGR_SECRET_OWNER_SEED: + 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); @@ -1022,20 +1032,20 @@ static void ot_otp_eg_pwr_load_tokens(OtOTPEngineState *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: + case OT_OTP_TOKEN_TEST_UNLOCK: partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_UNLOCK_TOKEN; break; - case OTP_TOKEN_TEST_EXIT: + case OT_OTP_TOKEN_TEST_EXIT: partition = (unsigned)OTP_PART_SECRET0; secret_addr = A_SECRET0_TEST_EXIT_TOKEN; break; - case OTP_TOKEN_RMA: + case OT_OTP_TOKEN_RMA: partition = (unsigned)OTP_PART_SECRET2; secret_addr = A_SECRET2_RMA_TOKEN; break; @@ -1181,11 +1191,11 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) ic->part_descs[part_ix].zer_offset == UINT16_MAX); } - g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_FLASH_ADDR].size == + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_FLASH_ADDR].size == SECRET1_FLASH_ADDR_KEY_SEED_SIZE); - g_assert(OT_OTP_KEY_SEEDS[OTP_KEY_FLASH_DATA].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[OTP_KEY_SRAM].size == + g_assert(OT_OTP_KEY_SEEDS[OT_OTP_KEY_SRAM].size == SECRET1_SRAM_DATA_KEY_SEED_SIZE); } diff --git a/hw/opentitan/ot_otp_eg_parts.c b/hw/opentitan/ot_otp_eg_parts.c index d3069987c7b3a..a8c76dd53897d 100644 --- a/hw/opentitan/ot_otp_eg_parts.c +++ b/hw/opentitan/ot_otp_eg_parts.c @@ -184,23 +184,23 @@ static const OtOTPPartDesc OT_OTP_PART_DESCS[] = { #define OTP_PART_COUNT ARRAY_SIZE(OT_OTP_PART_DESCS) -static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OTP_KEY_COUNT] = { - [OTP_KEY_FLASH_ADDR] = { +static const OtOTPKeySeed OT_OTP_KEY_SEEDS[OT_OTP_KEY_COUNT] = { + [OT_OTP_KEY_FLASH_ADDR] = { .partition = OTP_PART_SECRET1, .offset = 0, .size = 32, }, - [OTP_KEY_FLASH_DATA] = { + [OT_OTP_KEY_FLASH_DATA] = { .partition = OTP_PART_SECRET1, .offset = 32, .size = 32, }, - [OTP_KEY_OTBN] = { + [OT_OTP_KEY_OTBN] = { .partition = OTP_PART_SECRET1, .offset = 64, .size = 16, }, - [OTP_KEY_SRAM] = { + [OT_OTP_KEY_SRAM] = { .partition = OTP_PART_SECRET1, .offset = 64, .size = 16, diff --git a/hw/opentitan/ot_otp_engine.c b/hw/opentitan/ot_otp_engine.c index def3852ccf399..f2d9db69dd853 100644 --- a/hw/opentitan/ot_otp_engine.c +++ b/hw/opentitan/ot_otp_engine.c @@ -28,6 +28,8 @@ * THE SOFTWARE. */ +#define OT_OTP_COMPORTABLE_REGS + #include "qemu/osdep.h" #include "qemu/bswap.h" #include "qemu/log.h" @@ -67,51 +69,53 @@ /* 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 */ - 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), + 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 */ - 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), + 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 */ - 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(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 */ }; @@ -142,7 +146,8 @@ static const char *ERR_CODE_NAMES[] = { #define OTP_ENTRY_COUNT(_s_) ((_s_)->part_count + 2u) #define DIRECT_ACCESS_REG(_s_, _reg_) \ - ((_s_)->regs[(_s_)->reg_offset.dai_base + (unsigned)(DA_REG_##_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_)]) @@ -150,10 +155,10 @@ static const char *ERR_CODE_NAMES[] = { * 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[OTP_KEY_SRAM].size) -#define SRAM_NONCE_BYTES(_ic_) ((_ic_)->key_seeds[OTP_KEY_SRAM].size) -#define OTBN_KEY_BYTES(_ic_) ((_ic_)->key_seeds[OTP_KEY_OTBN].size) -#define OTBN_NONCE_BYTES(_ic_) (((_ic_)->key_seeds[OTP_KEY_OTBN].size) / 2u) +#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) @@ -271,8 +276,8 @@ ot_otp_engine_part_name(const OtOTPEngineState *s, unsigned part_ix) static void ot_otp_engine_disable_all_partitions(OtOTPEngineState *s) { - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); + 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]; @@ -286,7 +291,7 @@ static void ot_otp_engine_set_error(OtOTPEngineState *s, unsigned part_ix, OtOTPImplIfClass *ic = OT_OTP_IMPL_IF_GET_CLASS(s); g_assert(part_ix < OTP_ENTRY_COUNT(s)); - uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; + 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); @@ -294,25 +299,25 @@ static void ot_otp_engine_set_error(OtOTPEngineState *s, unsigned part_ix, ERR_CODE_PART_REG(s, part_ix) = errval; switch (err) { - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: + 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 OTP_MACRO_ECC_CORR_ERROR: + case OT_OTP_MACRO_ECC_CORR_ERROR: /* * "The corresponding controller automatically recovers from this error * when issuing a new command." */ break; - case OTP_MACRO_WRITE_BLANK_ERROR: + case OT_OTP_MACRO_WRITE_BLANK_ERROR: break; - case OTP_ACCESS_ERROR: + case OT_OTP_ACCESS_ERROR: ic->update_status_error(OT_OTP_IMPL_IF(s), OT_OTP_STATUS_DAI, true); break; - case OTP_CHECK_FAIL_ERROR: - case OTP_FSM_STATE_ERROR: + 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; @@ -325,7 +330,7 @@ static void ot_otp_engine_set_error(OtOTPEngineState *s, unsigned part_ix, error_report("%s: %s: OTP disabled on fatal error", __func__, s->ot_id); } - if (err != OTP_NO_ERROR) { + if (err != OT_OTP_NO_ERROR) { s->regs[R_INTR_STATE] |= INTR_OTP_ERROR_MASK; ot_otp_engine_update_irqs(s); } @@ -477,8 +482,8 @@ static uint64_t ot_otp_engine_apply_digest_ecc( 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; + 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 @@ -506,8 +511,8 @@ static int ot_otp_engine_apply_ecc(OtOTPEngineState *s, unsigned part_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) ? OTP_MACRO_ECC_UNCORR_ERROR : - OTP_MACRO_ECC_CORR_ERROR; + 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 @@ -573,7 +578,7 @@ ot_otp_engine_get_part_digest_reg(OtOTPEngineState *s, uint32_t offset) * 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); + 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); @@ -617,7 +622,7 @@ 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], READ_LOCK); + return (bool)SHARED_FIELD_EX32(s->regs[reg], OT_OTP_READ_LOCK); } static bool ot_otp_engine_is_readable(const OtOTPEngineState *s, @@ -739,8 +744,8 @@ static void ot_otp_engine_lc_broadcast_bh(void *opaque) break; case OT_OTP_LC_ESCALATE_EN: if (level) { - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); - LCI_CHANGE_STATE(s, OTP_LCI_ERROR); + 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", @@ -948,7 +953,7 @@ static void ot_otp_engine_check_buffered_partition_integrity( pctrl->failed = true; /* this is a fatal error */ - ot_otp_engine_set_error(s, part_ix, OTP_CHECK_FAIL_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, @@ -980,7 +985,7 @@ static inline int ot_otp_engine_write_backend( static void ot_otp_engine_dai_init(OtOTPEngineState *s) { - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); } static void ot_otp_engine_dai_set_error(OtOTPEngineState *s, OtOTPError err) @@ -988,13 +993,13 @@ 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 OTP_FSM_STATE_ERROR: - case OTP_MACRO_ERROR: - case OTP_MACRO_ECC_UNCORR_ERROR: - DAI_CHANGE_STATE(s, OTP_DAI_ERROR); + 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, OTP_DAI_IDLE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); break; } } @@ -1018,7 +1023,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) ot_otp_engine_dai_clear_error(s); - DAI_CHANGE_STATE(s, OTP_DAI_READ); + DAI_CHANGE_STATE(s, OT_OTP_DAI_READ); unsigned address = DIRECT_ACCESS_REG(s, ADDRESS); @@ -1028,7 +1033,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1038,7 +1043,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1060,7 +1065,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1069,7 +1074,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) bool do_ecc = s->part_descs[part_ix].integrity && ot_otp_engine_is_ecc_enabled(s); - DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); + DAI_CHANGE_STATE(s, OT_OTP_DAI_READ_WAIT); uint32_t data_lo, data_hi; unsigned err = 0; @@ -1146,8 +1151,8 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) DIRECT_ACCESS_REG(s, RDATA_1) = data_hi; if (err) { - OtOTPError otp_err = - (err > 1) ? OTP_MACRO_ECC_UNCORR_ERROR : OTP_MACRO_ECC_CORR_ERROR; + 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; } @@ -1160,7 +1165,7 @@ static void ot_otp_engine_dai_read(OtOTPEngineState *s) timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + access_time); } else { - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); } } @@ -1195,7 +1200,7 @@ static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) 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), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } dst[0u] |= lo; @@ -1207,7 +1212,7 @@ static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) 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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return -1; } @@ -1225,7 +1230,7 @@ static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) "%s: %s: Cannot clear OTP ECC bits\n", __func__, s->ot_id); ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } *edst |= ecc; @@ -1235,7 +1240,7 @@ static int ot_otp_engine_dai_write_u64(OtOTPEngineState *s, unsigned address) sizeof(uint32_t))) { error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_engine_dai_set_error(s, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return -1; } @@ -1259,7 +1264,7 @@ static int ot_otp_engine_dai_write_u32(OtOTPEngineState *s, unsigned address) 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), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } *dst |= data; @@ -1270,7 +1275,7 @@ static int ot_otp_engine_dai_write_u32(OtOTPEngineState *s, unsigned address) 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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return -1; } @@ -1284,7 +1289,7 @@ static int ot_otp_engine_dai_write_u32(OtOTPEngineState *s, unsigned address) "%s: %s: cannot clear OTP ECC bits\n", __func__, s->ot_id); ot_otp_engine_set_error(s, OTP_ENTRY_DAI(s), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } *edst |= ecc; @@ -1294,7 +1299,7 @@ static int ot_otp_engine_dai_write_u32(OtOTPEngineState *s, unsigned address) sizeof(uint16_t))) { error_report("%s: %s: cannot update OTP backend", __func__, s->ot_id); - ot_otp_engine_dai_set_error(s, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return -1; } @@ -1321,11 +1326,11 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) 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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return; } - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE); ot_otp_engine_dai_clear_error(s); @@ -1337,7 +1342,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1348,7 +1353,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1364,7 +1369,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1372,7 +1377,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1387,7 +1392,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) "written\n", __func__, s->ot_id, ot_otp_engine_part_name(s, part_ix), part_ix); - ot_otp_engine_dai_set_error(s, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } } @@ -1416,7 +1421,7 @@ static void ot_otp_engine_dai_write(OtOTPEngineState *s) cell_count += cell_count / 2u; }; - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); + 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; @@ -1436,11 +1441,11 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) 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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return; } - DAI_CHANGE_STATE(s, OTP_DAI_DIG_CLR); + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG_CLR); ot_otp_engine_dai_clear_error(s); @@ -1452,7 +1457,7 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1463,7 +1468,7 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1472,7 +1477,7 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) "%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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1488,7 +1493,7 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } @@ -1496,17 +1501,17 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) 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, OTP_ACCESS_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_ACCESS_ERROR); return; } - DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); + 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, OTP_DAI_DIG); + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG); pctrl->buffer.next_digest = ot_otp_engine_compute_partition_digest(s, data, part_size); @@ -1516,7 +1521,7 @@ static void ot_otp_engine_dai_digest(OtOTPEngineState *s) s->ot_id, pctrl->buffer.next_digest, ot_otp_hexdump(s, data, part_size)); - DAI_CHANGE_STATE(s, OTP_DAI_DIG_WAIT); + DAI_CHANGE_STATE(s, OT_OTP_DAI_DIG_WAIT); /* fake slow update of OTP cell */ timer_mod(s->dai->delay, @@ -1529,7 +1534,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) g_assert((s->dai->partition >= 0) && (s->dai->partition < s->part_count)); - DAI_CHANGE_STATE(s, OTP_DAI_WRITE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE); unsigned part_ix = (unsigned)s->dai->partition; OtOTPPartController *pctrl = &s->part_ctrls[part_ix]; @@ -1543,7 +1548,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) 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), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } *dst |= data; @@ -1552,7 +1557,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) 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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return; } @@ -1567,7 +1572,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) 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), - OTP_MACRO_WRITE_BLANK_ERROR); + OT_OTP_MACRO_WRITE_BLANK_ERROR); } *edst |= ecc; @@ -1576,7 +1581,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) (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, OTP_MACRO_ERROR); + ot_otp_engine_dai_set_error(s, OT_OTP_MACRO_ERROR); return; } @@ -1584,7 +1589,7 @@ static void ot_otp_engine_dai_write_digest(void *opaque) ot_otp_engine_part_name(s, part_ix), part_ix, *dst, *edst); - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); + DAI_CHANGE_STATE(s, OT_OTP_DAI_WRITE_WAIT); /* fake slow update of OTP cell */ unsigned cell_count = sizeof(uint64_t) + sizeof(uint32_t); @@ -1597,7 +1602,7 @@ static void ot_otp_engine_dai_complete(void *opaque) OtOTPEngineState *s = opaque; switch (s->dai->state) { - case OTP_DAI_READ_WAIT: + 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) @@ -1606,19 +1611,19 @@ static void ot_otp_engine_dai_complete(void *opaque) DIRECT_ACCESS_REG(s, RDATA_0), DIRECT_ACCESS_REG(s, RDATA_1)); s->dai->partition = -1; - DAI_CHANGE_STATE(s, OTP_DAI_IDLE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); break; - case OTP_DAI_WRITE_WAIT: + 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, OTP_DAI_IDLE); + DAI_CHANGE_STATE(s, OT_OTP_DAI_IDLE); break; - case OTP_DAI_DIG_WAIT: + case OT_OTP_DAI_DIG_WAIT: g_assert(s->dai->partition >= 0); qemu_bh_schedule(s->dai->digest_bh); break; - case OTP_DAI_ERROR: + case OT_OTP_DAI_ERROR: break; default: g_assert_not_reached(); @@ -1628,7 +1633,7 @@ static void ot_otp_engine_dai_complete(void *opaque) static void ot_otp_engine_lci_init(OtOTPEngineState *s) { - LCI_CHANGE_STATE(s, OTP_LCI_IDLE); + LCI_CHANGE_STATE(s, OT_OTP_LCI_IDLE); } static const OtOTPHWCfg *ot_otp_engine_get_hw_cfg(const OtOTPIf *dev) @@ -1716,7 +1721,7 @@ 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 < OTP_KEY_COUNT); + 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); @@ -1824,7 +1829,7 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, const uint8_t *constant; bool ingest_entropy; switch (type) { - case OTP_KEY_FLASH_DATA: + case OT_OTP_KEY_FLASH_DATA: if (!ic->has_flash_support) { iv = NULL; break; @@ -1835,7 +1840,7 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, constant = s->flash_data_const; ingest_entropy = false; break; - case OTP_KEY_FLASH_ADDR: + case OT_OTP_KEY_FLASH_ADDR: if (!ic->has_flash_support) { iv = NULL; break; @@ -1846,14 +1851,14 @@ static void ot_otp_engine_get_otp_key(OtOTPIf *dev, OtOTPKeyType type, constant = s->flash_addr_const; ingest_entropy = false; break; - case OTP_KEY_OTBN: + 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 OTP_KEY_SRAM: + 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; @@ -1889,16 +1894,16 @@ static bool ot_otp_engine_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, OtOTPLCIController *lci = s->lci; switch (lci->state) { - case OTP_LCI_IDLE: - case OTP_LCI_ERROR: + 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 OTP_LCI_WRITE: - case OTP_LCI_WRITE_WAIT: + case OT_OTP_LCI_WRITE: + case OT_OTP_LCI_WRITE_WAIT: /* another LC programming request is on-going */ return false; - case OTP_LCI_RESET: + case OT_OTP_LCI_RESET: /* cannot reach this point if PwrMgr init has been executed */ default: g_assert_not_reached(); @@ -1908,7 +1913,7 @@ static bool ot_otp_engine_program_req(OtOTPIf *dev, const uint16_t *lc_tcount, lci->ack_fn = ack; lci->ack_data = opaque; - if (lci->state == OTP_LCI_IDLE) { + 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); @@ -1947,9 +1952,9 @@ static void ot_otp_engine_lci_write_complete(OtOTPEngineState *s, bool success) 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 (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)) { @@ -1961,9 +1966,9 @@ static void ot_otp_engine_lci_write_complete(OtOTPEngineState *s, bool success) 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); + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_ERROR; + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); } } } @@ -1976,7 +1981,7 @@ static void ot_otp_engine_lci_write_complete(OtOTPEngineState *s, bool success) lci->ack_data = NULL; lci->hpos = 0u; - if (!success && lci->error != OTP_NO_ERROR) { + if (!success && lci->error != OT_OTP_NO_ERROR) { ot_otp_engine_set_error(s, s->part_lc_num, lci->error); } @@ -1990,8 +1995,8 @@ static void ot_otp_engine_lci_write_word(void *opaque) const OtOTPPartDesc *lcdesc = &s->part_descs[s->part_lc_num]; /* should not be called if already in error */ - if (lci->state == OTP_LCI_ERROR) { - lci->error = OTP_FSM_STATE_ERROR; + if (lci->state == OT_OTP_LCI_ERROR) { + lci->error = OT_OTP_FSM_STATE_ERROR; ot_otp_engine_lci_write_complete(s, false); return; } @@ -2001,8 +2006,8 @@ static void ot_otp_engine_lci_write_word(void *opaque) 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); + 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; @@ -2010,17 +2015,17 @@ static void ot_otp_engine_lci_write_word(void *opaque) 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); + 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, OTP_LCI_ERROR); + LCI_CHANGE_STATE(s, OT_OTP_LCI_ERROR); ot_otp_engine_lci_write_complete(s, false); } return; } - LCI_CHANGE_STATE(s, OTP_LCI_WRITE); + LCI_CHANGE_STATE(s, OT_OTP_LCI_WRITE); uint16_t *lc_dst = (uint16_t *)&s->otp->data[lcdesc->offset / sizeof(uint32_t)]; @@ -2034,8 +2039,8 @@ static void ot_otp_engine_lci_write_word(void *opaque) 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; + 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 @@ -2060,8 +2065,8 @@ static void ot_otp_engine_lci_write_word(void *opaque) 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; + if (lci->error == OT_OTP_NO_ERROR) { + lci->error = OT_OTP_MACRO_WRITE_BLANK_ERROR; } } @@ -2074,7 +2079,7 @@ static void ot_otp_engine_lci_write_word(void *opaque) timer_mod(lci->prog_delay, qemu_clock_get_ns(OT_OTP_HW_CLOCK) + update_time); - LCI_CHANGE_STATE(s, OTP_LCI_WRITE_WAIT); + LCI_CHANGE_STATE(s, OT_OTP_LCI_WRITE_WAIT); } static void ot_otp_engine_pwr_otp_req(void *opaque, int n, int level) @@ -2770,9 +2775,9 @@ static void ot_otp_engine_reset_enter(Object *obj, ResetType type) 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 = OTP_BUF_IDLE; + s->part_ctrls[part_ix].state.b = OT_OTP_BUF_IDLE; } else { - s->part_ctrls[part_ix].state.u = OTP_UNBUF_IDLE; + 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); @@ -2784,8 +2789,8 @@ static void ot_otp_engine_reset_enter(Object *obj, ResetType type) s->part_ctrls[part_ix].write_lock = true; } } - DAI_CHANGE_STATE(s, OTP_DAI_RESET); - LCI_CHANGE_STATE(s, OTP_LCI_RESET); + 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) diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index bff06f3770a7d..d0c99bdd903e8 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -292,7 +292,7 @@ static void ot_sram_ctrl_reseed(OtSramCtrlState *s) qemu_log_mask(LOG_UNIMP, "%s: %s OTP does not support key generation\n", __func__, s->ot_id); } else { - oc->get_otp_key(oi, 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, diff --git a/include/hw/opentitan/ot_otp_engine.h b/include/hw/opentitan/ot_otp_engine.h index d3870dea84827..07ee708ed7a8f 100644 --- a/include/hw/opentitan/ot_otp_engine.h +++ b/include/hw/opentitan/ot_otp_engine.h @@ -45,102 +45,103 @@ #define TYPE_OT_OTP_ENGINE "ot-otp_engine" OBJECT_DECLARE_TYPE(OtOTPEngineState, OtOTPEngineClass, OT_OTP_ENGINE) -#define NUM_IRQS 2u -#define NUM_ALERTS 5u -#define NUM_DAI_WORDS 2u -#define NUM_DIGEST_WORDS 2u -#define NUM_ZER_WORDS 2u +#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 OTP_DIGEST_ADDR_MASK (sizeof(uint64_t) - 1u) -#define OTP_ZER_ADDR_MASK (sizeof(uint64_t) - 1u) +#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 { - 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, - OTP_ERROR_COUNT, + 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 { - OTP_UNBUF_RESET, - OTP_UNBUF_INIT, - OTP_UNBUF_INIT_WAIT, - OTP_UNBUF_IDLE, - OTP_UNBUF_READ, - OTP_UNBUF_READ_WAIT, - OTP_UNBUF_ERROR, + 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 { - 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, + 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 { - 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, + 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 { - OTP_LCI_RESET, - OTP_LCI_IDLE, - OTP_LCI_WRITE, - OTP_LCI_WRITE_WAIT, - OTP_LCI_ERROR, + OT_OTP_LCI_RESET, + OT_OTP_LCI_IDLE, + OT_OTP_LCI_WRITE, + OT_OTP_LCI_WRITE_WAIT, + OT_OTP_LCI_ERROR, } OtOTPLCIState; typedef enum { - DA_REG_REGWEN, - DA_REG_CMD, - DA_REG_ADDRESS, - DA_REG_WDATA_0, - DA_REG_WDATA_1, - DA_REG_RDATA_0, - DA_REG_RDATA_1, + 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 { @@ -213,8 +214,8 @@ struct OtOTPEngineState { SysBusDevice parent_obj; QEMUBH *pwr_otp_bh; - IbexIRQ irqs[NUM_IRQS]; - IbexIRQ alerts[NUM_ALERTS]; + IbexIRQ irqs[OT_OTP_NUM_IRQS]; + IbexIRQ alerts[OT_OTP_NUM_ALERTS]; IbexIRQ pwc_otp_rsp; uint32_t *regs; @@ -287,13 +288,16 @@ struct OtOTPEngineClass { void *opaque); }; +#ifdef OT_OTP_COMPORTABLE_REGS + /* Comportable registers are identical for all OTP variants */ -REG32(INTR_STATE, 0x000u) +REG32(INTR_STATE, 0x00u) 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) +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) @@ -307,21 +311,20 @@ SHARED_FIELD(ALERT_RECOV_PRIM_OTP_ALERT, 4u, 1u) 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(DIRECT_ACCESS_CMD, RD, 0u, 1u) -FIELD(DIRECT_ACCESS_CMD, WR, 1u, 1u) -FIELD(DIRECT_ACCESS_CMD, DIGEST, 2u, 1u) +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(DIRECT_ACCESS_CMD, ZEROIZE, 3u, 1u) +FIELD(OT_OTP_DIRECT_ACCESS_CMD, ZEROIZE, 3u, 1u) -SHARED_FIELD(ERR_CODE, 0u, 3u) -static_assert(ERR_CODE_MASK >= OTP_ERROR_COUNT - 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(READ_LOCK, 0u, 1u) - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) -#define OTP_NAME_ENTRY(_st_) [_st_] = stringify(_st_) +SHARED_FIELD(OT_OTP_READ_LOCK, 0u, 1u) static inline unsigned ot_otp_engine_part_data_offset(const OtOTPEngineState *s, unsigned part_ix) @@ -335,11 +338,11 @@ 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) * NUM_DIGEST_WORDS; + size -= sizeof(uint32_t) * OT_OTP_NUM_DIGEST_WORDS; } if (s->part_descs[part_ix].zeroizable) { - size -= sizeof(uint32_t) * NUM_ZER_WORDS; + size -= sizeof(uint32_t) * OT_OTP_NUM_ZER_WORDS; } return (unsigned)size; @@ -393,7 +396,8 @@ static inline bool ot_otp_engine_is_part_digest_offset( { uint16_t offset = s->part_descs[part_ix].digest_offset; - return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); + return (offset != UINT16_MAX) && + ((addr & ~OT_OTP_DIGEST_ADDR_MASK) == offset); } static inline bool ot_otp_engine_is_part_zer_offset( @@ -401,12 +405,12 @@ static inline bool ot_otp_engine_is_part_zer_offset( { uint16_t offset = s->part_descs[part_ix].zer_offset; - return (offset != UINT16_MAX) && ((addr & ~OTP_ZER_ADDR_MASK) == 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 != OTP_DAI_IDLE; + return s->dai->state != OT_OTP_DAI_IDLE; } #endif /* HW_OPENTITAN_OT_OTP_ENGINE_H */ diff --git a/include/hw/opentitan/ot_otp_if.h b/include/hw/opentitan/ot_otp_if.h index 9e314f0cb3790..237bf4eff877a 100644 --- a/include/hw/opentitan/ot_otp_if.h +++ b/include/hw/opentitan/ot_otp_if.h @@ -78,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 { @@ -90,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 */ @@ -114,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) */ From 4ca737419060c1bd6e5d819aebc3939b1447578d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 3 Nov 2025 14:55:24 +0100 Subject: [PATCH 13/14] [ot] python/qemu: ot.otp.descriptor: make OTP definitions use an OT_OTP prefix Signed-off-by: Emmanuel Blot --- python/qemu/ot/otp/descriptor.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/python/qemu/ot/otp/descriptor.py b/python/qemu/ot/otp/descriptor.py index 1237f5dd8f761..68fc818f2b171 100644 --- a/python/qemu/ot/otp/descriptor.py +++ b/python/qemu/ot/otp/descriptor.py @@ -131,15 +131,19 @@ def save(self, kind: str, hjname: str, scriptname: str, cfp: TextIO) \ key_seeds: list[tuple[str, str, int, int]] = [] for kseed in self._otpmap.key_seeds: if kseed.name == 'SRAM_DATA': - key_seeds.append(('OTP_KEY_OTBN', f'OTP_PART_{kseed.part_name}', + # 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(('OTP_KEY_SRAM', f'OTP_PART_{kseed.part_name}', + key_seeds.append(('OT_OTP_KEY_SRAM', + f'OTP_PART_{kseed.part_name}', kseed.offset, kseed.size)) continue - key_seeds.append((f'OTP_KEY_{kseed.name}', + 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[OTP_KEY_COUNT] = {', + 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) @@ -272,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) From 8bad622cf4b61a6ecc70b821b335278311319741 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Oct 2025 17:14:01 +0100 Subject: [PATCH 14/14] [ot] hw/opentitan: ot_otp: retrieve HW config parameters from buffered data Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 21 +++++++++++++++------ hw/opentitan/ot_otp_eg.c | 18 +++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index fa11aeaf94d43..a9bd0ed352d79 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1118,22 +1118,31 @@ ot_otp_dj_update_status_error(OtOTPImplIf *dev, OtOTPStatus error, bool set) 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; } diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 84a6b68a520aa..97370bb2abc1f 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1007,20 +1007,28 @@ ot_otp_eg_update_status_error(OtOTPImplIf *dev, OtOTPStatus error, bool set) static void ot_otp_eg_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)); /* 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; }