From 2dcc90ea78b420f00aaeef21be970e6643d14934 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 24 Oct 2025 12:35:15 +0200 Subject: [PATCH 1/9] [ot] hw/opentitan: ot_otp: rename/simplify digest ECC check Function naming was a bit confusing, rename it to match similar feature. Remove condition known to be always true. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 15 ++++++--------- hw/opentitan/ot_otp_eg.c | 15 ++++++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 24945f71f893..48d0ab3bd2f0 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1346,18 +1346,15 @@ static uint32_t ot_otp_dj_verify_ecc(const OtOTPDjState *s, uint32_t data, return (data_hi_o << 16u) | data_lo_o; } -static uint64_t ot_otd_dj_verify_digest(OtOTPDjState *s, unsigned partition, - uint64_t digest, uint32_t ecc) +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; - if (ot_otp_dj_is_ecc_enabled(s)) { - 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); - } - + 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) { @@ -1428,7 +1425,7 @@ static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, int part) unsigned ewaddr = waddr >> 1u; g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otd_dj_verify_digest(s, (unsigned)part, digest, ecc); + digest = ot_otp_dj_apply_digest_ecc(s, (unsigned)part, digest, ecc); } return digest; @@ -1735,7 +1732,7 @@ ot_otp_dj_load_partition_digest(OtOTPDjState *s, unsigned partition) unsigned ewaddr = (digoff >> 3u); g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otd_dj_verify_digest(s, partition, digest, ecc); + digest = ot_otp_dj_apply_digest_ecc(s, partition, digest, ecc); } return digest; diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 31245ad3aaaa..35154802670b 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1239,18 +1239,15 @@ static uint32_t ot_otp_eg_verify_ecc(const OtOTPEgState *s, uint32_t data, return (data_hi_o << 16u) | data_lo_o; } -static uint64_t ot_otd_eg_verify_digest(OtOTPEgState *s, unsigned partition, - uint64_t digest, uint32_t ecc) +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; - if (ot_otp_eg_is_ecc_enabled(s)) { - 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); - } - + 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) { @@ -1321,7 +1318,7 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, int part) unsigned ewaddr = waddr >> 1u; g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otd_eg_verify_digest(s, (unsigned)part, digest, ecc); + digest = ot_otp_eg_apply_digest_ecc(s, (unsigned)part, digest, ecc); } return digest; @@ -1564,7 +1561,7 @@ ot_otp_eg_load_partition_digest(OtOTPEgState *s, unsigned partition) unsigned ewaddr = (digoff >> 3u); g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - digest = ot_otd_eg_verify_digest(s, partition, digest, ecc); + digest = ot_otp_eg_apply_digest_ecc(s, partition, digest, ecc); } return digest; From 23943d15e5d9d1d6435c7258ca1bc990a31532c7 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 Oct 2025 08:39:01 +0100 Subject: [PATCH 2/9] [ot] python/qemu: ot.top.descriptor: fix CASE_SUB range Signed-off-by: Emmanuel Blot --- python/qemu/ot/otp/descriptor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qemu/ot/otp/descriptor.py b/python/qemu/ot/otp/descriptor.py index e819758fb0e9..4712a0afc19a 100644 --- a/python/qemu/ot/otp/descriptor.py +++ b/python/qemu/ot/otp/descriptor.py @@ -280,7 +280,7 @@ def _save_qemu(self, topname: Optional[str], hjname: str, scriptname: str, case A_##_reg_: \\ return stringify(_reg_) #define CASE_SUB(_reg_, _sz_) \\ - case A_##_reg_...(A_##_reg_ + (_sz_)): \\ + case A_##_reg_...(A_##_reg_ + (_sz_) - 1u): \\ return stringify(_reg_) #define CASE_REG(_reg_) \\ case A_##_reg_...(A_##_reg_ + 3u): \\ From 49d6d220b0934e2e697981bb45aecf1de654993e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 Oct 2025 19:36:43 +0100 Subject: [PATCH 3/9] [ot] hw/opentitan: ot_otp_eg: replace OT_OTP_PART_DATA_BYTE_{OFFSET,SIZE} macros with ot_otp_eg_part_data_byte_{offset,size} static inline function. This aligns code with Darjeeling version. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_eg.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 35154802670b..0b39d169f60b 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -899,12 +899,6 @@ 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__) -#define OT_OTP_PART_DATA_OFFSET(_pix_) \ - ((unsigned)(OtOTPPartDescs[(_pix_)].offset)) -#define OT_OTP_PART_DATA_BYTE_SIZE(_pix_) \ - ((unsigned)(OtOTPPartDescs[(_pix_)].size - \ - sizeof(uint32_t) * NUM_DIGEST_WORDS)) - #ifdef OT_OTP_DEBUG #define OT_OTP_HEXSTR_SIZE 256u #define TRACE_OTP(msg, ...) qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); @@ -916,6 +910,22 @@ 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) { uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -1580,7 +1590,7 @@ static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) } unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = OT_OTP_PART_DATA_BYTE_SIZE(ix); + unsigned part_size = ot_otp_eg_part_data_byte_size(ix); const uint8_t *base = (const uint8_t *)s->otp->data; base += offset; @@ -1600,7 +1610,7 @@ static void ot_otp_eg_check_partition_integrity(OtOTPEgState *s, unsigned ix) pctrl->locked = true; - unsigned part_size = OT_OTP_PART_DATA_BYTE_SIZE(ix); + unsigned part_size = ot_otp_eg_part_data_byte_size(ix); uint64_t digest = ot_otp_eg_compute_partition_digest(s, (const uint8_t *)pctrl->buffer.data, @@ -2104,11 +2114,11 @@ static void ot_otp_eg_dai_digest(OtOTPEgState *s) if (OtOTPPartDescs[partition].buffered) { data = ((const uint8_t *)s->otp->data) + - OT_OTP_PART_DATA_OFFSET(partition); + ot_otp_eg_part_data_offset(partition); } else { data = (const uint8_t *)pctrl->buffer.data; } - unsigned part_size = OT_OTP_PART_DATA_BYTE_SIZE(partition); + unsigned part_size = ot_otp_eg_part_data_byte_size(partition); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -3961,7 +3971,7 @@ static void ot_otp_eg_reset_enter(Object *obj, ResetType type) s->partctrls[ix].state.u = OTP_UNBUF_IDLE; continue; } - unsigned part_size = OT_OTP_PART_DATA_BYTE_SIZE(ix); + unsigned part_size = ot_otp_eg_part_data_byte_size(ix); memset(s->partctrls[ix].buffer.data, 0, part_size); s->partctrls[ix].buffer.digest = 0; if (OtOTPPartDescs[ix].iskeymgr_creator || @@ -4079,7 +4089,8 @@ static void ot_otp_eg_init(Object *obj) if (!OtOTPPartDescs[ix].buffered) { continue; } - size_t part_words = OT_OTP_PART_DATA_BYTE_SIZE(ix) / sizeof(uint32_t); + 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); } From cb3b99b1f5b50001c97f0dd47a634bff73cdbd0b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 Oct 2025 19:37:18 +0100 Subject: [PATCH 4/9] [ot] hw/opentitan: ot_otp_eg: update OTP to match Darjeeling new implementation - Partially generated with autoreg.py and otptool.py Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 23 +- hw/opentitan/ot_otp_eg.c | 677 +++++++++++++++++---------------------- 2 files changed, 315 insertions(+), 385 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 48d0ab3bd2f0..3d304eaa3af3 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -6,6 +6,7 @@ * 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 @@ -968,8 +969,6 @@ static const char *ERR_CODE_NAMES[] = { /* clang-format on */ }; -/* clang-format on */ - #undef OTP_NAME_ENTRY #define BUF_STATE_NAME(_st_) \ @@ -1116,6 +1115,15 @@ static bool ot_otp_dj_is_buffered(int partition) return false; } +static bool ot_otp_dj_is_secret(int partition) +{ + if (partition >= 0 && partition < OTP_PART_COUNT) { + return OtOTPPartDescs[partition].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); @@ -1475,7 +1483,7 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *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 = (int)(offset / NUM_DIGEST_WORDS); + 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); @@ -1523,6 +1531,8 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) static bool ot_otp_dj_is_readable(OtOTPDjState *s, int partition) { + g_assert(partition < OTP_PART_COUNT); + if (OtOTPPartDescs[partition].secret) { /* secret partitions are only readable if digest is not yet set. */ return ot_otp_dj_get_buffered_part_digest(s, partition) == 0u; @@ -1558,7 +1568,7 @@ static bool ot_otp_dj_is_readable(OtOTPDjState *s, int partition) /* * If the previous loop reached the last partition, something - * seriously wrong occured. Use this feature as a sanity check + * seriously wrong occurred. Use this feature as a sanity check */ g_assert(pix < OTP_PART_LIFE_CYCLE); @@ -1886,7 +1896,7 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) bool is_zer = ot_otp_dj_is_part_zer_offset(partition, address); bool is_readable = ot_otp_dj_is_readable(s, partition); bool is_wide = ot_otp_dj_is_wide_granule(partition, address); - bool is_secret = OtOTPPartDescs[partition].secret; + bool is_secret = ot_otp_dj_is_secret(partition); /* "in all partitions, the digest itself is ALWAYS readable." */ if (!is_digest && !is_zer && !is_readable) { @@ -2600,7 +2610,7 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) case A_##_reg_: \ return stringify(_reg_) #define CASE_SUB(_reg_, _sz_) \ - case A_##_reg_...(A_##_reg_ + (_sz_)): \ + case A_##_reg_...(A_##_reg_ + (_sz_) - 1u): \ return stringify(_reg_) #define CASE_REG(_reg_) \ case A_##_reg_...(A_##_reg_ + 3u): \ @@ -2738,7 +2748,6 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) #undef CASE_SUB #undef CASE_REG #undef CASE_RANGE -#undef CASE_SUB_WORD #undef CASE_DIGEST } diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 0b39d169f60b..f4d1643a5e26 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -7,6 +7,7 @@ * 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 @@ -56,24 +57,25 @@ #define NUM_IRQS 2u #define NUM_ALERTS 5u -#define NUM_SRAM_KEY_REQ_SLOTS 4u -#define NUM_ERROR_ENTRIES 13u /* Partitions + DAI/LCI */ #define NUM_DAI_WORDS 2u #define NUM_DIGEST_WORDS 2u -#define NUM_SW_CFG_WINDOW_WORDS 512u +#define NUM_ERROR_ENTRIES 13u #define NUM_PART 11u -#define NUM_PART_UNBUF 5u #define NUM_PART_BUF 6u -#define OTP_BYTE_ADDR_WIDTH 11u +#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, 0x0u) +REG32(INTR_STATE, 0x00u) SHARED_FIELD(INTR_OTP_OPERATION_DONE, 0u, 1u) SHARED_FIELD(INTR_OTP_ERROR, 1u, 1u) -REG32(INTR_ENABLE, 0x4u) -REG32(INTR_TEST, 0x8u) -REG32(ALERT_TEST, 0xcu) +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) @@ -387,17 +389,23 @@ REG32(LC_STATE, 2008u) #define LC_TRANSITION_CNT_SIZE 48u #define LC_STATE_SIZE 40u -#define INTR_MASK (INTR_OTP_OPERATION_DONE_MASK | INTR_OTP_ERROR_MASK) -#define ALERT_TEST_MASK \ +#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 DAI_DELAY_NS 100000u /* 100us */ - #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) #define R_LAST_REG (R_SECRET2_DIGEST_1) @@ -470,8 +478,8 @@ typedef enum { OTP_PART_SECRET1, OTP_PART_SECRET2, OTP_PART_LIFE_CYCLE, - OTP_PART_COUNT, - OTP_ENTRY_DAI = OTP_PART_COUNT, /* Fake partitions for error (... )*/ + OTP_PART_OTP_COUNT, /* count of "real" OTP partitions */ + OTP_ENTRY_DAI = OTP_PART_OTP_COUNT, /* Fake partitions for error (...) */ OTP_ENTRY_KDI, /* Key derivation issue, not really OTP */ OTP_ENTRY_COUNT, } OtOTPPartitionType; @@ -575,12 +583,8 @@ typedef struct { /* NOLINTNEXTLINE */ #include "ot_otp_eg_parts.c" -static_assert(OTP_PART_COUNT == NUM_PART, "Invalid partition definitions"); -static_assert(OTP_PART_COUNT == OTP_PART_COUNT, - "Invalid partition definitions"); -static_assert(NUM_PART_UNBUF + NUM_PART_BUF == NUM_PART, "Invalid partitions"); -static_assert(NUM_ERROR_ENTRIES == OTP_ENTRY_COUNT, "Invalid entries"); -static_assert(NUM_PART <= 64, "Maximum part count reached"); +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) @@ -998,6 +1002,15 @@ static bool ot_otp_eg_is_buffered(int partition) return false; } +static bool ot_otp_eg_is_secret(int partition) +{ + if (partition >= 0 && partition < OTP_PART_COUNT) { + return OtOTPPartDescs[partition].secret; + } + + return false; +} + static bool ot_otp_eg_is_backend_ecc_enabled(const OtOTPEgState *s) { OtOtpBeIfClass *bec = OT_OTP_BE_IF_GET_CLASS(s->otp_backend); @@ -1033,8 +1046,7 @@ static void ot_otp_eg_disable_all_partitions(OtOTPEgState *s) static void ot_otp_eg_set_error(OtOTPEgState *s, unsigned part, OtOTPError err) { - /* This is in NUM_ERROR_ENTRIES */ - g_assert(part < NUM_ERROR_ENTRIES); + g_assert(part < OTP_ENTRY_COUNT); uint32_t errval = ((uint32_t)err) & ERR_CODE_MASK; if (errval || errval != s->regs[R_ERR_CODE_0 + part]) { @@ -1087,7 +1099,7 @@ static uint32_t ot_otp_eg_dai_is_busy(const OtOTPEgState *s) return s->dai->state != OTP_DAI_IDLE; } -static uint32_t ot_otp_eg_get_status(OtOTPEgState *s) +static uint32_t ot_otp_eg_get_status(const OtOTPEgState *s) { uint32_t status; @@ -1112,26 +1124,6 @@ static int ot_otp_eg_get_part_from_address(const OtOTPEgState *s, hwaddr addr) return -1; } -static uint16_t ot_otp_eg_get_part_digest_offset(int part) -{ - switch (part) { - case OTP_PART_VENDOR_TEST: - case OTP_PART_CREATOR_SW_CFG: - case OTP_PART_OWNER_SW_CFG: - case OTP_PART_ROT_CREATOR_AUTH_CODESIGN: - case OTP_PART_ROT_CREATOR_AUTH_STATE: - case OTP_PART_HW_CFG0: - case OTP_PART_HW_CFG1: - case OTP_PART_SECRET0: - case OTP_PART_SECRET1: - case OTP_PART_SECRET2: - case OTP_PART_LIFE_CYCLE: - return OtOTPPartDescs[part].digest_offset; - default: - return UINT16_MAX; - } -} - static uint8_t ot_otp_eg_compute_ecc_u16(uint16_t data) { uint32_t data_o = (uint32_t)data; @@ -1314,7 +1306,7 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, int part) { g_assert(!ot_otp_eg_is_buffered(part)); - uint16_t offset = ot_otp_eg_get_part_digest_offset(part); + uint16_t offset = OtOTPPartDescs[part].digest_offset; if (offset == UINT16_MAX) { return 0u; @@ -1345,69 +1337,128 @@ static uint64_t ot_otp_eg_get_buffered_part_digest(OtOTPEgState *s, int part) static bool ot_otp_eg_is_part_digest_offset(int part, hwaddr addr) { - uint16_t offset = ot_otp_eg_get_part_digest_offset(part); + uint16_t offset = OtOTPPartDescs[part].digest_offset; return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); } +static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) +{ + /* + * Offset is the register offset from the first read 32-bit digest register. + * All digests are 64-bits, which means each partition have two 32-bit + * registers to expose their digest. + * + * Not all partitions have digests, which means the offset argument is the + * relative partition offset for those partitions that features a digest, as + * there is no defined digest access registers defined for partitions that + * do not have a digest. + * + * Need to traverse the partition table to only account for those partitions + * with a digest to match the proper offset. + */ + + /* + * part_look_ix is the index of the partition in the contiguous array of + * digest registers, which would be equivalent as the index of the partition + * that would exist in a virtual partition-with-digest array. + */ + unsigned part_look_ix = (unsigned)(offset / NUM_DIGEST_WORDS); + /* whether to retrieve the top most 32-bit of the digest or not */ + bool hi = (bool)(offset & 0x1u); + + /* part_ix: the partition number in the global partition array */ + unsigned part_ix = 0; + /* traverse the partition array and count each partition with a digest */ + for (unsigned part_dig_ix = 0; part_ix < OTP_PART_COUNT; part_ix++) { + if (ot_otp_eg_has_digest(part_ix)) { + /* + * stop searching if we've found the part-with-digest defined from + * the offset argument. Otherwise, increment the part-with-digest + * index and continue. + */ + if (part_dig_ix == part_look_ix) { + break; + } + part_dig_ix++; + } + } + + /* + * If the part_ix as reached the latest partition, there is something wrong + * with the partition table or the register definitions, as it is assumed + * that LifeCycle partition is the last partition. + */ + static_assert(OTP_PART_LIFE_CYCLE == NUM_PART - 1, + "LC is expected to be the last partition"); + g_assert(part_ix < OTP_PART_COUNT); + + const OtOTPPartDesc *part = &OtOTPPartDescs[part_ix]; + uint64_t digest; + + if (part->buffered) { + digest = ot_otp_eg_get_buffered_part_digest(s, (int)part_ix); + } else { + digest = ot_otp_eg_get_part_digest(s, (int)part_ix); + } + + if (hi) { + digest >>= 32u; + } + + return (uint32_t)digest; +} + static bool ot_otp_eg_is_readable(OtOTPEgState *s, int partition) { + g_assert(partition < OTP_PART_COUNT); + if (OtOTPPartDescs[partition].secret) { /* secret partitions are only readable if digest is not yet set. */ return ot_otp_eg_get_buffered_part_digest(s, partition) == 0u; } - uint32_t reg; + if (!OtOTPPartDescs[partition].read_lock_csr) { + if (!OtOTPPartDescs[partition].read_lock) { + /* read lock is not supported for this partition */ + return true; + } - switch (partition) { - case OTP_PART_VENDOR_TEST: - reg = R_VENDOR_TEST_READ_LOCK; - break; - case OTP_PART_CREATOR_SW_CFG: - reg = R_CREATOR_SW_CFG_READ_LOCK; - break; - case OTP_PART_OWNER_SW_CFG: - reg = R_OWNER_SW_CFG_READ_LOCK; - break; - case OTP_PART_ROT_CREATOR_AUTH_CODESIGN: - reg = R_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK; - break; - case OTP_PART_ROT_CREATOR_AUTH_STATE: - reg = R_ROT_CREATOR_AUTH_STATE_READ_LOCK; - break; - case OTP_PART_HW_CFG0: - case OTP_PART_HW_CFG1: - case OTP_PART_SECRET0: - case OTP_PART_SECRET1: - case OTP_PART_SECRET2: - reg = UINT32_MAX; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid partition: %d\n", - __func__, s->ot_id, partition); - return false; + /* hw read lock, not locked */ + return !s->partctrls[partition].read_lock; } - if (OtOTPPartDescs[partition].read_lock_csr) { - if (reg == UINT32_MAX) { - error_setg(&error_fatal, "CSR register not defined"); - g_assert_not_reached(); + unsigned roffset = 0; + unsigned pix; + for (pix = 0; pix < OTP_PART_COUNT; pix++) { + if (pix == partition) { + break; + } + if (OtOTPPartDescs[partition].read_lock_csr) { + roffset++; } - return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); } + /* + * know for sure last partition is the life cycle one, which never + * support a read_lock_csr. Ideally this g_assert should be a + * static_assert, but C being C, constants are not defined as such + * at build time... + */ + g_assert(!OtOTPPartDescs[OTP_PART_LIFE_CYCLE].read_lock_csr); - if (reg != UINT32_MAX) { - error_setg(&error_fatal, "Unexpected CSR register"); - g_assert_not_reached(); - } + /* + * 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); - if (!OtOTPPartDescs[partition].read_lock) { - /* read lock is not supported for this partition */ - return true; - } + /* + * 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; - /* hw read lock, not locked */ - return !s->partctrls[partition].read_lock; + return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); } static void @@ -1724,10 +1775,13 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) bool is_digest = ot_otp_eg_is_part_digest_offset(partition, address); bool is_readable = ot_otp_eg_is_readable(s, partition); bool is_wide = ot_otp_eg_is_wide_granule(partition, address); - bool is_secret = OtOTPPartDescs[partition].secret; + bool is_secret = ot_otp_eg_is_secret(partition); /* "in all partitions, the digest itself is ALWAYS readable." */ if (!is_digest && !is_readable) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: partition %s @ 0x%04x not readable\n", __func__, + s->ot_id, PART_NAME(partition), address); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2248,29 +2302,13 @@ static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) switch (reg) { case R_INTR_STATE: case R_INTR_ENABLE: - case R_ERR_CODE_0: - case R_ERR_CODE_1: - case R_ERR_CODE_2: - case R_ERR_CODE_3: - case R_ERR_CODE_4: - case R_ERR_CODE_5: - case R_ERR_CODE_6: - case R_ERR_CODE_7: - case R_ERR_CODE_8: - case R_ERR_CODE_9: - case R_ERR_CODE_10: - case R_ERR_CODE_11: - case R_ERR_CODE_12: + 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: - case R_CREATOR_SW_CFG_READ_LOCK: - case R_OWNER_SW_CFG_READ_LOCK: - case R_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK: - case R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: val32 = s->regs[reg]; break; case R_STATUS: @@ -2293,97 +2331,12 @@ static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) /* TODO: not yet implemented */ val32 = 0; break; - /* in all partitions, the digest itself is ALWAYS readable."*/ - case R_VENDOR_TEST_DIGEST_0: - val32 = (uint32_t)ot_otp_eg_get_part_digest(s, OTP_PART_VENDOR_TEST); - break; - case R_VENDOR_TEST_DIGEST_1: - val32 = (uint32_t)(ot_otp_eg_get_part_digest(s, OTP_PART_VENDOR_TEST) >> - 32u); - break; - case R_CREATOR_SW_CFG_DIGEST_0: - val32 = (uint32_t)ot_otp_eg_get_part_digest(s, OTP_PART_CREATOR_SW_CFG); - break; - case R_CREATOR_SW_CFG_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_part_digest(s, OTP_PART_CREATOR_SW_CFG) >> - 32u); - break; - case R_OWNER_SW_CFG_DIGEST_0: - val32 = (uint32_t)ot_otp_eg_get_part_digest(s, OTP_PART_OWNER_SW_CFG); - break; - case R_OWNER_SW_CFG_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_part_digest(s, OTP_PART_OWNER_SW_CFG) >> - 32u); - break; - case R_ROT_CREATOR_AUTH_CODESIGN_DIGEST_0: - val32 = (uint32_t) - ot_otp_eg_get_part_digest(s, OTP_PART_ROT_CREATOR_AUTH_CODESIGN); - break; - case R_ROT_CREATOR_AUTH_CODESIGN_DIGEST_1: - val32 = (uint32_t)(ot_otp_eg_get_part_digest( - s, OTP_PART_ROT_CREATOR_AUTH_CODESIGN) >> - 32u); - break; - case R_ROT_CREATOR_AUTH_STATE_DIGEST_0: - val32 = (uint32_t) - ot_otp_eg_get_part_digest(s, OTP_PART_ROT_CREATOR_AUTH_STATE); - break; - case R_ROT_CREATOR_AUTH_STATE_DIGEST_1: - val32 = (uint32_t)(ot_otp_eg_get_part_digest( - s, OTP_PART_ROT_CREATOR_AUTH_STATE) >> - 32u); - break; - case R_HW_CFG0_DIGEST_0: - val32 = - (uint32_t)ot_otp_eg_get_buffered_part_digest(s, OTP_PART_HW_CFG0); - break; - case R_HW_CFG0_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_buffered_part_digest(s, - OTP_PART_HW_CFG0) >> - 32u); - break; - case R_HW_CFG1_DIGEST_0: - val32 = - (uint32_t)ot_otp_eg_get_buffered_part_digest(s, OTP_PART_HW_CFG1); - break; - case R_HW_CFG1_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_buffered_part_digest(s, - OTP_PART_HW_CFG1) >> - 32u); - break; - case R_SECRET0_DIGEST_0: - val32 = - (uint32_t)ot_otp_eg_get_buffered_part_digest(s, OTP_PART_SECRET0); - break; - case R_SECRET0_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_buffered_part_digest(s, - OTP_PART_SECRET0) >> - 32u); - break; - case R_SECRET1_DIGEST_0: - val32 = - (uint32_t)ot_otp_eg_get_buffered_part_digest(s, OTP_PART_SECRET1); - break; - case R_SECRET1_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_buffered_part_digest(s, - OTP_PART_SECRET1) >> - 32u); - break; - case R_SECRET2_DIGEST_0: - val32 = - (uint32_t)ot_otp_eg_get_buffered_part_digest(s, OTP_PART_SECRET2); - break; - case R_SECRET2_DIGEST_1: - val32 = - (uint32_t)(ot_otp_eg_get_buffered_part_digest(s, - OTP_PART_SECRET2) >> - 32u); + 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: @@ -2426,11 +2379,7 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, case R_DIRECT_ACCESS_ADDRESS: case R_DIRECT_ACCESS_WDATA_0: case R_DIRECT_ACCESS_WDATA_1: - case R_VENDOR_TEST_READ_LOCK: - case R_CREATOR_SW_CFG_READ_LOCK: - case R_OWNER_SW_CFG_READ_LOCK: - case R_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK: - case R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + 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, @@ -2462,42 +2411,11 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, } break; case R_STATUS: - case R_ERR_CODE_0: - case R_ERR_CODE_1: - case R_ERR_CODE_2: - case R_ERR_CODE_3: - case R_ERR_CODE_4: - case R_ERR_CODE_5: - case R_ERR_CODE_6: - case R_ERR_CODE_7: - case R_ERR_CODE_8: - case R_ERR_CODE_9: - case R_ERR_CODE_10: - case R_ERR_CODE_11: - case R_ERR_CODE_12: + case R_ERR_CODE_0 ... R_ERR_CODE_12: case R_DIRECT_ACCESS_REGWEN: case R_DIRECT_ACCESS_RDATA_0: case R_DIRECT_ACCESS_RDATA_1: - case R_VENDOR_TEST_DIGEST_0: - case R_VENDOR_TEST_DIGEST_1: - case R_CREATOR_SW_CFG_DIGEST_0: - case R_CREATOR_SW_CFG_DIGEST_1: - case R_OWNER_SW_CFG_DIGEST_0: - case R_OWNER_SW_CFG_DIGEST_1: - case R_ROT_CREATOR_AUTH_CODESIGN_DIGEST_0: - case R_ROT_CREATOR_AUTH_CODESIGN_DIGEST_1: - case R_ROT_CREATOR_AUTH_STATE_DIGEST_0: - case R_ROT_CREATOR_AUTH_STATE_DIGEST_1: - case R_HW_CFG0_DIGEST_0: - case R_HW_CFG0_DIGEST_1: - case R_HW_CFG1_DIGEST_0: - case R_HW_CFG1_DIGEST_1: - case R_SECRET0_DIGEST_0: - case R_SECRET0_DIGEST_1: - case R_SECRET1_DIGEST_0: - case R_SECRET1_DIGEST_1: - case R_SECRET2_DIGEST_0: - case R_SECRET2_DIGEST_1: + case R_VENDOR_TEST_DIGEST_0 ... R_SECRET2_DIGEST_1: qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: R/O register 0x%03" HWADDR_PRIx " (%s)\n", __func__, s->ot_id, addr, REG_NAME(reg)); @@ -2508,22 +2426,22 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, switch (reg) { case R_INTR_STATE: - val32 &= INTR_MASK; + val32 &= INTR_WMASK; s->regs[R_INTR_STATE] &= ~val32; /* RW1C */ ot_otp_eg_update_irqs(s); break; case R_INTR_ENABLE: - val32 &= INTR_MASK; + val32 &= INTR_WMASK; s->regs[R_INTR_ENABLE] = val32; ot_otp_eg_update_irqs(s); break; case R_INTR_TEST: - val32 &= INTR_MASK; + val32 &= INTR_WMASK; s->regs[R_INTR_STATE] = val32; ot_otp_eg_update_irqs(s); break; case R_ALERT_TEST: - val32 &= ALERT_TEST_MASK; + val32 &= ALERT_WMASK; s->regs[reg] = val32; ot_otp_eg_update_alerts(s); break; @@ -2544,11 +2462,7 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, case R_DIRECT_ACCESS_WDATA_1: s->regs[reg] = val32; break; - case R_VENDOR_TEST_READ_LOCK: - case R_CREATOR_SW_CFG_READ_LOCK: - case R_OWNER_SW_CFG_READ_LOCK: - case R_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK: - case R_ROT_CREATOR_AUTH_STATE_READ_LOCK: + case R_VENDOR_TEST_READ_LOCK ... R_ROT_CREATOR_AUTH_STATE_READ_LOCK: val32 &= READ_LOCK_MASK; s->regs[reg] &= val32; /* RW0C */ break; @@ -2571,140 +2485,146 @@ static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, static const char *ot_otp_eg_swcfg_reg_name(unsigned swreg) { -#define CASE_SCALAR(_reg_) \ +#define CASE_BYTE(_reg_) \ + case A_##_reg_: \ + return stringify(_reg_) +#define CASE_SUB(_reg_, _sz_) \ + case A_##_reg_...(A_##_reg_ + (_sz_) - 1u): \ + return stringify(_reg_) +#define CASE_REG(_reg_) \ case A_##_reg_...(A_##_reg_ + 3u): \ return stringify(_reg_) +#define CASE_WIDE(_reg_) \ + case A_##_reg_...(A_##_reg_ + 7u): \ + return stringify(_reg_) #define CASE_RANGE(_reg_) \ case A_##_reg_...(A_##_reg_ + (_reg_##_SIZE) - 1u): \ return stringify(_reg_) -#define CASE_SUB_WORD CASE_RANGE -#define CASE_DIGEST(_reg_) \ - case A_##_reg_...(A_##_reg_ + 7u): \ - return stringify(_reg_) switch (swreg) { CASE_RANGE(VENDOR_TEST_SCRATCH); - CASE_DIGEST(VENDOR_TEST_DIGEST); + CASE_WIDE(VENDOR_TEST_DIGEST); CASE_RANGE(CREATOR_SW_CFG_AST_CFG); - CASE_SCALAR(CREATOR_SW_CFG_AST_INIT_EN); - CASE_SCALAR(CREATOR_SW_CFG_ROM_EXT_SKU); - CASE_SCALAR(CREATOR_SW_CFG_SIGVERIFY_SPX_EN); - CASE_SCALAR(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG); - CASE_SCALAR(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG); - CASE_SCALAR(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE); - CASE_SCALAR(CREATOR_SW_CFG_RNG_EN); - CASE_SCALAR(CREATOR_SW_CFG_JITTER_EN); - CASE_SCALAR(CREATOR_SW_CFG_RET_RAM_RESET_MASK); - CASE_SCALAR(CREATOR_SW_CFG_MANUF_STATE); - CASE_SCALAR(CREATOR_SW_CFG_ROM_EXEC_EN); - CASE_SCALAR(CREATOR_SW_CFG_CPUCTRL); - CASE_SCALAR(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT); - CASE_SCALAR(CREATOR_SW_CFG_MIN_SEC_VER_BL0); - CASE_SCALAR(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN); - CASE_SCALAR(CREATOR_SW_CFG_RMA_SPIN_EN); - CASE_SCALAR(CREATOR_SW_CFG_RMA_SPIN_CYCLES); - CASE_SCALAR(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS); - CASE_SCALAR(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD); - CASE_SCALAR(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST); - CASE_SCALAR(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN); - CASE_SCALAR(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_EN); - CASE_SCALAR(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_START_OFFSET); - CASE_SCALAR(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_LENGTH); + CASE_REG(CREATOR_SW_CFG_AST_INIT_EN); + CASE_REG(CREATOR_SW_CFG_ROM_EXT_SKU); + CASE_REG(CREATOR_SW_CFG_SIGVERIFY_SPX_EN); + CASE_REG(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG); + CASE_REG(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG); + CASE_REG(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE); + CASE_REG(CREATOR_SW_CFG_RNG_EN); + CASE_REG(CREATOR_SW_CFG_JITTER_EN); + CASE_REG(CREATOR_SW_CFG_RET_RAM_RESET_MASK); + CASE_REG(CREATOR_SW_CFG_MANUF_STATE); + CASE_REG(CREATOR_SW_CFG_ROM_EXEC_EN); + CASE_REG(CREATOR_SW_CFG_CPUCTRL); + CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT); + CASE_REG(CREATOR_SW_CFG_MIN_SEC_VER_BL0); + CASE_REG(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN); + CASE_REG(CREATOR_SW_CFG_RMA_SPIN_EN); + CASE_REG(CREATOR_SW_CFG_RMA_SPIN_CYCLES); + CASE_REG(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS); + CASE_REG(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD); + CASE_REG(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST); + CASE_REG(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_EN); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_START_OFFSET); + CASE_REG(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_LENGTH); CASE_RANGE(CREATOR_SW_CFG_IMMUTABLE_ROM_EXT_SHA256_HASH); CASE_RANGE(CREATOR_SW_CFG_RESERVED); - CASE_DIGEST(CREATOR_SW_CFG_DIGEST); - CASE_SCALAR(OWNER_SW_CFG_ROM_ERROR_REPORTING); - CASE_SCALAR(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_CLASS_EN); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_ESCALATION); + CASE_WIDE(CREATOR_SW_CFG_DIGEST); + CASE_REG(OWNER_SW_CFG_ROM_ERROR_REPORTING); + CASE_REG(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_CLASS_EN); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_ESCALATION); CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION); CASE_RANGE(OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION); CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH); CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES); CASE_RANGE(OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV); - CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA); - CASE_SCALAR(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES); - CASE_SCALAR(OWNER_SW_CFG_ROM_KEYMGR_OTP_MEAS_EN); - CASE_SCALAR(OWNER_SW_CFG_MANUF_STATE); - CASE_SCALAR(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN); - CASE_SCALAR(OWNER_SW_CFG_ROM_EXT_BOOTSTRAP_EN); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV); + CASE_REG(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA); + CASE_REG(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES); + CASE_REG(OWNER_SW_CFG_ROM_KEYMGR_OTP_MEAS_EN); + CASE_REG(OWNER_SW_CFG_MANUF_STATE); + CASE_REG(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN); + CASE_REG(OWNER_SW_CFG_ROM_EXT_BOOTSTRAP_EN); CASE_RANGE(OWNER_SW_CFG_ROM_SENSOR_CTRL_ALERT_CFG); - CASE_SCALAR(OWNER_SW_CFG_ROM_SRAM_READBACK_EN); - CASE_SCALAR(OWNER_SW_CFG_ROM_PRESERVE_RESET_REASON_EN); - CASE_SCALAR(OWNER_SW_CFG_ROM_RESET_REASON_CHECK_VALUE); - CASE_SCALAR(OWNER_SW_CFG_ROM_BANNER_EN); - CASE_SCALAR(OWNER_SW_CFG_ROM_FLASH_ECC_EXC_HANDLER_EN); + CASE_REG(OWNER_SW_CFG_ROM_SRAM_READBACK_EN); + CASE_REG(OWNER_SW_CFG_ROM_PRESERVE_RESET_REASON_EN); + CASE_REG(OWNER_SW_CFG_ROM_RESET_REASON_CHECK_VALUE); + CASE_REG(OWNER_SW_CFG_ROM_BANNER_EN); + CASE_REG(OWNER_SW_CFG_ROM_FLASH_ECC_EXC_HANDLER_EN); CASE_RANGE(OWNER_SW_CFG_RESERVED); - CASE_DIGEST(OWNER_SW_CFG_DIGEST); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE0); + CASE_WIDE(OWNER_SW_CFG_DIGEST); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE0); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY0); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE1); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY1); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE2); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY2); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE3); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY_TYPE3); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_ECDSA_KEY3); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE0); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE0); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY0); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG0); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG0); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE1); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY1); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG1); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG1); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE2); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY2); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG2); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE3); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG2); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_TYPE3); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY3); - CASE_SCALAR(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG3); + CASE_REG(ROT_CREATOR_AUTH_CODESIGN_SPX_KEY_CONFIG3); CASE_RANGE(ROT_CREATOR_AUTH_CODESIGN_BLOCK_SHA2_256_HASH); - CASE_DIGEST(ROT_CREATOR_AUTH_CODESIGN_DIGEST); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_ECDSA_KEY0); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_ECDSA_KEY1); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_ECDSA_KEY2); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_ECDSA_KEY3); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_SPX_KEY0); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_SPX_KEY1); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_SPX_KEY2); - CASE_SCALAR(ROT_CREATOR_AUTH_STATE_SPX_KEY3); - CASE_DIGEST(ROT_CREATOR_AUTH_STATE_DIGEST); + CASE_WIDE(ROT_CREATOR_AUTH_CODESIGN_DIGEST); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY0); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY1); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY2); + CASE_REG(ROT_CREATOR_AUTH_STATE_ECDSA_KEY3); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY0); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY1); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY2); + CASE_REG(ROT_CREATOR_AUTH_STATE_SPX_KEY3); + CASE_WIDE(ROT_CREATOR_AUTH_STATE_DIGEST); CASE_RANGE(HW_CFG0_DEVICE_ID); CASE_RANGE(HW_CFG0_MANUF_STATE); - CASE_DIGEST(HW_CFG0_DIGEST); - CASE_SUB_WORD(HW_CFG1_EN_SRAM_IFETCH); - CASE_SUB_WORD(HW_CFG1_EN_CSRNG_SW_APP_READ); - CASE_SUB_WORD(HW_CFG1_DIS_RV_DM_LATE_DEBUG); - CASE_DIGEST(HW_CFG1_DIGEST); + CASE_WIDE(HW_CFG0_DIGEST); + CASE_BYTE(HW_CFG1_EN_SRAM_IFETCH); + CASE_BYTE(HW_CFG1_EN_CSRNG_SW_APP_READ); + CASE_BYTE(HW_CFG1_DIS_RV_DM_LATE_DEBUG); + CASE_WIDE(HW_CFG1_DIGEST); CASE_RANGE(SECRET0_TEST_UNLOCK_TOKEN); CASE_RANGE(SECRET0_TEST_EXIT_TOKEN); - CASE_DIGEST(SECRET0_DIGEST); + CASE_WIDE(SECRET0_DIGEST); CASE_RANGE(SECRET1_FLASH_ADDR_KEY_SEED); CASE_RANGE(SECRET1_FLASH_DATA_KEY_SEED); CASE_RANGE(SECRET1_SRAM_DATA_KEY_SEED); - CASE_DIGEST(SECRET1_DIGEST); + CASE_WIDE(SECRET1_DIGEST); CASE_RANGE(SECRET2_RMA_TOKEN); CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE0); CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE1); - CASE_DIGEST(SECRET2_DIGEST); + CASE_WIDE(SECRET2_DIGEST); CASE_RANGE(LC_TRANSITION_CNT); CASE_RANGE(LC_STATE); default: return ""; } -#undef CASE_SCALAR +#undef CASE_BYTE +#undef CASE_SUB +#undef CASE_REG #undef CASE_RANGE -#undef CASE_SUB_WORD #undef CASE_DIGEST } @@ -2763,8 +2683,8 @@ static void ot_otp_eg_get_lc_info( const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) { - const OtOTPEgState *ds = OT_OTP_EG(s); - const OtOTPStorage *otp = ds->otp; + const OtOTPEgState *es = OT_OTP_EG(s); + const OtOTPStorage *otp = es->otp; if (lc_tcount) { memcpy(lc_tcount, &otp->data[R_LC_TRANSITION_CNT], @@ -2776,35 +2696,35 @@ static void ot_otp_eg_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 = !(es->partctrls[OTP_PART_SECRET0].failed || + es->partctrls[OTP_PART_SECRET2].failed || + es->partctrls[OTP_PART_LIFE_CYCLE].failed) ? OT_MULTIBITBOOL_LC4_TRUE : OT_MULTIBITBOOL_LC4_FALSE; } if (secret_valid) { - *secret_valid = (!ds->partctrls[OTP_PART_SECRET2].failed && - ds->partctrls[OTP_PART_SECRET2].locked) ? + *secret_valid = (!es->partctrls[OTP_PART_SECRET2].failed && + es->partctrls[OTP_PART_SECRET2].locked) ? OT_MULTIBITBOOL_LC4_TRUE : OT_MULTIBITBOOL_LC4_FALSE; } if (tokens) { - *tokens = ds->tokens; + *tokens = es->tokens; } } static const OtOTPHWCfg *ot_otp_eg_get_hw_cfg(const OtOTPState *s) { - const OtOTPEgState *ds = OT_OTP_EG(s); + const OtOTPEgState *es = OT_OTP_EG(s); - return ds->hw_cfg; + return es->hw_cfg; } static const OtOTPEntropyCfg *ot_otp_eg_get_entropy_cfg(const OtOTPState *s) { - const OtOTPEgState *ds = OT_OTP_EG(s); + const OtOTPEgState *es = OT_OTP_EG(s); - return ds->entropy_cfg; + return es->entropy_cfg; } static void ot_otp_eg_request_entropy_bh(void *opaque) @@ -2971,60 +2891,60 @@ static void ot_otp_eg_generate_scrambling_key( static void ot_otp_eg_get_otp_key(OtOTPState *s, OtOTPKeyType type, OtOTPKey *key) { - OtOTPEgState *ds = OT_OTP_EG(s); + OtOTPEgState *es = OT_OTP_EG(s); hwaddr key_offset; - trace_ot_otp_get_otp_key(ds->ot_id, type); + 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, ds->scrmbl_key_init->key, FLASH_KEY_BYTES); - memcpy(key->nonce, ds->scrmbl_key_init->nonce, FLASH_NONCE_BYTES); + 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(ds, key, type, key_offset, - ds->flash_data_iv, - ds->flash_data_const, true, false); + 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, ds->scrmbl_key_init->key, FLASH_KEY_BYTES); + 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(ds, key, type, key_offset, - ds->flash_addr_iv, - ds->flash_addr_const, true, false); + 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, ds->scrmbl_key_init->key, OTBN_KEY_BYTES); - memcpy(key->nonce, ds->scrmbl_key_init->nonce, OTBN_NONCE_BYTES); + 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(ds, key, type, key_offset, - ds->sram_iv, ds->sram_const, true, + 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, ds->scrmbl_key_init->key, SRAM_KEY_BYTES); - memcpy(key->nonce, ds->scrmbl_key_init->nonce, SRAM_NONCE_BYTES); + 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(ds, key, type, key_offset, - ds->sram_iv, ds->sram_const, true, + 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__, ds->ot_id, + error_report("%s: %s: invalid OTP key type: %d", __func__, es->ot_id, type); break; } @@ -3033,7 +2953,7 @@ 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) { - OtOTPEgState *ds = OT_OTP_EG(s); + OtOTPEgState *es = OT_OTP_EG(s); int partition; size_t offset; @@ -3052,7 +2972,7 @@ static void ot_otp_eg_get_keymgr_secret( case OTP_KEYMGR_SECRET_OWNER_SEED: default: error_report("%s: %s: invalid OTP keymgr secret type: %d", __func__, - ds->ot_id, type); + es->ot_id, type); secret->valid = false; memset(secret->secret, 0, OT_OTP_KEYMGR_SECRET_SIZE); return; @@ -3061,14 +2981,14 @@ static void ot_otp_eg_get_keymgr_secret( g_assert(ot_otp_eg_is_buffered(partition)); 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[partition].buffer.data; + if (es->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { + data_ptr = (const uint8_t *)es->partctrls[partition].buffer.data; } else { /* source data from PartInvDefault instead of real buffer */ - data_ptr = ds->inv_default_parts[partition]; + data_ptr = es->inv_default_parts[partition]; } - secret->valid = ot_otp_eg_get_buffered_part_digest(ds, partition) != 0; + secret->valid = ot_otp_eg_get_buffered_part_digest(es, partition) != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3076,8 +2996,8 @@ static bool ot_otp_eg_program_req(OtOTPState *s, const uint16_t *lc_tcount, const uint16_t *lc_state, ot_otp_program_ack_fn ack, void *opaque) { - OtOTPEgState *ds = OT_OTP_EG(s); - OtOTPLCIController *lci = ds->lci; + OtOTPEgState *es = OT_OTP_EG(s); + OtOTPLCIController *lci = es->lci; switch (lci->state) { case OTP_LCI_IDLE: @@ -3353,8 +3273,9 @@ static void ot_otp_eg_pwr_load(OtOTPEgState *s) 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: %d", - s->ot_id, rc); + "%s: failed to read the initial OTP content %zu bytes: " + "%d", + s->ot_id, otp_size, rc); return; } From a8aab5c3fca22bea6e365df90cd89cb257cb0de4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 Oct 2025 11:26:24 +0100 Subject: [PATCH 5/9] [ot] hw/opentitan: ot_otp: cleanup partition index management Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 196 ++++++++++++++++++++++----------------- hw/opentitan/ot_otp_eg.c | 190 +++++++++++++++++++++---------------- 2 files changed, 224 insertions(+), 162 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 3d304eaa3af3..303ca064880a 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1090,14 +1090,14 @@ static void ot_otp_dj_update_alerts(OtOTPDjState *s) } } -static bool ot_otp_dj_is_wide_granule(int partition, unsigned address) +static bool ot_otp_dj_is_wide_granule(unsigned part_ix, unsigned address) { - if ((unsigned)partition < OTP_PART_COUNT) { - if (OtOTPPartDescs[partition].secret) { + if (part_ix < OTP_PART_COUNT) { + if (OtOTPPartDescs[part_ix].secret) { return true; } - if (OtOTPPartDescs[partition].digest_offset == + if (OtOTPPartDescs[part_ix].digest_offset == (address & OTP_DIGEST_ADDR_MASK)) { return true; } @@ -1106,19 +1106,19 @@ static bool ot_otp_dj_is_wide_granule(int partition, unsigned address) return false; } -static bool ot_otp_dj_is_buffered(int partition) +static bool ot_otp_dj_is_buffered(unsigned part_ix) { - if (partition >= 0 && partition < OTP_PART_COUNT) { - return OtOTPPartDescs[partition].buffered; + if (part_ix < OTP_PART_COUNT) { + return OtOTPPartDescs[part_ix].buffered; } return false; } -static bool ot_otp_dj_is_secret(int partition) +static bool ot_otp_dj_is_secret(unsigned part_ix) { - if (partition >= 0 && partition < OTP_PART_COUNT) { - return OtOTPPartDescs[partition].secret; + if (part_ix < OTP_PART_COUNT) { + return OtOTPPartDescs[part_ix].secret; } return false; @@ -1378,16 +1378,15 @@ static uint64_t ot_otp_dj_apply_digest_ecc(OtOTPDjState *s, unsigned partition, return digest; } -static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned partition) +static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned part_ix) { g_assert(ot_otp_dj_is_ecc_enabled(s)); - unsigned start = OtOTPPartDescs[partition].offset >> 2u; + unsigned start = OtOTPPartDescs[part_ix].offset >> 2u; unsigned end = - (ot_otp_dj_is_buffered((int)partition) && - ot_otp_dj_has_digest(partition)) ? - (unsigned)(OtOTPPartDescs[partition].digest_offset >> 2u) : - start + (unsigned)(OtOTPPartDescs[partition].size >> 2u); + (ot_otp_dj_is_buffered((int)part_ix) && ot_otp_dj_has_digest(part_ix)) ? + (unsigned)(OtOTPPartDescs[part_ix].digest_offset >> 2u) : + start + (unsigned)(OtOTPPartDescs[part_ix].size >> 2u); g_assert(start < end && (end / sizeof(uint32_t)) < s->otp->data_size); for (unsigned ix = start; ix < end; ix++) { @@ -1402,11 +1401,11 @@ static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned partition) * Note: need to check if any caller could override the error/state * in this case */ - ot_otp_dj_set_error(s, partition, otp_err); + ot_otp_dj_set_error(s, part_ix, otp_err); if (err > 1) { - trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(partition), - partition, ix << 2u, *word, ecc); - s->partctrls[partition].failed = true; + trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(part_ix), + part_ix, ix << 2u, *word, ecc); + s->partctrls[part_ix].failed = true; return -1; } } @@ -1415,11 +1414,11 @@ static int ot_otp_dj_apply_ecc(OtOTPDjState *s, unsigned partition) return 0; } -static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, int part) +static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) { - g_assert(!ot_otp_dj_is_buffered(part)); + g_assert(!ot_otp_dj_is_buffered(part_ix)); - uint16_t offset = OtOTPPartDescs[part].digest_offset; + uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; if (offset == UINT16_MAX) { return 0u; @@ -1428,36 +1427,37 @@ static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, int part) const uint8_t *data = (const uint8_t *)s->otp->data; uint64_t digest = ldq_le_p(data + offset); - if (part != OTP_PART_VENDOR_TEST && ot_otp_dj_is_ecc_enabled(s)) { + if (part_ix != OTP_PART_VENDOR_TEST && 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, (unsigned)part, digest, ecc); + digest = ot_otp_dj_apply_digest_ecc(s, part_ix, digest, ecc); } return digest; } -static uint64_t ot_otp_dj_get_buffered_part_digest(OtOTPDjState *s, int part) +static uint64_t +ot_otp_dj_get_buffered_part_digest(OtOTPDjState *s, unsigned part_ix) { - g_assert(ot_otp_dj_is_buffered(part)); + g_assert(ot_otp_dj_is_buffered(part_ix)); - OtOTPPartController *pctrl = &s->partctrls[part]; + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; return pctrl->buffer.digest; } -static bool ot_otp_dj_is_part_digest_offset(int part, hwaddr addr) +static bool ot_otp_dj_is_part_digest_offset(unsigned part_ix, hwaddr addr) { - uint16_t offset = OtOTPPartDescs[part].digest_offset; + 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(int part, hwaddr addr) +static bool ot_otp_dj_is_part_zer_offset(unsigned part_ix, hwaddr addr) { - uint16_t offset = OtOTPPartDescs[part].zer_offset; + uint16_t offset = OtOTPPartDescs[part_ix].zer_offset; return (offset != UINT16_MAX) && ((addr & ~OTP_ZER_ADDR_MASK) == offset); } @@ -1517,9 +1517,9 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) uint64_t digest; if (part->buffered) { - digest = ot_otp_dj_get_buffered_part_digest(s, (int)part_ix); + digest = ot_otp_dj_get_buffered_part_digest(s, part_ix); } else { - digest = ot_otp_dj_get_part_digest(s, (int)part_ix); + digest = ot_otp_dj_get_part_digest(s, part_ix); } if (hi) { @@ -1529,32 +1529,35 @@ 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, int partition) +static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) { - g_assert(partition < OTP_PART_COUNT); + g_assert(part_ix < OTP_PART_COUNT); + + const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - if (OtOTPPartDescs[partition].secret) { + if (pdesc->secret) { /* secret partitions are only readable if digest is not yet set. */ - return ot_otp_dj_get_buffered_part_digest(s, partition) == 0u; + return ot_otp_dj_get_buffered_part_digest(s, part_ix) == 0u; } - if (!OtOTPPartDescs[partition].read_lock_csr) { - if (!OtOTPPartDescs[partition].read_lock) { + 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 !s->partctrls[partition].read_lock; + return !pctrl->read_lock; } unsigned roffset = 0; unsigned pix; for (pix = 0; pix < OTP_PART_COUNT; pix++) { - if (pix == partition) { + if (pix == part_ix) { break; } - if (OtOTPPartDescs[partition].read_lock_csr) { + if (pdesc->read_lock_csr) { roffset++; } } @@ -1892,11 +1895,13 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) return; } - bool is_digest = ot_otp_dj_is_part_digest_offset(partition, address); - bool is_zer = ot_otp_dj_is_part_zer_offset(partition, address); - bool is_readable = ot_otp_dj_is_readable(s, partition); - bool is_wide = ot_otp_dj_is_wide_granule(partition, address); - bool is_secret = ot_otp_dj_is_secret(partition); + unsigned part_ix = (unsigned)partition; + bool is_readable = ot_otp_dj_is_readable(s, part_ix); + bool is_wide = ot_otp_dj_is_wide_granule(part_ix, address); + bool is_buffered = ot_otp_dj_is_buffered(part_ix); + bool is_secret = ot_otp_dj_is_secret(part_ix); + bool is_digest = ot_otp_dj_is_part_digest_offset(part_ix, address); + bool is_zer = ot_otp_dj_is_part_zer_offset(part_ix, address); /* "in all partitions, the digest itself is ALWAYS readable." */ if (!is_digest && !is_zer && !is_readable) { @@ -1907,7 +1912,8 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) return; } - unsigned waddr = address >> 2u; + unsigned part_offset = address - ot_otp_dj_part_data_offset(partition); + unsigned part_waddr = part_offset >> 2u; bool do_ecc = (partition != OTP_PART_VENDOR_TEST) && ot_otp_dj_is_ecc_enabled(s); @@ -1917,13 +1923,22 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) unsigned err = 0; unsigned cell_count = sizeof(uint32_t) + (do_ecc ? sizeof(uint16_t) : 0); + const uint32_t *data = (const uint32_t *)s->otp->data; + /* parenthesis inform the C linter sizeof() call is valid with 'data */ + data += (ot_otp_dj_part_data_offset(partition) / sizeof(uint32_t)); + if (is_wide || is_digest) { - waddr &= ~0b1u; - data_lo = s->otp->data[waddr]; - data_hi = s->otp->data[waddr + 1u]; + /* 64-bit requests */ + part_waddr &= ~0b1u; + + g_assert((part_waddr + 1u) * sizeof(uint32_t) < + OtOTPPartDescs[partition].size); + + data_lo = data[part_waddr]; + data_hi = data[part_waddr + 1u]; if (do_ecc) { - unsigned ewaddr = waddr >> 1u; + 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)) { @@ -1934,14 +1949,18 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) cell_count *= 2u; } else { - data_lo = s->otp->data[waddr]; + /* 32-bit request */ + g_assert(part_waddr * sizeof(uint32_t) < + OtOTPPartDescs[partition].size); + + data_lo = data[part_waddr]; data_hi = 0u; if (do_ecc) { - unsigned ewaddr = waddr >> 1u; + unsigned ewaddr = address >> 3u; g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - if (waddr & 1u) { + if ((address >> 2u) & 1u) { ecc >>= 16u; } if (ot_otp_dj_is_ecc_enabled(s)) { @@ -1977,7 +1996,7 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) s->dai->partition = partition; - if (!ot_otp_dj_is_buffered(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, @@ -2135,7 +2154,9 @@ static void ot_otp_dj_dai_write(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", @@ -2144,17 +2165,17 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) return; } - OtOTPPartController *pctrl = &s->partctrls[partition]; + OtOTPPartController *pctrl = &s->partctrls[part_ix]; if (pctrl->failed) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); + __func__, s->ot_id, PART_NAME(part_ix)); return; } if (pctrl->locked) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(partition), partition); + __func__, s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2162,13 +2183,13 @@ 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(partition), partition); + s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } - bool is_digest = ot_otp_dj_is_part_digest_offset(partition, address); - bool is_wide = ot_otp_dj_is_wide_granule(partition, address); + bool is_digest = ot_otp_dj_is_part_digest_offset(part_ix, address); + bool is_wide = ot_otp_dj_is_wide_granule(part_ix, address); if (is_digest) { if (OtOTPPartDescs[partition].hw_digest) { @@ -2177,7 +2198,7 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) LOG_GUEST_ERROR, "%s: %s: partition %s (%u) HW digest cannot be directly " "written\n", - __func__, s->ot_id, PART_NAME(partition), partition); + __func__, s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_dj_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2756,7 +2777,6 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( { OtOTPDjState *s = OT_OTP_DJ(opaque); (void)attrs; - uint32_t val32; g_assert(addr + size <= SW_CFG_WINDOW_SIZE); @@ -2765,32 +2785,34 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( if (partition < 0) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "invalid"); - val32 = 0; + *data = 0ull; + + return MEMTX_DECODE_ERROR; } - if (ot_otp_dj_is_buffered(partition)) { + unsigned part_ix = (unsigned)partition; + + if (ot_otp_dj_is_buffered(part_ix)) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "buffered"); - ot_otp_dj_set_error(s, (unsigned)partition, OTP_ACCESS_ERROR); + ot_otp_dj_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_digest = ot_otp_dj_is_part_digest_offset(partition, addr); - bool is_zer = ot_otp_dj_is_part_zer_offset(partition, addr); - bool is_readable = ot_otp_dj_is_readable(s, partition); + 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); - if (!is_digest && !is_zer && !is_readable) { + 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, (unsigned)partition, OTP_ACCESS_ERROR); + ot_otp_dj_set_error(s, part_ix, OTP_ACCESS_ERROR); return MEMTX_DECODE_ERROR; } - if (!(partition < 0)) { - val32 = s->otp->data[reg]; - ot_otp_dj_set_error(s, (unsigned)partition, OTP_NO_ERROR); - } + uint32_t val32 = s->otp->data[reg]; + ot_otp_dj_set_error(s, part_ix, OTP_NO_ERROR); uint64_t pc; @@ -3093,17 +3115,18 @@ static void ot_otp_dj_get_keymgr_secret( return; } - g_assert(ot_otp_dj_is_buffered(partition)); + unsigned part_ix = (unsigned)partition; + g_assert(ot_otp_dj_is_buffered(part_ix)); const uint8_t *data_ptr; if (ds->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { - data_ptr = (const uint8_t *)ds->partctrls[partition].buffer.data; + data_ptr = (const uint8_t *)ds->partctrls[part_ix].buffer.data; } else { /* source data from PartInvDefault instead of real buffer */ - data_ptr = ds->inv_default_parts[partition]; + data_ptr = ds->inv_default_parts[part_ix]; } - secret->valid = ot_otp_dj_get_buffered_part_digest(ds, partition) != 0; + secret->valid = ot_otp_dj_get_buffered_part_digest(ds, part_ix) != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3520,7 +3543,7 @@ static void ot_otp_dj_pwr_initialize_partitions(OtOTPDjState *s) } if (OtOTPPartDescs[ix].sw_digest) { - uint64_t digest = ot_otp_dj_get_part_digest(s, (int)ix); + uint64_t digest = ot_otp_dj_get_part_digest(s, ix); s->partctrls[ix].locked = digest != 0; continue; } @@ -3549,16 +3572,23 @@ static void ot_otp_dj_pwr_otp_bh(void *opaque) */ trace_ot_otp_pwr_otp_req(s->ot_id, "initialize"); + /* 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); } diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index f4d1643a5e26..3375cafc27c2 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -484,6 +484,8 @@ typedef enum { OTP_ENTRY_COUNT, } OtOTPPartitionType; +static_assert(OTP_PART_OTP_COUNT == NUM_PART, "Invalid partition count"); + /* Error code (compliant with ERR_CODE registers) */ typedef enum { OTP_NO_ERROR, @@ -977,14 +979,14 @@ static void ot_otp_eg_update_alerts(OtOTPEgState *s) } } -static bool ot_otp_eg_is_wide_granule(int partition, unsigned address) +static bool ot_otp_eg_is_wide_granule(unsigned part_ix, unsigned address) { - if ((unsigned)partition < OTP_PART_COUNT) { - if (OtOTPPartDescs[partition].secret) { + if (part_ix < OTP_PART_COUNT) { + if (OtOTPPartDescs[part_ix].secret) { return true; } - if (OtOTPPartDescs[partition].digest_offset == + if (OtOTPPartDescs[part_ix].digest_offset == (address & OTP_DIGEST_ADDR_MASK)) { return true; } @@ -993,19 +995,19 @@ static bool ot_otp_eg_is_wide_granule(int partition, unsigned address) return false; } -static bool ot_otp_eg_is_buffered(int partition) +static bool ot_otp_eg_is_buffered(unsigned part_ix) { - if (partition >= 0 && partition < OTP_PART_COUNT) { - return OtOTPPartDescs[partition].buffered; + if (part_ix < OTP_PART_COUNT) { + return OtOTPPartDescs[part_ix].buffered; } return false; } -static bool ot_otp_eg_is_secret(int partition) +static bool ot_otp_eg_is_secret(unsigned part_ix) { - if (partition >= 0 && partition < OTP_PART_COUNT) { - return OtOTPPartDescs[partition].secret; + if (part_ix < OTP_PART_COUNT) { + return OtOTPPartDescs[part_ix].secret; } return false; @@ -1265,16 +1267,15 @@ static uint64_t ot_otp_eg_apply_digest_ecc(OtOTPEgState *s, unsigned partition, return digest; } -static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned partition) +static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned part_ix) { g_assert(ot_otp_eg_is_ecc_enabled(s)); - unsigned start = OtOTPPartDescs[partition].offset >> 2u; + unsigned start = OtOTPPartDescs[part_ix].offset >> 2u; unsigned end = - (ot_otp_eg_is_buffered((int)partition) && - ot_otp_eg_has_digest(partition)) ? - (unsigned)(OtOTPPartDescs[partition].digest_offset >> 2u) : - start + (unsigned)(OtOTPPartDescs[partition].size >> 2u); + (ot_otp_eg_is_buffered((int)part_ix) && ot_otp_eg_has_digest(part_ix)) ? + (unsigned)(OtOTPPartDescs[part_ix].digest_offset >> 2u) : + start + (unsigned)(OtOTPPartDescs[part_ix].size >> 2u); g_assert(start < end && (end / sizeof(uint32_t)) < s->otp->data_size); for (unsigned ix = start; ix < end; ix++) { @@ -1289,11 +1290,11 @@ static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned partition) * Note: need to check if any caller could override the error/state * in this case */ - ot_otp_eg_set_error(s, partition, otp_err); + ot_otp_eg_set_error(s, part_ix, otp_err); if (err > 1) { - trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(partition), - partition, ix << 2u, *word, ecc); - s->partctrls[partition].failed = true; + trace_ot_otp_ecc_init_error(s->ot_id, PART_NAME(part_ix), + part_ix, ix << 2u, *word, ecc); + s->partctrls[part_ix].failed = true; return -1; } } @@ -1302,11 +1303,11 @@ static int ot_otp_eg_apply_ecc(OtOTPEgState *s, unsigned partition) return 0; } -static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, int part) +static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) { - g_assert(!ot_otp_eg_is_buffered(part)); + g_assert(!ot_otp_eg_is_buffered(part_ix)); - uint16_t offset = OtOTPPartDescs[part].digest_offset; + uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; if (offset == UINT16_MAX) { return 0u; @@ -1315,29 +1316,30 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, int part) const uint8_t *data = (const uint8_t *)s->otp->data; uint64_t digest = ldq_le_p(data + offset); - if (part != OTP_PART_VENDOR_TEST && ot_otp_eg_is_ecc_enabled(s)) { + if (part_ix != OTP_PART_VENDOR_TEST && 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, (unsigned)part, digest, ecc); + digest = ot_otp_eg_apply_digest_ecc(s, part_ix, digest, ecc); } return digest; } -static uint64_t ot_otp_eg_get_buffered_part_digest(OtOTPEgState *s, int part) +static uint64_t +ot_otp_eg_get_buffered_part_digest(OtOTPEgState *s, unsigned part_ix) { - g_assert(ot_otp_eg_is_buffered(part)); + g_assert(ot_otp_eg_is_buffered(part_ix)); - OtOTPPartController *pctrl = &s->partctrls[part]; + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; return pctrl->buffer.digest; } -static bool ot_otp_eg_is_part_digest_offset(int part, hwaddr addr) +static bool ot_otp_eg_is_part_digest_offset(unsigned part_ix, hwaddr addr) { - uint16_t offset = OtOTPPartDescs[part].digest_offset; + uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; return (offset != UINT16_MAX) && ((addr & ~OTP_DIGEST_ADDR_MASK) == offset); } @@ -1397,9 +1399,9 @@ static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) uint64_t digest; if (part->buffered) { - digest = ot_otp_eg_get_buffered_part_digest(s, (int)part_ix); + digest = ot_otp_eg_get_buffered_part_digest(s, part_ix); } else { - digest = ot_otp_eg_get_part_digest(s, (int)part_ix); + digest = ot_otp_eg_get_part_digest(s, part_ix); } if (hi) { @@ -1409,32 +1411,35 @@ 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, int partition) +static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) { - g_assert(partition < OTP_PART_COUNT); + g_assert(part_ix < OTP_PART_COUNT); - if (OtOTPPartDescs[partition].secret) { + const OtOTPPartDesc *pdesc = &OtOTPPartDescs[part_ix]; + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + + if (pdesc->secret) { /* secret partitions are only readable if digest is not yet set. */ - return ot_otp_eg_get_buffered_part_digest(s, partition) == 0u; + return ot_otp_eg_get_buffered_part_digest(s, part_ix) == 0u; } - if (!OtOTPPartDescs[partition].read_lock_csr) { - if (!OtOTPPartDescs[partition].read_lock) { + 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 !s->partctrls[partition].read_lock; + return !pctrl->read_lock; } unsigned roffset = 0; unsigned pix; for (pix = 0; pix < OTP_PART_COUNT; pix++) { - if (pix == partition) { + if (pix == part_ix) { break; } - if (OtOTPPartDescs[partition].read_lock_csr) { + if (pdesc->read_lock_csr) { roffset++; } } @@ -1772,10 +1777,12 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) return; } - bool is_digest = ot_otp_eg_is_part_digest_offset(partition, address); - bool is_readable = ot_otp_eg_is_readable(s, partition); - bool is_wide = ot_otp_eg_is_wide_granule(partition, address); - bool is_secret = ot_otp_eg_is_secret(partition); + unsigned part_ix = (unsigned)partition; + bool is_readable = ot_otp_eg_is_readable(s, part_ix); + bool is_wide = ot_otp_eg_is_wide_granule(part_ix, address); + bool is_buffered = ot_otp_eg_is_buffered(part_ix); + bool is_secret = ot_otp_eg_is_secret(part_ix); + bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, address); /* "in all partitions, the digest itself is ALWAYS readable." */ if (!is_digest && !is_readable) { @@ -1786,7 +1793,8 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) return; } - unsigned waddr = address >> 2u; + unsigned part_offset = address - ot_otp_eg_part_data_offset(partition); + unsigned part_waddr = part_offset >> 2u; bool do_ecc = (partition != OTP_PART_VENDOR_TEST) && ot_otp_eg_is_ecc_enabled(s); @@ -1796,13 +1804,22 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) unsigned err = 0; unsigned cell_count = sizeof(uint32_t) + (do_ecc ? sizeof(uint16_t) : 0); + const uint32_t *data = (const uint32_t *)s->otp->data; + /* parenthesis inform the C linter sizeof() call is valid with 'data */ + data += (ot_otp_eg_part_data_offset(partition) / sizeof(uint32_t)); + if (is_wide || is_digest) { - waddr &= ~0b1u; - data_lo = s->otp->data[waddr]; - data_hi = s->otp->data[waddr + 1u]; + /* 64-bit requests */ + part_waddr &= ~0b1u; + + g_assert((part_waddr + 1u) * sizeof(uint32_t) < + OtOTPPartDescs[partition].size); + + data_lo = data[part_waddr]; + data_hi = data[part_waddr + 1u]; if (do_ecc) { - unsigned ewaddr = waddr >> 1u; + 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)) { @@ -1813,14 +1830,18 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) cell_count *= 2u; } else { - data_lo = s->otp->data[waddr]; + /* 32-bit request */ + g_assert(part_waddr * sizeof(uint32_t) < + OtOTPPartDescs[partition].size); + + data_lo = data[part_waddr]; data_hi = 0u; if (do_ecc) { - unsigned ewaddr = waddr >> 1u; + unsigned ewaddr = address >> 3u; g_assert(ewaddr < s->otp->ecc_size); uint32_t ecc = s->otp->ecc[ewaddr]; - if (waddr & 1u) { + if ((address >> 2u) & 1u) { ecc >>= 16u; } if (ot_otp_eg_is_ecc_enabled(s)) { @@ -1856,7 +1877,7 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) s->dai->partition = partition; - if (!ot_otp_eg_is_buffered(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, @@ -2014,7 +2035,9 @@ static void ot_otp_eg_dai_write(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", @@ -2023,17 +2046,17 @@ static void ot_otp_eg_dai_write(OtOTPEgState *s) return; } - OtOTPPartController *pctrl = &s->partctrls[partition]; + OtOTPPartController *pctrl = &s->partctrls[part_ix]; if (pctrl->failed) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s is disabled\n", - __func__, s->ot_id, PART_NAME(partition)); + __func__, s->ot_id, PART_NAME(part_ix)); return; } if (pctrl->locked) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: partition %s (%u) is locked\n", - __func__, s->ot_id, PART_NAME(partition), partition); + __func__, s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2041,13 +2064,13 @@ 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(partition), partition); + s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } - bool is_digest = ot_otp_eg_is_part_digest_offset(partition, address); - bool is_wide = ot_otp_eg_is_wide_granule(partition, address); + bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, address); + bool is_wide = ot_otp_eg_is_wide_granule(part_ix, address); if (is_digest) { if (OtOTPPartDescs[partition].hw_digest) { @@ -2056,7 +2079,7 @@ static void ot_otp_eg_dai_write(OtOTPEgState *s) LOG_GUEST_ERROR, "%s: %s: partition %s (%u) HW digest cannot be directly " "written\n", - __func__, s->ot_id, PART_NAME(partition), partition); + __func__, s->ot_id, PART_NAME(part_ix), part_ix); ot_otp_eg_dai_set_error(s, OTP_ACCESS_ERROR); return; } @@ -2633,7 +2656,6 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( { OtOTPEgState *s = OT_OTP_EG(opaque); (void)attrs; - uint32_t val32; g_assert(addr + size <= SW_CFG_WINDOW_SIZE); @@ -2642,31 +2664,33 @@ static MemTxResult ot_otp_eg_swcfg_read_with_attrs( if (partition < 0) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "invalid"); - val32 = 0; + *data = 0ull; + + return MEMTX_DECODE_ERROR; } - if (ot_otp_eg_is_buffered(partition)) { + unsigned part_ix = (unsigned)partition; + + if (ot_otp_eg_is_buffered(part_ix)) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "buffered"); - ot_otp_eg_set_error(s, (unsigned)partition, OTP_ACCESS_ERROR); + ot_otp_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); /* real HW seems to stall the Tile Link bus in this case */ return MEMTX_ACCESS_ERROR; } - bool is_digest = ot_otp_eg_is_part_digest_offset(partition, addr); - bool is_readable = ot_otp_eg_is_readable(s, partition); + bool is_readable = ot_otp_eg_is_readable(s, part_ix); + bool is_digest = ot_otp_eg_is_part_digest_offset(part_ix, addr); - if (!is_digest && !is_readable) { + if (!is_readable && !is_digest) { trace_ot_otp_access_error_on(s->ot_id, partition, addr, "not readable"); - ot_otp_eg_set_error(s, (unsigned)partition, OTP_ACCESS_ERROR); + ot_otp_eg_set_error(s, part_ix, OTP_ACCESS_ERROR); return MEMTX_DECODE_ERROR; } - if (!(partition < 0)) { - val32 = s->otp->data[reg]; - ot_otp_eg_set_error(s, (unsigned)partition, OTP_NO_ERROR); - } + uint32_t val32 = s->otp->data[reg]; + ot_otp_eg_set_error(s, part_ix, OTP_NO_ERROR); uint64_t pc; @@ -2978,17 +3002,18 @@ static void ot_otp_eg_get_keymgr_secret( return; } - g_assert(ot_otp_eg_is_buffered(partition)); + unsigned part_ix = (unsigned)partition; + g_assert(ot_otp_eg_is_buffered(part_ix)); const uint8_t *data_ptr; if (es->lc_broadcast.current_level & BIT(OT_OTP_LC_SEED_HW_RD_EN)) { - data_ptr = (const uint8_t *)es->partctrls[partition].buffer.data; + data_ptr = (const uint8_t *)es->partctrls[part_ix].buffer.data; } else { /* source data from PartInvDefault instead of real buffer */ - data_ptr = es->inv_default_parts[partition]; + data_ptr = es->inv_default_parts[part_ix]; } - secret->valid = ot_otp_eg_get_buffered_part_digest(es, partition) != 0; + secret->valid = ot_otp_eg_get_buffered_part_digest(es, part_ix) != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3408,7 +3433,7 @@ static void ot_otp_eg_pwr_initialize_partitions(OtOTPEgState *s) } if (OtOTPPartDescs[ix].sw_digest) { - uint64_t digest = ot_otp_eg_get_part_digest(s, (int)ix); + uint64_t digest = ot_otp_eg_get_part_digest(s, ix); s->partctrls[ix].locked = digest != 0; continue; } @@ -3437,16 +3462,23 @@ static void ot_otp_eg_pwr_otp_bh(void *opaque) */ 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); } From 53e6f925cdef41a529ce489e10e59d1f1097e324 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 Oct 2025 19:51:36 +0100 Subject: [PATCH 6/9] [ot] hw/opentitan: ot_otp: rework secret partition unscrambling and digest management. - unscramble secret partitions at OTP load time - content of buffered partitions is stored, unscrambled, in partition buffers - all partition digest are loaded, ECC-corrected and stored in dedicated partition controller field, whether they are buffered or not - DAI read always access the OTP back-end data, never the buffered copy Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 163 +++++++++++++++++++++++++------------- hw/opentitan/ot_otp_eg.c | 163 +++++++++++++++++++++++++------------- hw/opentitan/trace-events | 1 + 3 files changed, 213 insertions(+), 114 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 303ca064880a..56d91a3a10eb 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -666,9 +666,9 @@ typedef struct { } state; struct { uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ - uint64_t digest; 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; @@ -1438,16 +1438,6 @@ static uint64_t ot_otp_dj_get_part_digest(OtOTPDjState *s, unsigned part_ix) return digest; } -static uint64_t -ot_otp_dj_get_buffered_part_digest(OtOTPDjState *s, unsigned part_ix) -{ - g_assert(ot_otp_dj_is_buffered(part_ix)); - - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - return pctrl->buffer.digest; -} - static bool ot_otp_dj_is_part_digest_offset(unsigned part_ix, hwaddr addr) { uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; @@ -1513,14 +1503,8 @@ static uint32_t ot_otp_dj_get_part_digest_reg(OtOTPDjState *s, uint32_t offset) "LC is expected to be the last partition"); g_assert(part_ix < OTP_PART_COUNT); - const OtOTPPartDesc *part = &OtOTPPartDescs[part_ix]; - uint64_t digest; - - if (part->buffered) { - digest = ot_otp_dj_get_buffered_part_digest(s, part_ix); - } else { - digest = ot_otp_dj_get_part_digest(s, part_ix); - } + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + uint64_t digest = pctrl->digest; if (hi) { digest >>= 32u; @@ -1538,7 +1522,7 @@ static bool ot_otp_dj_is_readable(OtOTPDjState *s, unsigned part_ix) if (pdesc->secret) { /* secret partitions are only readable if digest is not yet set. */ - return ot_otp_dj_get_buffered_part_digest(s, part_ix) == 0u; + return pctrl->digest == 0u; } if (!pdesc->read_lock_csr) { @@ -1751,6 +1735,43 @@ ot_otp_dj_load_partition_digest(OtOTPDjState *s, unsigned partition) return digest; } +static void ot_otp_dj_unscramble_partition(OtOTPDjState *s, unsigned ix) +{ + OtOTPPartController *pctrl = &s->partctrls[ix]; + + unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; + unsigned part_size = ot_otp_dj_part_data_byte_size(ix); + + /* part_size should be a multiple of PRESENT block size */ + g_assert((part_size & (sizeof(uint64_t) - 1u)) == 0u); + unsigned dword_count = part_size / sizeof(uint64_t); + + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; + + /* source address should be aligned to 64-bit boundary */ + g_assert(((uintptr_t)base & (sizeof(uint64_t) - 1u)) == 0u); + const uint64_t *scrambled = (const uint64_t *)base; + + /* destination address should be aligned to 64-bit boundary */ + g_assert(pctrl->buffer.data != NULL); + uint64_t *clear = (uint64_t *)pctrl->buffer.data; + + const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; + g_assert(scrambling_key); + + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, scrambling_key); + + trace_ot_otp_unscramble_partition(s->ot_id, PART_NAME(ix), ix, part_size); + /* neither the digest block nor the zeroizable block are scrambled */ + for (unsigned dix = 0u; dix < dword_count; dix++) { + ot_present_decrypt(ps, scrambled[dix], &clear[dix]); + } + + ot_present_free(ps); +} + static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned ix) { OtOTPPartController *pctrl = &s->partctrls[ix]; @@ -1758,25 +1779,37 @@ static void ot_otp_dj_bufferize_partition(OtOTPDjState *s, unsigned ix) g_assert(pctrl->buffer.data != NULL); if (OtOTPPartDescs[ix].hw_digest) { - pctrl->buffer.digest = ot_otp_dj_load_partition_digest(s, ix); + pctrl->digest = ot_otp_dj_load_partition_digest(s, ix); } else { - pctrl->buffer.digest = 0; + pctrl->digest = 0; } - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_dj_part_data_byte_size(ix); + if (OtOTPPartDescs[ix].secret) { + /* secret partitions need to be unscrambled */ + if (s->blk) { + /* + * nothing to unscramble if no OTP data is loaded + * scrambling keys in this case may not be known + */ + ot_otp_dj_unscramble_partition(s, ix); + } + } else { + unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; + unsigned part_size = ot_otp_dj_part_data_byte_size(ix); - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; - memcpy(pctrl->buffer.data, base, part_size); + memcpy(pctrl->buffer.data, base, part_size); + } } -static void ot_otp_dj_check_partition_integrity(OtOTPDjState *s, unsigned ix) +static void +ot_otp_dj_check_buffered_partition_integrity(OtOTPDjState *s, unsigned ix) { OtOTPPartController *pctrl = &s->partctrls[ix]; - if (pctrl->buffer.digest == 0) { + if (pctrl->digest == 0) { trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); pctrl->locked = false; return; @@ -1784,19 +1817,23 @@ static void ot_otp_dj_check_partition_integrity(OtOTPDjState *s, unsigned ix) pctrl->locked = true; + /* + * digests are always calculated over the original data (scrambled or not) + */ + const uint8_t *part_data = + ((const uint8_t *)s->otp->data) + ot_otp_dj_part_data_offset(ix); unsigned part_size = ot_otp_dj_part_data_byte_size(ix); + uint64_t digest = - ot_otp_dj_compute_partition_digest(s, - (const uint8_t *)pctrl->buffer.data, - part_size); + ot_otp_dj_compute_partition_digest(s, part_data, part_size); - if (digest != pctrl->buffer.digest) { + if (digest != pctrl->digest) { trace_ot_otp_mismatch_digest(s->ot_id, PART_NAME(ix), ix, digest, - pctrl->buffer.digest); + pctrl->digest); TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", PART_NAME(ix), digest, - ot_otp_hexdump(s, pctrl->buffer.data, part_size)); + ot_otp_hexdump(s, part_data, part_size)); pctrl->failed = true; /* this is a fatal error */ @@ -1972,16 +2009,20 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) } } - if (is_secret) { + if (is_secret && !(is_digest || is_zer)) { + /* + * if the partition is a secret partition, OTP storage is scrambled + * except the digest and the zeroification fields + */ const uint8_t *scrambling_key = s->otp_scramble_keys[partition]; g_assert(scrambling_key); - uint64_t data = ((uint64_t)data_hi << 32u) | data_lo; + 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, data, &data); + ot_present_decrypt(ps, tmp_data, &tmp_data); ot_present_free(ps); - data_lo = data; - data_hi = (data >> 32u); + data_lo = (uint32_t)tmp_data; + data_hi = (uint32_t)(tmp_data >> 32u); } s->regs[R_DIRECT_ACCESS_RDATA_0] = data_lo; @@ -2306,14 +2347,8 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); - const uint8_t *data; - - if (OtOTPPartDescs[partition].buffered) { - data = ((const uint8_t *)s->otp->data) + - ot_otp_dj_part_data_offset(partition); - } else { - data = (const uint8_t *)pctrl->buffer.data; - } + const uint8_t *data = + ((const uint8_t *)s->otp->data) + ot_otp_dj_part_data_offset(partition); unsigned part_size = ot_otp_dj_part_data_byte_size(partition); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -3126,7 +3161,7 @@ static void ot_otp_dj_get_keymgr_secret( data_ptr = ds->inv_default_parts[part_ix]; } - secret->valid = ot_otp_dj_get_buffered_part_digest(ds, part_ix) != 0; + secret->valid = ds->partctrls[part_ix].digest != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3536,6 +3571,9 @@ static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) static void ot_otp_dj_pwr_initialize_partitions(OtOTPDjState *s) { for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { + /* sanity check: all secret partitions are also buffered */ + g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); + if (ot_otp_dj_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { if (ot_otp_dj_apply_ecc(s, ix)) { continue; @@ -3543,15 +3581,15 @@ static void ot_otp_dj_pwr_initialize_partitions(OtOTPDjState *s) } if (OtOTPPartDescs[ix].sw_digest) { - uint64_t digest = ot_otp_dj_get_part_digest(s, ix); - s->partctrls[ix].locked = digest != 0; + s->partctrls[ix].digest = ot_otp_dj_get_part_digest(s, ix); + s->partctrls[ix].locked = s->partctrls[ix].digest != 0; continue; } if (OtOTPPartDescs[ix].buffered) { ot_otp_dj_bufferize_partition(s, ix); if (OtOTPPartDescs[ix].hw_digest) { - ot_otp_dj_check_partition_integrity(s, ix); + ot_otp_dj_check_buffered_partition_integrity(s, ix); } continue; } @@ -3727,7 +3765,18 @@ 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) { + continue; + } + if (!s->otp_scramble_key_xstrs[ix]) { + /* if OTP data is loaded, unscrambling keys are mandatory */ + if (s->blk) { + error_setg(&error_fatal, + "%s: %s Missing OTP scrambling key for part %s (%u)", + __func__, s->ot_id, PART_NAME(ix), ix); + return; + } continue; } @@ -3735,8 +3784,8 @@ static void ot_otp_dj_configure_part_scramble_keys(OtOTPDjState *s) if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { error_setg( &error_fatal, - "%s: %s Invalid OTP scrambling key length %zu for partition %u", - __func__, s->ot_id, len, ix); + "%s: %s Invalid OTP scrambling key length %zu for part %s (%u)", + __func__, s->ot_id, len, PART_NAME(ix), ix); return; } @@ -3747,8 +3796,8 @@ static void ot_otp_dj_configure_part_scramble_keys(OtOTPDjState *s) s->otp_scramble_key_xstrs[ix], OTP_SCRAMBLING_KEY_BYTES, true, true)) { error_setg(&error_fatal, - "%s: %s unable to parse otp_scramble_keys[%u]", __func__, - s->ot_id, ix); + "%s: %s unable to parse otp_scramble_keys[%u] for %s", + __func__, s->ot_id, ix, PART_NAME(ix)); return; } @@ -3951,7 +4000,7 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) } unsigned part_size = ot_otp_dj_part_data_byte_size(ix); memset(s->partctrls[ix].buffer.data, 0, part_size); - s->partctrls[ix].buffer.digest = 0; + s->partctrls[ix].digest = 0; if (OtOTPPartDescs[ix].iskeymgr_creator || OtOTPPartDescs[ix].iskeymgr_owner) { s->partctrls[ix].read_lock = true; diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 3375cafc27c2..059ca7b807c4 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -600,9 +600,9 @@ typedef struct { } state; struct { uint32_t *data; /* size, see OtOTPPartDescs; w/o digest data */ - uint64_t digest; 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; @@ -1327,16 +1327,6 @@ static uint64_t ot_otp_eg_get_part_digest(OtOTPEgState *s, unsigned part_ix) return digest; } -static uint64_t -ot_otp_eg_get_buffered_part_digest(OtOTPEgState *s, unsigned part_ix) -{ - g_assert(ot_otp_eg_is_buffered(part_ix)); - - const OtOTPPartController *pctrl = &s->partctrls[part_ix]; - - return pctrl->buffer.digest; -} - static bool ot_otp_eg_is_part_digest_offset(unsigned part_ix, hwaddr addr) { uint16_t offset = OtOTPPartDescs[part_ix].digest_offset; @@ -1395,14 +1385,8 @@ static uint32_t ot_otp_eg_get_part_digest_reg(OtOTPEgState *s, uint32_t offset) "LC is expected to be the last partition"); g_assert(part_ix < OTP_PART_COUNT); - const OtOTPPartDesc *part = &OtOTPPartDescs[part_ix]; - uint64_t digest; - - if (part->buffered) { - digest = ot_otp_eg_get_buffered_part_digest(s, part_ix); - } else { - digest = ot_otp_eg_get_part_digest(s, part_ix); - } + const OtOTPPartController *pctrl = &s->partctrls[part_ix]; + uint64_t digest = pctrl->digest; if (hi) { digest >>= 32u; @@ -1420,7 +1404,7 @@ static bool ot_otp_eg_is_readable(OtOTPEgState *s, unsigned part_ix) if (pdesc->secret) { /* secret partitions are only readable if digest is not yet set. */ - return ot_otp_eg_get_buffered_part_digest(s, part_ix) == 0u; + return pctrl->digest == 0u; } if (!pdesc->read_lock_csr) { @@ -1633,6 +1617,43 @@ ot_otp_eg_load_partition_digest(OtOTPEgState *s, unsigned partition) return digest; } +static void ot_otp_eg_unscramble_partition(OtOTPEgState *s, unsigned ix) +{ + OtOTPPartController *pctrl = &s->partctrls[ix]; + + unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; + unsigned part_size = ot_otp_eg_part_data_byte_size(ix); + + /* part_size should be a multiple of PRESENT block size */ + g_assert((part_size & (sizeof(uint64_t) - 1u)) == 0u); + unsigned dword_count = part_size / sizeof(uint64_t); + + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; + + /* source address should be aligned to 64-bit boundary */ + g_assert(((uintptr_t)base & (sizeof(uint64_t) - 1u)) == 0u); + const uint64_t *scrambled = (const uint64_t *)base; + + /* destination address should be aligned to 64-bit boundary */ + g_assert(pctrl->buffer.data != NULL); + uint64_t *clear = (uint64_t *)pctrl->buffer.data; + + const uint8_t *scrambling_key = s->otp_scramble_keys[ix]; + g_assert(scrambling_key); + + OtPresentState *ps = ot_present_new(); + ot_present_init(ps, scrambling_key); + + trace_ot_otp_unscramble_partition(s->ot_id, PART_NAME(ix), ix, part_size); + /* neither the digest block nor the zeroizable block are scrambled */ + for (unsigned dix = 0u; dix < dword_count; dix++) { + ot_present_decrypt(ps, scrambled[dix], &clear[dix]); + } + + ot_present_free(ps); +} + static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) { OtOTPPartController *pctrl = &s->partctrls[ix]; @@ -1640,25 +1661,37 @@ static void ot_otp_eg_bufferize_partition(OtOTPEgState *s, unsigned ix) g_assert(pctrl->buffer.data != NULL); if (OtOTPPartDescs[ix].hw_digest) { - pctrl->buffer.digest = ot_otp_eg_load_partition_digest(s, ix); + pctrl->digest = ot_otp_eg_load_partition_digest(s, ix); } else { - pctrl->buffer.digest = 0; + pctrl->digest = 0; } - unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; - unsigned part_size = ot_otp_eg_part_data_byte_size(ix); + if (OtOTPPartDescs[ix].secret) { + /* secret partitions need to be unscrambled */ + if (s->blk) { + /* + * nothing to unscramble if no OTP data is loaded + * scrambling keys in this case may not be known + */ + ot_otp_eg_unscramble_partition(s, ix); + } + } else { + unsigned offset = (unsigned)OtOTPPartDescs[ix].offset; + unsigned part_size = ot_otp_eg_part_data_byte_size(ix); - const uint8_t *base = (const uint8_t *)s->otp->data; - base += offset; + const uint8_t *base = (const uint8_t *)s->otp->data; + base += offset; - memcpy(pctrl->buffer.data, base, part_size); + memcpy(pctrl->buffer.data, base, part_size); + } } -static void ot_otp_eg_check_partition_integrity(OtOTPEgState *s, unsigned ix) +static void +ot_otp_eg_check_buffered_partition_integrity(OtOTPEgState *s, unsigned ix) { OtOTPPartController *pctrl = &s->partctrls[ix]; - if (pctrl->buffer.digest == 0) { + if (pctrl->digest == 0) { trace_ot_otp_skip_digest(s->ot_id, PART_NAME(ix), ix); pctrl->locked = false; return; @@ -1666,19 +1699,23 @@ static void ot_otp_eg_check_partition_integrity(OtOTPEgState *s, unsigned ix) pctrl->locked = true; + /* + * digests are always calculated over the original data (scrambled or not) + */ + const uint8_t *part_data = + ((const uint8_t *)s->otp->data) + ot_otp_eg_part_data_offset(ix); unsigned part_size = ot_otp_eg_part_data_byte_size(ix); + uint64_t digest = - ot_otp_eg_compute_partition_digest(s, - (const uint8_t *)pctrl->buffer.data, - part_size); + ot_otp_eg_compute_partition_digest(s, part_data, part_size); - if (digest != pctrl->buffer.digest) { + if (digest != pctrl->digest) { trace_ot_otp_mismatch_digest(s->ot_id, PART_NAME(ix), ix, digest, - pctrl->buffer.digest); + pctrl->digest); TRACE_OTP("compute digest of %s: %016" PRIx64 " from %s\n", PART_NAME(ix), digest, - ot_otp_hexdump(s, pctrl->buffer.data, part_size)); + ot_otp_hexdump(s, part_data, part_size)); pctrl->failed = true; /* this is a fatal error */ @@ -1853,16 +1890,20 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) } } - if (is_secret) { + if (is_secret && !is_digest) { + /* + * if the partition is a secret partition, OTP storage is scrambled + * except the digest and the zeroification fields + */ const uint8_t *scrambling_key = s->otp_scramble_keys[partition]; g_assert(scrambling_key); - uint64_t data = ((uint64_t)data_hi << 32u) | data_lo; + 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, data, &data); + ot_present_decrypt(ps, tmp_data, &tmp_data); ot_present_free(ps); - data_lo = data; - data_hi = (data >> 32u); + data_lo = (uint32_t)tmp_data; + data_hi = (uint32_t)(tmp_data >> 32u); } s->regs[R_DIRECT_ACCESS_RDATA_0] = data_lo; @@ -2187,14 +2228,8 @@ static void ot_otp_eg_dai_digest(OtOTPEgState *s) DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); - const uint8_t *data; - - if (OtOTPPartDescs[partition].buffered) { - data = ((const uint8_t *)s->otp->data) + - ot_otp_eg_part_data_offset(partition); - } else { - data = (const uint8_t *)pctrl->buffer.data; - } + const uint8_t *data = + ((const uint8_t *)s->otp->data) + ot_otp_eg_part_data_offset(partition); unsigned part_size = ot_otp_eg_part_data_byte_size(partition); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -3013,7 +3048,7 @@ static void ot_otp_eg_get_keymgr_secret( data_ptr = es->inv_default_parts[part_ix]; } - secret->valid = ot_otp_eg_get_buffered_part_digest(es, part_ix) != 0; + secret->valid = es->partctrls[part_ix].digest != 0; memcpy(secret->secret, &data_ptr[offset], OT_OTP_KEYMGR_SECRET_SIZE); } @@ -3426,6 +3461,9 @@ 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++) { + /* sanity check: all secret partitions are also buffered */ + g_assert(!OtOTPPartDescs[ix].secret || OtOTPPartDescs[ix].buffered); + if (ot_otp_eg_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { if (ot_otp_eg_apply_ecc(s, ix)) { continue; @@ -3433,15 +3471,15 @@ static void ot_otp_eg_pwr_initialize_partitions(OtOTPEgState *s) } if (OtOTPPartDescs[ix].sw_digest) { - uint64_t digest = ot_otp_eg_get_part_digest(s, ix); - s->partctrls[ix].locked = digest != 0; + s->partctrls[ix].digest = ot_otp_eg_get_part_digest(s, ix); + s->partctrls[ix].locked = s->partctrls[ix].digest != 0; continue; } if (OtOTPPartDescs[ix].buffered) { ot_otp_eg_bufferize_partition(s, ix); if (OtOTPPartDescs[ix].hw_digest) { - ot_otp_eg_check_partition_integrity(s, ix); + ot_otp_eg_check_buffered_partition_integrity(s, ix); } continue; } @@ -3708,7 +3746,18 @@ 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) { + continue; + } + if (!s->otp_scramble_key_xstrs[ix]) { + /* if OTP data is loaded, unscrambling keys are mandatory */ + if (s->blk) { + error_setg(&error_fatal, + "%s: %s Missing OTP scrambling key for part %s (%u)", + __func__, s->ot_id, PART_NAME(ix), ix); + return; + } continue; } @@ -3716,8 +3765,8 @@ static void ot_otp_eg_configure_part_scramble_keys(OtOTPEgState *s) if (len != OTP_SCRAMBLING_KEY_BYTES * 2u) { error_setg( &error_fatal, - "%s: %s Invalid OTP scrambling key length %zu for partition %u", - __func__, s->ot_id, len, ix); + "%s: %s Invalid OTP scrambling key length %zu for part %s (%u)", + __func__, s->ot_id, len, PART_NAME(ix), ix); return; } @@ -3728,8 +3777,8 @@ static void ot_otp_eg_configure_part_scramble_keys(OtOTPEgState *s) s->otp_scramble_key_xstrs[ix], OTP_SCRAMBLING_KEY_BYTES, true, true)) { error_setg(&error_fatal, - "%s: %s unable to parse otp_scramble_keys[%u]", __func__, - s->ot_id, ix); + "%s: %s unable to parse otp_scramble_keys[%u] for %s", + __func__, s->ot_id, ix, PART_NAME(ix)); return; } @@ -3926,7 +3975,7 @@ static void ot_otp_eg_reset_enter(Object *obj, ResetType type) } unsigned part_size = ot_otp_eg_part_data_byte_size(ix); memset(s->partctrls[ix].buffer.data, 0, part_size); - s->partctrls[ix].buffer.digest = 0; + s->partctrls[ix].digest = 0; if (OtOTPPartDescs[ix].iskeymgr_creator || OtOTPPartDescs[ix].iskeymgr_owner) { s->partctrls[ix].read_lock = true; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 9473bbeaa2e9..76ce0226c7a9 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -441,6 +441,7 @@ ot_otp_reset(const char * id, const char *phase) "%s: %s" ot_otp_set_error(const char * id, const char *part, unsigned pix, const char* err, unsigned eix) "%s: %s (#%u): %s (%u)" ot_otp_skip_digest(const char * id, const char* part, unsigned pix) "%s: skipping empty digest on %s (#%u)" ot_otp_key_generated(const char * id, int type) "%s: type %d" +ot_otp_unscramble_partition(const char * id, const char *part, unsigned pix, unsigned size) "%s: partition %s (#%u) size:%u" ot_otp_update_irq(const char * id, int prev, int next) "%s: %d -> %d" ot_otp_update_alert(const char * id, int prev, int next) "%s: %d -> %d" From 8333fbface5243b9b2d533af3f04b3dc67325263 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 Oct 2025 12:32:27 +0100 Subject: [PATCH 7/9] [ot] hw/opentitan: ot_otp: use `integrity` descriptor bit Replace this legacy implementation based on partition "type", with the `integrity` partition descriptor to perform ECC. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 4 ++-- hw/opentitan/ot_otp_eg.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 56d91a3a10eb..67895f9ed903 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1427,7 +1427,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 (part_ix != OTP_PART_VENDOR_TEST && ot_otp_dj_is_ecc_enabled(s)) { + if (OtOTPPartDescs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s)) { unsigned waddr = offset >> 2u; unsigned ewaddr = waddr >> 1u; g_assert(ewaddr < s->otp->ecc_size); @@ -1952,7 +1952,7 @@ static void ot_otp_dj_dai_read(OtOTPDjState *s) unsigned part_offset = address - ot_otp_dj_part_data_offset(partition); unsigned part_waddr = part_offset >> 2u; bool do_ecc = - (partition != OTP_PART_VENDOR_TEST) && ot_otp_dj_is_ecc_enabled(s); + OtOTPPartDescs[part_ix].integrity && ot_otp_dj_is_ecc_enabled(s); DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 059ca7b807c4..99a340315321 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1316,7 +1316,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 (part_ix != OTP_PART_VENDOR_TEST && ot_otp_eg_is_ecc_enabled(s)) { + if (OtOTPPartDescs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s)) { unsigned waddr = offset >> 2u; unsigned ewaddr = waddr >> 1u; g_assert(ewaddr < s->otp->ecc_size); @@ -1833,7 +1833,7 @@ static void ot_otp_eg_dai_read(OtOTPEgState *s) unsigned part_offset = address - ot_otp_eg_part_data_offset(partition); unsigned part_waddr = part_offset >> 2u; bool do_ecc = - (partition != OTP_PART_VENDOR_TEST) && ot_otp_eg_is_ecc_enabled(s); + OtOTPPartDescs[part_ix].integrity && ot_otp_eg_is_ecc_enabled(s); DAI_CHANGE_STATE(s, OTP_DAI_READ_WAIT); From 984e9bcc4f5598ac5c24f860947aa4c84a948a07 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 24 Oct 2025 15:22:02 +0200 Subject: [PATCH 8/9] [ot] .gitlab-ci.d/opentitan: ot-smoke.yml: add Darjeeling smoke test Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/ot-smoke.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.d/opentitan/ot-smoke.yml b/.gitlab-ci.d/opentitan/ot-smoke.yml index ffdfbb7cfad4..da4ac2751cf0 100644 --- a/.gitlab-ci.d/opentitan/ot-smoke.yml +++ b/.gitlab-ci.d/opentitan/ot-smoke.yml @@ -10,3 +10,6 @@ smoke-tests-ot: -device loader,addr=0x100080,file=build/exit_id.bin -d in_asm,int - timeout -s KILL 4 build/qemu-system-riscv32 -M ot-earlgrey,no_epmp_cfg=true -nographic -object ot-rom_img,id=rom,file=build/exit_eg.bin -d in_asm,int + - timeout -s KILL 4 build/qemu-system-riscv32 -M ot-darjeeling,no_epmp_cfg=true + -object ot-rom_img,id=rom,file=build/exit_eg.bin -d in_asm,int + -nographic -global ot-ibex_wrapper.lc-ignore=on From 473089b34e1c5296fa5a6b41cb0cf7d19d61741d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 24 Oct 2025 15:22:53 +0200 Subject: [PATCH 9/9] [ot] .github/workflows: build_test.yaml: add Darjeeling smoke test Signed-off-by: Emmanuel Blot --- .github/workflows/build_test.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index 694d4783d36e..a04ef400b543 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -159,6 +159,11 @@ jobs: run: | timeout -s KILL 4 ./qemu-system-riscv32 -M ot-earlgrey,no_epmp_cfg=true -nographic \ -object ot-rom_img,id=rom,file=exit_eg.bin -d in_asm,int + - name: Check Darjeeling VM execution + run: | + timeout -s KILL 4 ./qemu-system-riscv32 -M ot-darjeeling,no_epmp_cfg=true -nographic \ + -object ot-rom_img,id=rom0,file=exit_dj.bin -global ot-ibex_wrapper.lc-ignore=on \ + -d in_asm,int build-gcc: runs-on: ubuntu-24.04