diff --git a/docs/config/opentitan/darjeeling.cfg b/docs/config/opentitan/darjeeling.cfg index 0c27f85672b8b..e7b9802d8b9e3 100644 --- a/docs/config/opentitan/darjeeling.cfg +++ b/docs/config/opentitan/darjeeling.cfg @@ -1,5 +1,4 @@ -# Generated from OpenTitan 'master' branch -# commit: fc2d73b432 +# Generated from OpenTitan commit: c5507b4cdc [ot_device "ot-rom_ctrl.rom0"] key = "30ae84156d37cc68063276f9e85faee1" @@ -39,8 +38,8 @@ production = "ffc2171a49199bb53d8e0daa8f0578db" raw_unlock_token = "e4225dc332ea1fda63b4c524556ed4d4" rma = "aa5f022791480b4e709e0f80976e0966" - socdbg_first = "6b36fc2c" - socdbg_last = "ebf6fc7f" + soc_dbg_first = "6b36fc2c" + soc_dbg_last = "ebf6fc7f" test_unlocked = "e4c72c47d9e15fa8ff9f82833e32c151" [ot_device "ot-keymgr_dpe"] @@ -67,3 +66,4 @@ [ot_device "ot-pwrmgr"] clocks = "main,io" + diff --git a/docs/opentitan/ot_darjeeling.md b/docs/opentitan/ot_darjeeling.md index 783ce9cdc229e..23b95dc23cc77 100644 --- a/docs/opentitan/ot_darjeeling.md +++ b/docs/opentitan/ot_darjeeling.md @@ -30,7 +30,7 @@ Please check out `hw/opentitan/ot_ref.log` * zero-ization is not yet supported * [RISC-V Debug Module](jtag-dm.md) and Pulp Debug Module * [ROM controller](ot_rom_ctrl.md) -* [SoC debug controller documentation](ot_socdbg_ctrl.md) +* [SoC debug controller documentation](ot_soc_dbg_ctrl.md) * SPI data flash (from QEMU upstream w/ fixes) * [SPI Host controller](ot_spi_host.md) * HW bus config is ignored (SPI mode, speed, ...) diff --git a/docs/opentitan/ot_socdbg_ctrl.md b/docs/opentitan/ot_soc_dbg_ctrl.md similarity index 100% rename from docs/opentitan/ot_socdbg_ctrl.md rename to docs/opentitan/ot_soc_dbg_ctrl.md diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index d5bd6b5dc068a..8c1f5c6ae25eb 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -191,7 +191,7 @@ config OT_SENSOR_EG config OT_SOC_PROXY bool -config OT_SOCDBG_CTRL +config OT_SOC_DBG_CTRL bool config OT_SPI_DEVICE diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 3acdfbc2ae9ba..48fb5d9e4cccf 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -52,7 +52,7 @@ system_ss.add(when: 'CONFIG_OT_ROM_CTRL', if_true: files('ot_rom_ctrl.c', 'ot_ro system_ss.add(when: 'CONFIG_OT_RSTMGR', if_true: files('ot_rstmgr.c')) system_ss.add(when: 'CONFIG_OT_SENSOR_EG', if_true: files('ot_sensor_eg.c')) system_ss.add(when: 'CONFIG_OT_SOC_PROXY', if_true: files('ot_soc_proxy.c')) -system_ss.add(when: 'CONFIG_OT_SOCDBG_CTRL', if_true: files('ot_socdbg_ctrl.c')) +system_ss.add(when: 'CONFIG_OT_SOC_DBG_CTRL', if_true: files('ot_soc_dbg_ctrl.c')) system_ss.add(when: 'CONFIG_OT_SPI_DEVICE', if_true: files('ot_spi_device.c')) system_ss.add(when: 'CONFIG_OT_SPI_HOST', if_true: files('ot_spi_host.c')) system_ss.add(when: 'CONFIG_OT_SRAM_CTRL', if_true: files('ot_sram_ctrl.c')) diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index d6810c4e03351..04b5208c171f1 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -25,8 +25,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * - * Note: for now, only a minimalist subset of Life Cycle controller device is - * implemented in order to enable OpenTitan's ROM boot to progress + * Based on OpenTitan 0fc384d8a6 */ #include "qemu/osdep.h" @@ -40,7 +39,6 @@ #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otp_if.h" #include "hw/opentitan/ot_pwrmgr.h" -#include "hw/opentitan/ot_socdbg_ctrl.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/ibex_common.h" @@ -58,11 +56,11 @@ #define REVISION_ID_WIDTH 8u /* clang-format off */ -REG32(ALERT_TEST, 0x0u) +REG32(ALERT_TEST, 0x00u) SHARED_FIELD(ALERT_FATAL_PROG_ERROR, 0u, 1u) SHARED_FIELD(ALERT_FATAL_STATE_ERROR, 1u, 1u) SHARED_FIELD(ALERT_FATAL_BUS_INTEG_ERROR, 2u, 1u) -REG32(STATUS, 0x4u) +REG32(STATUS, 0x04u) FIELD(STATUS, INITIALIZED, 0u, 1u) FIELD(STATUS, READY, 1u, 1u) FIELD(STATUS, EXT_CLOCK_SWITCHED, 2u, 1u) @@ -75,10 +73,10 @@ REG32(STATUS, 0x4u) FIELD(STATUS, STATE_ERROR, 9u, 1u) FIELD(STATUS, BUS_INTEG_ERROR, 10u, 1u) FIELD(STATUS, OTP_PARTITION_ERROR, 11u, 1u) -REG32(CLAIM_TRANSITION_IF_REGWEN, 0x8u) +REG32(CLAIM_TRANSITION_IF_REGWEN, 0x08u) FIELD(CLAIM_TRANSITION_IF_REGWEN, EN, 0u, 1u) -REG32(CLAIM_TRANSITION_IF, 0xcu) - FIELD(CLAIM_TRANSITION, IF_MUTEX, 0u, 8u) +REG32(CLAIM_TRANSITION_IF, 0x0cu) + FIELD(CLAIM_TRANSITION_IF, MUTEX, 0u, 8u) REG32(TRANSITION_REGWEN, 0x10u) FIELD(TRANSITION_REGWEN, EN, 0u, 1u) REG32(TRANSITION_CMD, 0x14u) @@ -125,6 +123,13 @@ REG32(MANUF_STATE_6, 0x84u) REG32(MANUF_STATE_7, 0x88u) /* clang-format on */ +#define ALERT_TEST_WMASK \ + (ALERT_FATAL_PROG_ERROR_MASK | ALERT_FATAL_STATE_ERROR_MASK | \ + ALERT_FATAL_BUS_INTEG_ERROR_MASK) +#define TRANSITION_CTRL_WMASK \ + (TRANSITION_CTRL_EXT_CLOCK_EN_MASK | \ + TRANSITION_CTRL_VOLATILE_RAW_UNLOCK_MASK) + #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) #define R_LAST_REG (R_MANUF_STATE_7) @@ -138,10 +143,6 @@ REG32(MANUF_STATE_7, 0x88u) #define EXCLUSIVE_REGS_COUNT (R_LAST_EXCLUSIVE_REG - R_FIRST_EXCLUSIVE_REG + 1u) #define XREGS_OFFSET(_r_) ((_r_) - R_FIRST_EXCLUSIVE_REG) -#define ALERT_TEST_MASK \ - (ALERT_FATAL_PROG_ERROR_MASK | ALERT_FATAL_STATE_ERROR_MASK | \ - ALERT_FATAL_BUS_INTEG_ERROR_MASK) - #define LC_TRANSITION_COUNT_MAX 24u #define LC_TOKEN_WIDTH 16u /* 128 bits */ #define LC_TOKEN_DWORDS (LC_TOKEN_WIDTH / sizeof(uint64_t)) @@ -192,18 +193,18 @@ static const char *REG_NAMES[REGS_COUNT] = { #define NUM_LC_STATE (LC_STATE_VALID_COUNT) #define NUM_LC_TRANSITION_COUNT 25u #define NUM_OWNERSHIP 9u -#define NUM_SOCDBG 3u +#define NUM_SOC_DBG 3u #define LC_TRANSITION_COUNT_WORDS 24u #define LC_STATE_WORDS 20u #define OWNERSHIP_WORDS 8u -#define SOCDBG_WORDS 2u +#define SOC_DBG_WORDS 2u #define LC_TRANSITION_COUNT_BYTES \ ((LC_TRANSITION_COUNT_WORDS) / sizeof(uint16_t)) #define LC_STATE_BYTES ((LC_STATE_WORDS) / sizeof(uint16_t)) #define OWNERSHIP_SIZE ((OWNERSHIP_WORDS) / sizeof(uint16_t)) -#define SOCDBG_SIZE ((SOCDBG_WORDS) / sizeof(uint16_t)) +#define SOC_DBG_SIZE ((SOC_DBG_WORDS) / sizeof(uint16_t)) #define LC_STATE_BIT_WIDTH 5u #define LC_ENCODE_STATE(_x_) \ @@ -279,7 +280,7 @@ typedef enum { typedef uint16_t OtLcCtrlStateValue[LC_STATE_WORDS]; typedef uint16_t OtLcCtrlTransitionCountValue[LC_TRANSITION_COUNT_WORDS]; typedef uint16_t OtLcCtrlOwnershipValue[OWNERSHIP_WORDS]; -typedef uint16_t OtLcCtrlSocDbgValue[SOCDBG_WORDS]; +typedef uint16_t OtLcCtrlSocDbgValue[SOC_DBG_WORDS]; typedef enum { LC_IF_NONE, @@ -353,7 +354,7 @@ typedef enum { LC_CTRL_TRANS_LC_STATE, LC_CTRL_TRANS_LC_TCOUNT, LC_CTRL_TRANS_OWNERSHIP, - LC_CTRL_TRANS_SOCDBG, + LC_CTRL_TRANS_SOC_DBG, LC_CTRL_TRANS_COUNT } OtLcCtrlTransition; @@ -378,7 +379,7 @@ struct OtLcCtrlState { IbexIRQ alerts[NUM_ALERTS]; IbexIRQ broadcasts[OT_LC_BROADCAST_COUNT]; IbexIRQ pwc_lc_rsp; - IbexIRQ socdbg_tx; + IbexIRQ soc_dbg_tx; uint32_t *regs; /* slots in xregs are not used in regs */ uint32_t xregs[EXCLUSIVE_SLOTS_COUNT][EXCLUSIVE_REGS_COUNT]; @@ -392,7 +393,7 @@ struct OtLcCtrlState { OtLcCtrlStateValue *lc_states; OtLcCtrlTransitionCountValue *lc_transitions; OtLcCtrlOwnershipValue *ownerships; - OtLcCtrlSocDbgValue *socdbgs; + OtLcCtrlSocDbgValue *soc_dbgs; OtOTPTokenValue *hashed_tokens; uint8_t km_divs[LC_DIV_COUNT][OT_LC_KEYMGR_DIV_BYTES]; @@ -420,7 +421,7 @@ struct OtLcCtrlState { uint8_t revision_id; uint8_t kmac_app; bool volatile_raw_unlock; - bool socdbg; /* whether this instance use SoCDbg state */ + bool decode_soc_dbg; /* whether to handle SoCDbg state */ }; /* try to cope with the many ways to encode a transition matrix */ @@ -554,11 +555,11 @@ static const OtLcCtrlTransitionDesc TRANSITION_DESC[LC_CTRL_TRANS_COUNT] = { .mode = LC_TR_MODE_FIRST_FULL, .name = "ownership", }, - [LC_CTRL_TRANS_SOCDBG] = { - .word_count = SOCDBG_WORDS, - .step_count = NUM_SOCDBG, + [LC_CTRL_TRANS_SOC_DBG] = { + .word_count = SOC_DBG_WORDS, + .step_count = NUM_SOC_DBG, .mode = LC_TR_MODE_FULL_CHANGE, - .name = "socdbg", + .name = "soc_dbg", }, }; @@ -708,7 +709,7 @@ LC_STATES_TPL[NUM_LC_STATE][LC_STATE_WORDS] = { #ifdef OT_LC_CTRL_DEBUG #define OT_LC_CTRL_HEXSTR_SIZE 256u #define TRACE_LC_CTRL(msg, ...) \ - qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); + qemu_log("%s: %s: " msg "\n", __func__, s->ot_id, ##__VA_ARGS__); #define ot_lc_ctrl_hexdump(_s_, _b_, _l_) \ ot_common_lhexdump((const uint8_t *)_b_, _l_, false, (_s_)->hexstr, \ OT_LC_CTRL_HEXSTR_SIZE) @@ -823,15 +824,16 @@ static void ot_lc_ctrl_update_broadcast(OtLcCtrlState *s) div_type = LC_DIV_DEV; break; case LC_STATE_RMA: - sigbm = - LC_BCAST_BIT(RAW_TEST_RMA) | LC_BCAST_BIT(DFT_EN) | - LC_BCAST_BIT(NVM_DEBUG_EN) | LC_BCAST_BIT(HW_DEBUG_EN) | - LC_BCAST_BIT(CPU_EN) | LC_BCAST_BIT(KEYMGR_EN) | - LC_BCAST_BIT(CHECK_BYP_EN) | - LC_BCAST_BIT(CREATOR_SEED_SW_RW_EN) | - LC_BCAST_BIT(OWNER_SEED_SW_RW_EN) | - LC_BCAST_BIT(ISO_PART_SW_RD_EN) | - LC_BCAST_BIT(ISO_PART_SW_WR_EN) | LC_BCAST_BIT(SEED_HW_RD_EN); + /* note: RMA signal not available on EG 1.0.0 */ + sigbm = LC_BCAST_BIT(RAW_TEST_RMA) | LC_BCAST_BIT(DFT_EN) | + LC_BCAST_BIT(NVM_DEBUG_EN) | LC_BCAST_BIT(HW_DEBUG_EN) | + LC_BCAST_BIT(CPU_EN) | LC_BCAST_BIT(KEYMGR_EN) | + LC_BCAST_BIT(CHECK_BYP_EN) | + LC_BCAST_BIT(CREATOR_SEED_SW_RW_EN) | + LC_BCAST_BIT(OWNER_SEED_SW_RW_EN) | + LC_BCAST_BIT(ISO_PART_SW_RD_EN) | + LC_BCAST_BIT(ISO_PART_SW_WR_EN) | + LC_BCAST_BIT(SEED_HW_RD_EN) | LC_BCAST_BIT(RMA); div_type = LC_DIV_RMA; break; case LC_STATE_SCRAP: @@ -1228,31 +1230,31 @@ static void ot_lc_ctrl_load_otp_hw_cfg(OtLcCtrlState *s) memcpy(&s->regs[R_MANUF_STATE_0], &hw_cfg->manuf_state[0], LC_MANUF_STATE_WIDTH); - if (!s->socdbg) { + if (!s->decode_soc_dbg) { return; } /* default to lowest capabilities */ - int socdbg_ix = OT_SOCDBG_ST_PROD; + int soc_dbg_ix = OT_LC_SOC_DBG_STATE_COUNT - 1u; TRACE_LC_CTRL("soc_dbg_state: %s", ot_lc_ctrl_hexdump(s, hw_cfg->soc_dbg_state, sizeof(OtLcCtrlSocDbgValue))); - for (unsigned six = 0; six < OT_SOCDBG_ST_COUNT; six++) { - bool match = !memcmp(hw_cfg->soc_dbg_state, s->socdbgs[six], + for (unsigned six = 0; six < OT_LC_SOC_DBG_STATE_COUNT; six++) { + bool match = !memcmp(hw_cfg->soc_dbg_state, s->soc_dbgs[six], sizeof(OtLcCtrlSocDbgValue)); TRACE_LC_CTRL("soc_dbg ref[%u]: %s, match: %u", six, - ot_lc_ctrl_hexdump(s, s->socdbgs[six], + ot_lc_ctrl_hexdump(s, s->soc_dbgs[six], sizeof(OtLcCtrlSocDbgValue)), match); if (match) { - socdbg_ix = (int)six; + soc_dbg_ix = (int)six; break; } } - ibex_irq_set(&s->socdbg_tx, socdbg_ix); + ibex_irq_set(&s->soc_dbg_tx, soc_dbg_ix); } static void ot_lc_ctrl_handle_otp_ack(void *opaque, bool ack) @@ -1301,8 +1303,8 @@ static void ot_lc_ctrl_program_otp(OtLcCtrlState *s, unsigned lc_tcount, if (!oc->program_req) { qemu_log_mask(LOG_UNIMP, - "%s: OTP implementation does not support programming", - __func__); + "%s: %s: OTP implementation does not support programming", + __func__, s->ot_id); s->regs[R_STATUS] |= R_STATUS_OTP_ERROR_MASK; LC_FSM_CHANGE_STATE(s, ST_POST_TRANS); s->lc_state = LC_STATE_POST_TRANSITION; @@ -1765,13 +1767,14 @@ static uint32_t ot_lc_ctrl_regs_read(OtLcCtrlState *s, hwaddr addr, break; case R_ALERT_TEST: qemu_log_mask(LOG_GUEST_ERROR, - "%s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, - addr, REG_NAME(reg)); + "%s: %s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); val32 = 0; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); val32 = 0; break; } @@ -1819,7 +1822,7 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, switch (reg) { case R_ALERT_TEST: - val32 &= ALERT_TEST_MASK; + val32 &= ALERT_TEST_WMASK; s->regs[R_ALERT_TEST] = val32; ot_lc_ctrl_update_alerts(s); break; @@ -1830,8 +1833,9 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, case R_CLAIM_TRANSITION_IF: if (!(s->regs[R_CLAIM_TRANSITION_IF_REGWEN] & R_CLAIM_TRANSITION_IF_REGWEN_EN_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: CLAIM_TRANSITION_IF disabled\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: CLAIM_TRANSITION_IF disabled\n", __func__, + s->ot_id); break; } val32 &= R_CLAIM_TRANSITION_IF_MUTEX_MASK; @@ -1845,8 +1849,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, val32 &= R_TRANSITION_CMD_START_MASK; if (val32) { if (!ot_lc_ctrl_is_transition_en(s, ifreq)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: LC IF not available\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: LC IF not available\n", + __func__, s->ot_id); break; } ot_lc_ctrl_start_transition(s); @@ -1854,8 +1858,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, break; case R_TRANSITION_CTRL: if (!ot_lc_ctrl_is_transition_en(s, ifreq)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: LC IF not available\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: LC IF not available\n", + __func__, s->ot_id); break; } if (val32 & R_TRANSITION_CTRL_EXT_CLOCK_EN_MASK) { @@ -1874,8 +1878,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, case R_TRANSITION_TOKEN_2: case R_TRANSITION_TOKEN_3: if (!ot_lc_ctrl_is_transition_en(s, ifreq)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: LC IF not available\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: LC IF not available\n", + __func__, s->ot_id); break; } g_assert(LC_XSLOT(ifreq) < EXCLUSIVE_SLOTS_COUNT); @@ -1883,8 +1887,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, break; case R_TRANSITION_TARGET: if (!ot_lc_ctrl_is_transition_en(s, ifreq)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: LC IF not available\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: LC IF not available\n", + __func__, s->ot_id); break; } val32 &= R_TRANSITION_TARGET_STATE_MASK; @@ -1895,8 +1899,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, break; case R_OTP_VENDOR_TEST_CTRL: if (!ot_lc_ctrl_is_transition_en(s, ifreq)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: LC IF not available\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: LC IF not available\n", + __func__, s->ot_id); break; } s->regs[reg] = val32; @@ -1926,12 +1930,13 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, case R_MANUF_STATE_6: case R_MANUF_STATE_7: qemu_log_mask(LOG_GUEST_ERROR, - "%s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, - addr, REG_NAME(reg)); + "%s: %s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); break; } }; @@ -1997,7 +2002,7 @@ static void ot_lc_ctrl_load_transitions(OtLcCtrlState *s, len = strlen(s->trans_cfg[trans].state[ix]); /* each byte is encoding with two ASCII nibbles */ if (len != tdesc->word_count * sizeof(uint16_t) * 2u) { - qemu_log("%s %s %s %zu %zu\n", __func__, tdesc->name, + qemu_log("%s: %s: %s %s %zu %zu\n", __func__, s->ot_id, tdesc->name, TSTATE_NAME(ix), len, tdesc->word_count * sizeof(uint16_t)); error_setg(&err, "%s: %s invalid %s %s length\n", __func__, @@ -2101,7 +2106,7 @@ static void ot_lc_ctrl_configure_transitions( /* dump the generated transition tables */ lcval = table; for (unsigned tix = 0; tix < tdesc->step_count; tix++) { - qemu_log("%s: %s[%2u]", __func__, tdesc->name, tix); + qemu_log("%s: %s: %s[%2u]", __func__, s->ot_id, tdesc->name, tix); for (unsigned wix = 0; wix < tdesc->word_count; wix++) { qemu_log(" %04hx", *lcval++); }; @@ -2145,6 +2150,41 @@ static void ot_lc_ctrl_get_keymgr_div(const OtLcCtrlState *s, memcpy(&div->data[0], s->km_divs[s->km_div_type], OT_LC_KEYMGR_DIV_BYTES); } +static uint32_t +ot_lc_ctrl_get_soc_dbg_state(const OtLcCtrlState *s, unsigned state) +{ + g_assert(s->decode_soc_dbg); + + /* + * SoC debug controller is signalled with the OT_LC_CTRL_SOC_DBG interrupt + * line, with the decoded SoC debug state (OtSoCDbgState). In C code, this + * is defined as an enumeration. Valid constant values are initialized with + * the "soc_dbg_first" and "soc_dbg_last" properties and generated in + * #ot_lc_ctrl_configure_transitions. The encoded OTP values are loaded in + * #ot_lc_ctrl_load_otp_hw_cfg and decoded, if valid, into a OtSoCDbgState + * enumerated value. This feature is not deeply related to LC controller + * which only handles the Top-dependent SoC debug state value matrix and + * forward them to the Soc Debug Controller. + * However, the Soc Debug Controller now needs to expose the actual OTP + * encoded values onbly for debug purposes. + * This function converts back a OtSoCDbgState value into its actual + * top-specific encoding. + */ + uint32_t code; + + if (state < OT_LC_SOC_DBG_STATE_COUNT) { + code = (uint32_t)s->soc_dbgs[state][0u] | + ((uint32_t)s->soc_dbgs[state][1u]) << 16u; + } else { + error_report("%s: %s: unsupported Soc Dbg state %u", __func__, s->ot_id, + state); + code = UINT32_MAX; + } + + return code; +} + + static Property ot_lc_ctrl_properties[] = { DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtLcCtrlState, ot_id), DEFINE_PROP_LINK("otp-ctrl", OtLcCtrlState, otp_ctrl, TYPE_OT_OTP_IF, @@ -2176,11 +2216,11 @@ static Property ot_lc_ctrl_properties[] = { DEFINE_PROP_STRING("ownership_last", OtLcCtrlState, trans_cfg[LC_CTRL_TRANS_OWNERSHIP] .state[LC_CTRL_TSTATE_LAST]), - DEFINE_PROP_STRING("socdbg_first", OtLcCtrlState, - trans_cfg[LC_CTRL_TRANS_SOCDBG] + DEFINE_PROP_STRING("soc_dbg_first", OtLcCtrlState, + trans_cfg[LC_CTRL_TRANS_SOC_DBG] .state[LC_CTRL_TSTATE_FIRST]), - DEFINE_PROP_STRING("socdbg_last", OtLcCtrlState, - trans_cfg[LC_CTRL_TRANS_SOCDBG] + DEFINE_PROP_STRING("soc_dbg_last", OtLcCtrlState, + trans_cfg[LC_CTRL_TRANS_SOC_DBG] .state[LC_CTRL_TSTATE_LAST]), DEFINE_PROP_UINT16("silicon_creator_id", OtLcCtrlState, silicon_creator_id, 0), @@ -2188,7 +2228,7 @@ static Property ot_lc_ctrl_properties[] = { DEFINE_PROP_UINT8("revision_id", OtLcCtrlState, revision_id, 0), DEFINE_PROP_BOOL("volatile_raw_unlock", OtLcCtrlState, volatile_raw_unlock, true), - DEFINE_PROP_BOOL("socdbg", OtLcCtrlState, socdbg, false), + DEFINE_PROP_BOOL("decode_soc_dbg", OtLcCtrlState, decode_soc_dbg, false), DEFINE_PROP_UINT8("kmac-app", OtLcCtrlState, kmac_app, UINT8_MAX), DEFINE_PROP_END_OF_LIST(), }; @@ -2309,9 +2349,9 @@ static void ot_lc_ctrl_realize(DeviceState *dev, Error **errp) (uint16_t *)s->lc_transitions); ot_lc_ctrl_configure_transitions(s, LC_CTRL_TRANS_OWNERSHIP, (uint16_t *)s->ownerships); - if (s->socdbg) { - ot_lc_ctrl_configure_transitions(s, LC_CTRL_TRANS_SOCDBG, - (uint16_t *)s->socdbgs); + if (s->decode_soc_dbg) { + ot_lc_ctrl_configure_transitions(s, LC_CTRL_TRANS_SOC_DBG, + (uint16_t *)s->soc_dbgs); } ot_lc_ctrl_configure_km_div(s); ot_lc_ctrl_compute_predefined_tokens(s); @@ -2334,7 +2374,7 @@ static void ot_lc_ctrl_init(Object *obj) s->lc_transitions = g_new0(OtLcCtrlTransitionCountValue, NUM_LC_TRANSITION_COUNT); s->ownerships = g_new0(OtLcCtrlOwnershipValue, NUM_OWNERSHIP); - s->socdbgs = g_new0(OtLcCtrlSocDbgValue, NUM_SOCDBG); + s->soc_dbgs = g_new0(OtLcCtrlSocDbgValue, NUM_SOC_DBG); s->hashed_tokens = g_new0(OtOTPTokenValue, LC_TK_COUNT); for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { @@ -2346,8 +2386,9 @@ static void ot_lc_ctrl_init(Object *obj) } ibex_qdev_init_irq(obj, &s->pwc_lc_rsp, OT_PWRMGR_LC_RSP); - ibex_qdev_init_irq_default(obj, &s->socdbg_tx, OT_LC_CTRL_SOCDBG, - OT_SOCDBG_ST_COUNT); + ibex_qdev_init_irq_default(obj, &s->soc_dbg_tx, OT_LC_SOC_DBG, + /* initialize with an invalid level */ + OT_LC_SOC_DBG_STATE_COUNT); qdev_init_gpio_in_named(DEVICE(obj), &ot_lc_ctrl_pwr_lc_req, OT_PWRMGR_LC_REQ, 1); @@ -2379,6 +2420,7 @@ static void ot_lc_ctrl_class_init(ObjectClass *klass, void *data) &lc->parent_phases); lc->get_keymgr_div = &ot_lc_ctrl_get_keymgr_div; + lc->get_soc_dbg_state = &ot_lc_ctrl_get_soc_dbg_state; } static const TypeInfo ot_lc_ctrl_info = { diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index 9e74a4a66e790..6ad9dfa972192 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -211,13 +211,17 @@ typedef union { uint8_t lc_done:1; uint8_t escalate:1; /* escalation from alert handler */ uint8_t holdon_fetch:1; /* custom extension */ - uint8_t rom_good; /* up to 8 ROMs */ - uint8_t rom_done; /* up to 8 ROMs */ + /* clang-format off */ + uint8_t rom_good:OT_PWRMGR_MAX_ROM_COUNT; + uint8_t rom_done:OT_PWRMGR_MAX_ROM_COUNT; + /* clang-format on */ }; } OtPwrMgrEvents; static_assert(sizeof(OtPwrMgrEvents) == sizeof(uint32_t), "Invalid OtPwrMgrEvents definition"); +static_assert(sizeof(OtPwrMgrBootStatus) == sizeof(int), + "Invalid OtPwrMgrBootStatus size"); struct OtPwrMgrState { SysBusDevice parent_obj; @@ -472,30 +476,30 @@ static void ot_pwrmgr_cdc_sync(void *opaque) s->regs[R_CFG_CDC_SYNC] &= ~R_CFG_CDC_SYNC_SYNC_MASK; } -static void ot_pwrmgr_rom_good(void *opaque, int n, int level) +static void ot_pwrmgr_rom_good(void *opaque, int irq, int level) { OtPwrMgrState *s = opaque; - g_assert((unsigned)n < s->num_rom); + g_assert((unsigned)irq < s->num_rom); - trace_ot_pwrmgr_rom(s->ot_id, n, "good", level); + trace_ot_pwrmgr_rom(s->ot_id, irq, "good", level); if (level) { - s->fsm_events.rom_good |= 1u << n; + s->fsm_events.rom_good |= 1u << irq; ot_pwrmgr_schedule_fsm(s); } } -static void ot_pwrmgr_rom_done(void *opaque, int n, int level) +static void ot_pwrmgr_rom_done(void *opaque, int irq, int level) { OtPwrMgrState *s = opaque; - g_assert((unsigned)n < s->num_rom); + g_assert((unsigned)irq < s->num_rom); - trace_ot_pwrmgr_rom(s->ot_id, n, "done", level); + trace_ot_pwrmgr_rom(s->ot_id, irq, "done", level); if (level) { - s->fsm_events.rom_done |= 1u << n; + s->fsm_events.rom_done |= 1u << irq; ot_pwrmgr_schedule_fsm(s); } } @@ -644,8 +648,8 @@ static void ot_pwrmgr_fast_fsm_tick(OtPwrMgrState *s) ot_pwrmgr_clock_enable_all(s, true); break; case OT_PWR_FAST_ST_ENABLE_CLOCKS: - s->boot_status.main_ip_clk_en = 1u; - s->boot_status.io_ip_clk_en = 1u; + s->boot_status.main_clk_status = 1u; + s->boot_status.io_clk_status = 1u; ibex_irq_set(&s->boot_st, s->boot_status.i32); PWR_CHANGE_FAST_STATE(s, RELEASE_LC_RST); /* @@ -714,8 +718,8 @@ static void ot_pwrmgr_fast_fsm_tick(OtPwrMgrState *s) } break; case OT_PWR_FAST_ST_DIS_CLKS: - s->boot_status.main_ip_clk_en = 0u; - s->boot_status.io_ip_clk_en = 0u; + s->boot_status.main_clk_status = 0u; + s->boot_status.io_clk_status = 0u; ibex_irq_set(&s->boot_st, s->boot_status.i32); PWR_CHANGE_FAST_STATE(s, RESET_PREP); break; @@ -1028,6 +1032,7 @@ static void ot_pwrmgr_reset_enter(Object *obj, ResetType type) s->fsm_events.bitmap = 0; s->fsm_events.holdon_fetch = s->fetch_ctrl; s->boot_status.i32 = 0; + s->boot_status.rom_mask = (1u << s->num_rom) - 1u; ot_pwrmgr_sync_slow_regs(s); PWR_CHANGE_FAST_STATE(s, LOW_POWER); @@ -1073,7 +1078,7 @@ static void ot_pwrmgr_realize(DeviceState *dev, Error **errp) (int)PWRMGR_CONFIG[s->version].reset_count); if (s->num_rom) { - if (s->num_rom > 8u * sizeof(uint8_t)) { + if (s->num_rom > OT_PWRMGR_MAX_ROM_COUNT) { error_setg(&error_fatal, "%s: %s: too many ROMs\n", __func__, s->ot_id); g_assert_not_reached(); diff --git a/hw/opentitan/ot_soc_dbg_ctrl.c b/hw/opentitan/ot_soc_dbg_ctrl.c new file mode 100644 index 0000000000000..f08c7b85e3509 --- /dev/null +++ b/hw/opentitan/ot_soc_dbg_ctrl.c @@ -0,0 +1,1010 @@ +/* + * QEMU OpenTitan SoC Debug Controller + * + * Copyright (c) 2024-2025 Rivos, Inc. + * + * Author(s): + * Loïc Lefort + * Emmanuel Blot + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Based on OpenTitan c5507b4cdc + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_lc_ctrl.h" +#include "hw/opentitan/ot_pwrmgr.h" +#include "hw/opentitan/ot_soc_dbg_ctrl.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ibex_gpio.h" +#include "hw/riscv/ibex_irq.h" +#include "trace.h" + +#define NUM_ALERTS 2u + +/* clang-format off */ + +/* + * registers on core bus + * note: use shared register definition to avoid defining unreadable constants + */ +REG32(CORE_ALERT_TEST, 0x00u) + FIELD(CORE_ALERT_TEST, FATAL_FAULT, 0u, 1u) + FIELD(CORE_ALERT_TEST, RECOV_CTRL_UPDATE_ERR, 1u, 1u) +REG32(CORE_DEBUG_POLICY_VALID_SHADOWED, 0x04u) + SHARED_FIELD(DEBUG_POLICY_VALID, 0u, OT_MULTIBITBOOL4_WIDTH) +REG32(CORE_DEBUG_POLICY_CATEGORY_SHADOWED, 0x08u) + SHARED_FIELD(DEBUG_POLICY_CATEGORY, 0u, 7u) +REG32(CORE_DEBUG_POLICY_RELOCKED, 0x0cu) + SHARED_FIELD(DEBUG_POLICY_RELOCKED, 0u, OT_MULTIBITBOOL4_WIDTH) +REG32(CORE_TRACE_DEBUG_POLICY_CATEGORY, 0x10u) + SHARED_FIELD(TRACE_DEBUG_POLICY_CATEGORY, 0u, 7u) +REG32(CORE_TRACE_DEBUG_POLICY_VALID_RELOCKED, 0x14u) + SHARED_FIELD(TRACE_DEBUG_POLICY_VALID_RELOCKED_VALID, 0u, + OT_MULTIBITBOOL4_WIDTH) + SHARED_FIELD(TRACE_DEBUG_POLICY_VALID_RELOCKED_RELOCKED, 4u, + OT_MULTIBITBOOL4_WIDTH) +REG32(CORE_STATUS, 0x18u) + SHARED_FIELD(AUTH_DEBUG_INTENT_SET, 0u, 1u) + SHARED_FIELD(AUTH_WINDOW_OPEN, 4u, 1u) + SHARED_FIELD(AUTH_WINDOW_CLOSED, 5u, 1u) + SHARED_FIELD(AUTH_UNLOCK_SUCCESS, 6u, 1u) + SHARED_FIELD(AUTH_UNLOCK_FAILED, 7u, 1u) + +/* registers reachable from JTAG */ +REG32(JTAG_TRACE_DEBUG_POLICY_CATEGORY, 0x00u) +REG32(JTAG_TRACE_DEBUG_POLICY_VALID_RELOCKED, 0x04u) +REG32(JTAG_CONTROL, 0x08u) + FIELD(JTAG_CONTROL, BOOT_CONTINUE, 0u, 1u) +REG32(JTAG_STATUS, 0x0cu) +REG32(JTAG_BOOT_STATUS, 0x10u) + FIELD(JTAG_BOOT_STATUS, MAIN_CLK_STATUS, 0u, 1u) + FIELD(JTAG_BOOT_STATUS, IO_CLK_STATUS, 1u, 1u) + FIELD(JTAG_BOOT_STATUS, OTP_DONE, 2u, 1u) + FIELD(JTAG_BOOT_STATUS, LC_DONE, 3u, 1u) + FIELD(JTAG_BOOT_STATUS, CPU_FETCH_EN, 4u, 1u) + FIELD(JTAG_BOOT_STATUS, HALT_FSM_STATE, 5u, 6u) + FIELD(JTAG_BOOT_STATUS, BOOT_GREENLIGHT_DONE, 11u, 3u) + FIELD(JTAG_BOOT_STATUS, BOOT_GREENLIGHT_GOOD, 14u, 3u) +REG32(JTAG_TRACE_SOC_DBG_STATE, 0x14u) + +/* clang-format on */ + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) + +#define R_CORE_LAST_REG (R_CORE_STATUS) +#define REGS_CORE_COUNT (R_CORE_LAST_REG + 1u) +#define REGS_CORE_SIZE (REGS_CORE_COUNT * sizeof(uint32_t)) + +#define R_JTAG_LAST_REG (R_JTAG_TRACE_SOC_DBG_STATE) +#define REGS_JTAG_COUNT (R_JTAG_LAST_REG + 1u) +#define REGS_JTAG_SIZE (REGS_JTAG_COUNT * sizeof(uint32_t)) + +#define CORE_ALERT_TEST_WMASK \ + (R_CORE_ALERT_TEST_FATAL_FAULT_MASK | \ + R_CORE_ALERT_TEST_RECOV_CTRL_UPDATE_ERR_MASK) +#define CORE_STATUS_WMASK \ + (AUTH_DEBUG_INTENT_SET_MASK | AUTH_WINDOW_OPEN_MASK | \ + AUTH_WINDOW_CLOSED_MASK | AUTH_UNLOCK_SUCCESS_MASK | \ + AUTH_UNLOCK_FAILED_MASK) +#define BOOT_ROM_MASK \ + ((1u << (R_JTAG_BOOT_STATUS_BOOT_GREENLIGHT_DONE_LENGTH)) - 1u) + +enum { + CONTINUE_CPU_BOOT_GOOD, + CONTINUE_CPU_BOOT_DONE, + CONTINUE_CPU_BOOT_COUNT, +} OtSoCDbgContinueCpuBoot; + +typedef enum { + ST_IDLE, + ST_CHECK_LC_STATE, + ST_WAIT4DFT_EN, + ST_CHECK_HALT_PIN, + ST_CHECK_JTAG_GO, + ST_HALT_DONE, + ST_COUNT, +} OtSoCDbgCtrlFsmState; + +typedef enum { + ALERT_FATAL, + ALERT_RECOV, + ALERT_COUNT, +} OtSoCDbgAlert; + +typedef enum { + ALERT_RECOV_POLICY_VALID, + ALERT_RECOV_POLICY_CATEGORY, + ALERT_RECOV_COUNT, +} OtSoCDbgRecovAlert; + +typedef enum { + OT_SOC_DBG_CATEGORY_LOCKED = 0b1010000, + OT_SOC_DBG_CATEGORY_2 = 0b1001101, + OT_SOC_DBG_CATEGORY_3 = 0b0001010, + OT_SOC_DBG_CATEGORY_4 = 0b1100011, +} OtSocDbgCategory; + +typedef struct { + /* can be any value, comes either from a register or local value */ + uint8_t cat; + /* can be any 4-bit value, from a register or local value */ + ot_mb4_t relocked; + /* only local, a regular bool is sufficient */ + bool valid; +} OtSocDbgPolicy; + +typedef struct { + struct { + OtShadowReg valid; + OtShadowReg category; + uint32_t relocked; + } debug_policy; + uint32_t status; +} OtSoCDbgCtrlRegs; + +struct OtSoCDbgCtrlState { + SysBusDevice parent_obj; + + MemoryRegion core; + MemoryRegion jtag; + IbexIRQ alerts[ALERT_COUNT]; + IbexIRQ debug_policy; + IbexIRQ continue_cpu_boot[CONTINUE_CPU_BOOT_COUNT]; + QEMUBH *fsm_tick_bh; + + OtSoCDbgCtrlRegs regs; + OtSoCDbgCtrlFsmState fsm_state; + OtSoCDbgState soc_dbg_state; + OtSocDbgPolicy soc_dbg_policy; /* calculated policy */ + unsigned fsm_tick_count; + OtPwrMgrBootStatus pwr_boot_status; + uint16_t lc_broadcast_bm; /* OtLcCtrlBroadcast fields */ + uint8_t fatal_alert_bm; + uint8_t recov_alert_bm; + bool halt_cpu_boot; + bool boot_continue; + + char *ot_id; + OtLcCtrlState *lc_ctrl; + bool dft_ignore; +}; + +struct OtSoCDbgCtrlClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + +/* clang-format off */ +static const uint8_t OT_SOC_DBG_FSM_STATE[ST_COUNT] = { + [ST_IDLE] = 0b101000u, + [ST_CHECK_LC_STATE] = 0b011101u, + [ST_WAIT4DFT_EN] = 0b000110u, + [ST_CHECK_HALT_PIN] = 0b110011u, + [ST_CHECK_JTAG_GO] = 0b111110u, + [ST_HALT_DONE] = 0b100101u, +}; +/* clang-format on */ + +static_assert(ALERT_COUNT == NUM_ALERTS, "Invalid alert count"); +static_assert(sizeof(OtSocDbgDebugPolicy) == sizeof(int), + "Invalid OtSocDbgDebugPolicy size"); +static_assert(OT_SOC_DBG_ST_COUNT == OT_LC_SOC_DBG_STATE_COUNT, + "Invalid SoC Debug state count"); + +#define ROM_MASK ((1u << (R_BOOT_STATUS_ROM_CTRL_DONE_LENGTH - 1u)) - 1u) + +#define REG_NAME(_kind_, _reg_) \ + ((((_reg_) <= REGS_##_kind_##_COUNT) && REG_##_kind_##_NAMES[_reg_]) ? \ + REG_##_kind_##_NAMES[_reg_] : \ + "?") + +#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) + +static const char *REG_CORE_NAMES[REGS_CORE_COUNT] = { + /* clang-format off */ + REG_NAME_ENTRY(CORE_ALERT_TEST), + REG_NAME_ENTRY(CORE_DEBUG_POLICY_VALID_SHADOWED), + REG_NAME_ENTRY(CORE_DEBUG_POLICY_CATEGORY_SHADOWED), + REG_NAME_ENTRY(CORE_DEBUG_POLICY_RELOCKED), + REG_NAME_ENTRY(CORE_TRACE_DEBUG_POLICY_CATEGORY), + REG_NAME_ENTRY(CORE_TRACE_DEBUG_POLICY_VALID_RELOCKED), + REG_NAME_ENTRY(CORE_STATUS), + /* clang-format on */ +}; + +static const char *REG_JTAG_NAMES[REGS_JTAG_COUNT] = { + /* clang-format off */ + REG_NAME_ENTRY(JTAG_TRACE_DEBUG_POLICY_CATEGORY), + REG_NAME_ENTRY(JTAG_TRACE_DEBUG_POLICY_VALID_RELOCKED), + REG_NAME_ENTRY(JTAG_CONTROL), + REG_NAME_ENTRY(JTAG_STATUS), + REG_NAME_ENTRY(JTAG_BOOT_STATUS), + REG_NAME_ENTRY(JTAG_TRACE_SOC_DBG_STATE), + /* clang-format on */ +}; +#undef REG_NAME_ENTRY + +#define LC_NAME_ENTRY(_st_) [_st_] = stringify(_st_) +static const char *LC_BROADCAST_NAMES[] = { + /* clang-format off */ + LC_NAME_ENTRY(OT_LC_RAW_TEST_RMA), + LC_NAME_ENTRY(OT_LC_DFT_EN), + LC_NAME_ENTRY(OT_LC_NVM_DEBUG_EN), + LC_NAME_ENTRY(OT_LC_HW_DEBUG_EN), + LC_NAME_ENTRY(OT_LC_CPU_EN), + LC_NAME_ENTRY(OT_LC_KEYMGR_EN), + LC_NAME_ENTRY(OT_LC_ESCALATE_EN), + LC_NAME_ENTRY(OT_LC_CHECK_BYP_EN), + LC_NAME_ENTRY(OT_LC_CREATOR_SEED_SW_RW_EN), + LC_NAME_ENTRY(OT_LC_OWNER_SEED_SW_RW_EN), + LC_NAME_ENTRY(OT_LC_ISO_PART_SW_RD_EN), + LC_NAME_ENTRY(OT_LC_ISO_PART_SW_WR_EN), + LC_NAME_ENTRY(OT_LC_SEED_HW_RD_EN), + LC_NAME_ENTRY(OT_LC_RMA), + /* clang-format on */ +}; +#undef LC_NAME_ENTRY + +#define LC_BCAST_NAME(_bit_) \ + (((unsigned)(_bit_)) < ARRAY_SIZE(LC_BROADCAST_NAMES) ? \ + LC_BROADCAST_NAMES[(_bit_)] : \ + "?") + +#define SOC_DBG_NAME_ENTRY(_st_) [OT_SOC_DBG_ST_##_st_] = stringify(_st_) +static const char *SOC_DBG_NAMES[] = { + /* clang-format off */ + SOC_DBG_NAME_ENTRY(BLANK), + SOC_DBG_NAME_ENTRY(PRE_PROD), + SOC_DBG_NAME_ENTRY(PROD), + /* clang-format on */ +}; +#undef SOC_DBG_NAME_ENTRY + +#define SOC_DBG_NAME(_st_) \ + (((unsigned)(_st_)) < ARRAY_SIZE(SOC_DBG_NAMES) ? SOC_DBG_NAMES[(_st_)] : \ + "?") + +#define STATE_NAME_ENTRY(_st_) [ST_##_st_] = stringify(_st_) +static const char *STATE_NAMES[ST_COUNT] = { + /* clang-format off */ + STATE_NAME_ENTRY(IDLE), + STATE_NAME_ENTRY(CHECK_LC_STATE), + STATE_NAME_ENTRY(WAIT4DFT_EN), + STATE_NAME_ENTRY(CHECK_HALT_PIN), + STATE_NAME_ENTRY(CHECK_JTAG_GO), + STATE_NAME_ENTRY(HALT_DONE), + /* clang-format on */ +}; +#undef STATE_NAME_ENTRY +#define STATE_NAME(_st_) \ + ((_st_) >= 0 && (_st_) < ARRAY_SIZE(STATE_NAMES) ? STATE_NAMES[(_st_)] : \ + "?") +#define CHANGE_STATE(_s_, _st_) \ + ot_soc_dbg_ctrl_change_state_line(_s_, ST_##_st_, __LINE__) +#define SCHEDULE_FSM(_s_) ot_soc_dbg_ctrl_schedule_fsm(_s_, __func__, __LINE__) + +static void ot_soc_dbg_ctrl_change_state_line( + OtSoCDbgCtrlState *s, OtSoCDbgCtrlFsmState state, int line) +{ + trace_ot_soc_dbg_ctrl_change_state(s->ot_id, line, STATE_NAME(s->fsm_state), + s->fsm_state, STATE_NAME(state), state); + + s->fsm_state = state; +} + +static inline bool +ot_soc_dbg_ctrl_lc_test(const OtSoCDbgCtrlState *s, OtLcCtrlBroadcast lc_bc) +{ + return (bool)(s->lc_broadcast_bm & (1u << (unsigned)lc_bc)); +} + +static void +ot_soc_dbg_ctrl_update_alerts(OtSoCDbgCtrlState *s, uint32_t test_bm) +{ + bool alert; + + alert = (bool)s->fatal_alert_bm | + (bool)(test_bm & R_CORE_ALERT_TEST_FATAL_FAULT_MASK); + ibex_irq_set(&s->alerts[ALERT_FATAL], (int)alert); + + alert = (bool)s->recov_alert_bm | + (bool)(test_bm & R_CORE_ALERT_TEST_RECOV_CTRL_UPDATE_ERR_MASK); + ibex_irq_set(&s->alerts[ALERT_RECOV], (int)alert); + + if (test_bm) { + /* alert test is transient */ + ibex_irq_set(&s->alerts[ALERT_FATAL], (int)(bool)s->fatal_alert_bm); + ibex_irq_set(&s->alerts[ALERT_RECOV], (int)(bool)s->recov_alert_bm); + } +} + +static void +ot_soc_dbg_ctrl_schedule_fsm(OtSoCDbgCtrlState *s, const char *func, int line) +{ + s->fsm_tick_count += 1u; + trace_ot_soc_dbg_ctrl_schedule_fsm(s->ot_id, func, line, s->fsm_tick_count); + qemu_bh_schedule(s->fsm_tick_bh); +} + +static void ot_soc_dbg_ctrl_tick_fsm(OtSoCDbgCtrlState *s) +{ + bool cpu_boot_done = false; + + trace_ot_soc_dbg_ctrl_tick_fsm(s->ot_id, STATE_NAME(s->fsm_state), + s->lc_broadcast_bm, s->dft_ignore, + s->pwr_boot_status.lc_done, s->halt_cpu_boot, + s->boot_continue); + + switch (s->fsm_state) { + case ST_IDLE: + if (s->pwr_boot_status.lc_done) { + CHANGE_STATE(s, CHECK_LC_STATE); + } + break; + case ST_CHECK_LC_STATE: + if ((s->lc_broadcast_bm & (1u << OT_LC_RAW_TEST_RMA)) && + !s->dft_ignore) { + CHANGE_STATE(s, WAIT4DFT_EN); + } else { + CHANGE_STATE(s, HALT_DONE); + } + break; + case ST_WAIT4DFT_EN: + if (s->lc_broadcast_bm & (1u << OT_LC_DFT_EN)) { + CHANGE_STATE(s, CHECK_HALT_PIN); + } + break; + case ST_CHECK_HALT_PIN: + if (s->halt_cpu_boot) { + CHANGE_STATE(s, CHECK_JTAG_GO); + } else { + CHANGE_STATE(s, HALT_DONE); + } + break; + case ST_CHECK_JTAG_GO: + if (s->boot_continue) { + CHANGE_STATE(s, HALT_DONE); + } + break; + case ST_HALT_DONE: + cpu_boot_done = true; + break; + default: + /* + * it does not seem there is a special state for this case, i.e. + * the FSM does not enter a special error state, it only raises + * an alert. + */ + ibex_irq_set(&s->alerts[ALERT_FATAL], (int)true); + return; + } + + /* as with PwrMgr, use simple boolean value, not MuBi4 */ + int cpu_boot_done_i = (int)cpu_boot_done; + if (ibex_irq_get_level(&s->continue_cpu_boot[CONTINUE_CPU_BOOT_COUNT]) != + cpu_boot_done_i) { + trace_ot_soc_dbg_ctrl_cpu_boot_done(s->ot_id, cpu_boot_done_i); + } + ibex_irq_set(&s->continue_cpu_boot[CONTINUE_CPU_BOOT_DONE], + cpu_boot_done_i); +} + +static void ot_soc_dbg_ctrl_update_policy(OtSoCDbgCtrlState *s) +{ + OtSocDbgPolicy policy; + + ot_mb4_t relocked = + (ot_mb4_t)SHARED_FIELD_EX32(s->regs.debug_policy.relocked, + DEBUG_POLICY_RELOCKED); + + switch (s->soc_dbg_state) { + case OT_SOC_DBG_ST_BLANK: + policy.cat = ot_soc_dbg_ctrl_lc_test(s, OT_LC_DFT_EN) || + ot_soc_dbg_ctrl_lc_test(s, OT_LC_HW_DEBUG_EN) ? + OT_SOC_DBG_CATEGORY_4 : + OT_SOC_DBG_CATEGORY_LOCKED; + policy.valid = (bool)s->pwr_boot_status.lc_done; + policy.relocked = OT_MULTIBITBOOL4_FALSE; + break; + case OT_SOC_DBG_ST_PRE_PROD: + policy.cat = OT_SOC_DBG_CATEGORY_4; + policy.valid = (bool)s->pwr_boot_status.lc_done; + policy.relocked = OT_MULTIBITBOOL4_FALSE; + break; + case OT_SOC_DBG_ST_PROD: + if (ot_soc_dbg_ctrl_lc_test(s, OT_LC_RMA)) { + policy.cat = OT_SOC_DBG_CATEGORY_4; + policy.valid = true; + policy.relocked = OT_MULTIBITBOOL4_FALSE; + } else if (ot_soc_dbg_ctrl_lc_test(s, OT_LC_CPU_EN)) { + policy.cat = ot_shadow_reg_peek(&s->regs.debug_policy.category); + policy.valid = true; + policy.relocked = relocked; + } else { + policy.cat = OT_SOC_DBG_CATEGORY_LOCKED; + policy.valid = (bool)s->pwr_boot_status.lc_done; + policy.relocked = OT_MULTIBITBOOL4_FALSE; + } + break; + default: + policy.cat = OT_SOC_DBG_CATEGORY_LOCKED; + policy.valid = false; + policy.relocked = OT_MULTIBITBOOL4_FALSE; + break; + } + + /* detect valid rising edge */ + if (policy.valid && !s->soc_dbg_policy.valid) { + /* decode the values into the combined output signal */ + OtSocDbgDebugPolicy debug_policy = { + .cat_bm = 0u, + .relocked = policy.relocked == OT_MULTIBITBOOL4_TRUE, + }; + + if (policy.cat == OT_SOC_DBG_CATEGORY_4) { + debug_policy.cat_bm |= 1u << 4u; + } + if (!relocked) { + if (policy.cat == OT_SOC_DBG_CATEGORY_3 || + (debug_policy.cat_bm & (1u << 4u))) { + debug_policy.cat_bm |= 1u << 3u; + } + if (policy.cat == OT_SOC_DBG_CATEGORY_2 || + (debug_policy.cat_bm & ((1u << 4u) | (1u << 3u)))) { + debug_policy.cat_bm |= 1u << 2u; + } + } + + int prev_policy = ibex_irq_get_level(&s->debug_policy); + if (prev_policy != debug_policy.i32) { + trace_ot_soc_dbg_ctrl_update_policy(s->ot_id, + SOC_DBG_NAME(s->soc_dbg_state), + STATE_NAME(s->fsm_state), + debug_policy.cat_bm, relocked); + } + ibex_irq_set(&s->debug_policy, debug_policy.i32); + + /* store current policy for edge detection and trace registers */ + s->soc_dbg_policy = policy; + } else { + /* store only valid level for edge detection */ + s->soc_dbg_policy.valid = policy.valid; + } +} + +static void ot_soc_dbg_ctrl_fsm_tick(void *opaque) +{ + OtSoCDbgCtrlState *s = opaque; + + OtSoCDbgCtrlFsmState fsm_state = s->fsm_state; + g_assert(s->fsm_tick_count); + while (s->fsm_tick_count) { + ot_soc_dbg_ctrl_update_policy(s); + s->fsm_tick_count--; + ot_soc_dbg_ctrl_tick_fsm(s); + } + if (fsm_state != s->fsm_state) { + /* schedule FSM update once more if its state has changed */ + SCHEDULE_FSM(s); + } +} + + +static void ot_soc_dbg_ctrl_halt_cpu_boot(void *opaque, int n, int level) +{ + OtSoCDbgCtrlState *s = opaque; + + g_assert(n == 0); + + trace_ot_soc_dbg_ctrl_rcv(s->ot_id, "HALT_CPU_BOOT", 0, + ibex_gpio_repr(level)); + + /* expect an Ibex GPIO signal */ + g_assert(ibex_gpio_check(level)); + + /* active low */ + s->halt_cpu_boot = !ibex_gpio_level(level); + + SCHEDULE_FSM(s); +} + +static void ot_soc_dbg_ctrl_lc_broadcast(void *opaque, int n, int level) +{ + OtSoCDbgCtrlState *s = opaque; + + unsigned bcast = (unsigned)n; + g_assert(bcast < OT_LC_BROADCAST_COUNT); + g_assert(!ibex_gpio_check(level)); + + trace_ot_soc_dbg_ctrl_rcv(s->ot_id, LC_BCAST_NAME(bcast), bcast, + level ? '1' : '0'); + + switch (n) { + case OT_LC_RAW_TEST_RMA: + case OT_LC_DFT_EN: + case OT_LC_HW_DEBUG_EN: + case OT_LC_CPU_EN: + if (level) { + s->lc_broadcast_bm |= (1u << bcast); + } else { + s->lc_broadcast_bm &= (~1u << bcast); + } + break; + /* NOLINTBEGIN(bugprone-branch-clone) */ + case OT_LC_NVM_DEBUG_EN: + case OT_LC_KEYMGR_EN: + case OT_LC_ISO_PART_SW_RD_EN: + case OT_LC_ISO_PART_SW_WR_EN: + case OT_LC_OWNER_SEED_SW_RW_EN: + /* do not seem to be routed... */ + break; + case OT_LC_CREATOR_SEED_SW_RW_EN: + case OT_LC_SEED_HW_RD_EN: + case OT_LC_ESCALATE_EN: + case OT_LC_CHECK_BYP_EN: + /* verbatim from RTL: "Use unused signals to make lint clean" */ + break; + /* NOLINTEND(bugprone-branch-clone) */ + default: + g_assert_not_reached(); + } + + SCHEDULE_FSM(s); +} + +static void ot_soc_dbg_ctrl_boot_status(void *opaque, int n, int level) +{ + OtSoCDbgCtrlState *s = opaque; + + g_assert(n == 0); + + OtPwrMgrBootStatus bs = { .i32 = level }; + + g_assert(bs.rom_mask != 0); + g_assert(!(bs.rom_mask & ~BOOT_ROM_MASK)); + + trace_ot_soc_dbg_ctrl_boot_status(s->ot_id, (bool)bs.main_clk_status, + (bool)bs.io_clk_status, (bool)bs.otp_done, + (bool)bs.lc_done, (bool)bs.cpu_fetch_en, + bs.rom_done & bs.rom_mask, + bs.rom_good & bs.rom_mask); + s->pwr_boot_status = bs; + + SCHEDULE_FSM(s); +} + +static uint32_t ot_soc_dbg_ctrl_get_jtag_boot_status(const OtSoCDbgCtrlState *s) +{ + const OtPwrMgrBootStatus *bs = &s->pwr_boot_status; + + g_assert(s->fsm_state < ST_COUNT); + + uint32_t bs_bm = 0; + + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, MAIN_CLK_STATUS, + bs->main_clk_status); + bs_bm = + FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, IO_CLK_STATUS, bs->io_clk_status); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, OTP_DONE, bs->otp_done); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, LC_DONE, bs->lc_done); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, BOOT_GREENLIGHT_DONE, + bs->rom_done & bs->rom_mask); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, BOOT_GREENLIGHT_GOOD, + bs->rom_good & bs->rom_mask); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, CPU_FETCH_EN, bs->cpu_fetch_en); + bs_bm = FIELD_DP32(bs_bm, JTAG_BOOT_STATUS, HALT_FSM_STATE, + (uint32_t)OT_SOC_DBG_FSM_STATE[s->fsm_state]); + + return bs_bm; +} + +static void ot_soc_dbg_ctrl_soc_dbg_state(void *opaque, int n, int level) +{ + OtSoCDbgCtrlState *s = opaque; + + g_assert(n == 0); + g_assert(!ibex_gpio_check(level)); + + switch (level) { + case 0: + s->soc_dbg_state = OT_SOC_DBG_ST_BLANK; + break; + case 1: + s->soc_dbg_state = OT_SOC_DBG_ST_PRE_PROD; + break; + case 2: + s->soc_dbg_state = OT_SOC_DBG_ST_PROD; + break; + default: + g_assert_not_reached(); + } + + trace_ot_soc_dbg_ctrl_rcv(s->ot_id, "SOC_DBG_STATE", 0, + (char)('0' + level)); + + trace_ot_soc_dbg_ctrl_soc_dbg_state(s->ot_id, + SOC_DBG_NAME(s->soc_dbg_state)); + + SCHEDULE_FSM(s); +} + +static uint64_t +ot_soc_dbg_ctrl_core_read(void *opaque, hwaddr addr, unsigned size) +{ + OtSoCDbgCtrlState *s = opaque; + (void)size; + uint32_t val32 = 0; + + hwaddr reg = R32_OFF(addr); + switch (reg) { + case R_CORE_DEBUG_POLICY_VALID_SHADOWED: + val32 = ot_shadow_reg_read(&s->regs.debug_policy.valid); + break; + case R_CORE_DEBUG_POLICY_CATEGORY_SHADOWED: + val32 = ot_shadow_reg_read(&s->regs.debug_policy.category); + break; + case R_CORE_DEBUG_POLICY_RELOCKED: + val32 = s->regs.debug_policy.relocked; + break; + case R_CORE_TRACE_DEBUG_POLICY_CATEGORY: + val32 = (uint32_t)s->soc_dbg_policy.cat; + break; + case R_CORE_TRACE_DEBUG_POLICY_VALID_RELOCKED: + val32 = (uint32_t)s->soc_dbg_policy.relocked; + break; + case R_CORE_STATUS: + val32 = s->regs.status; + break; + case R_CORE_ALERT_TEST: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: W/O register 0x%02x (%s)\n", + __func__, s->ot_id, (uint32_t)addr, REG_NAME(CORE, reg)); + val32 = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Bad offset 0x%02x\n", __func__, + s->ot_id, (uint32_t)addr); + val32 = 0; + break; + } + + uint32_t pc = ibex_get_current_pc(); + trace_ot_soc_dbg_ctrl_core_io_read_out(s->ot_id, (uint32_t)addr, + REG_NAME(CORE, reg), val32, pc); + + return (uint32_t)val32; +} + +static void ot_soc_dbg_ctrl_core_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OtSoCDbgCtrlState *s = opaque; + (void)size; + uint32_t val32 = (uint32_t)value; + + hwaddr reg = R32_OFF(addr); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_soc_dbg_ctrl_core_io_write(s->ot_id, (uint32_t)addr, + REG_NAME(CORE, reg), val32, pc); + + switch (reg) { + case R_CORE_ALERT_TEST: + val32 &= CORE_ALERT_TEST_WMASK; + ot_soc_dbg_ctrl_update_alerts(s, val32); + break; + case R_CORE_DEBUG_POLICY_VALID_SHADOWED: + val32 &= DEBUG_POLICY_VALID_MASK; + switch (ot_shadow_reg_write(&s->regs.debug_policy.valid, val32)) { + case OT_SHADOW_REG_STAGED: + case OT_SHADOW_REG_COMMITTED: + s->recov_alert_bm &= ~(1u << ALERT_RECOV_POLICY_VALID); + break; + case OT_SHADOW_REG_ERROR: + s->recov_alert_bm |= 1u << ALERT_RECOV_POLICY_VALID; + default: + break; + } + ot_soc_dbg_ctrl_update_alerts(s, 0u); + break; + case R_CORE_DEBUG_POLICY_CATEGORY_SHADOWED: + val32 &= DEBUG_POLICY_CATEGORY_MASK; + if (ot_shadow_reg_peek(&s->regs.debug_policy.valid) == + OT_MULTIBITBOOL4_FALSE) { + /* debug policy not yet validated, can still update it */ + switch ( + ot_shadow_reg_write(&s->regs.debug_policy.category, val32)) { + case OT_SHADOW_REG_STAGED: + case OT_SHADOW_REG_COMMITTED: + s->recov_alert_bm &= ~(1u << ALERT_RECOV_POLICY_CATEGORY); + break; + case OT_SHADOW_REG_ERROR: + s->recov_alert_bm |= 1u << ALERT_RECOV_POLICY_CATEGORY; + default: + break; + } + ot_soc_dbg_ctrl_update_alerts(s, 0u); + } + break; + case R_CORE_DEBUG_POLICY_RELOCKED: + val32 &= DEBUG_POLICY_RELOCKED_MASK; + s->regs.debug_policy.relocked = val32; + break; + case R_CORE_TRACE_DEBUG_POLICY_CATEGORY: + case R_CORE_TRACE_DEBUG_POLICY_VALID_RELOCKED: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: R/O register 0x%02x (%s)\n", + __func__, s->ot_id, (uint32_t)addr, REG_NAME(CORE, reg)); + break; + case R_CORE_STATUS: + /* + * this register does nothing but masks some bits and make them + * readable from JTAG side. + */ + val32 &= CORE_STATUS_WMASK; + s->regs.status = val32; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Bad offset 0x%02x\n", __func__, + s->ot_id, (uint32_t)addr); + break; + } +} + +static uint64_t +ot_soc_dbg_ctrl_jtag_read(void *opaque, hwaddr addr, unsigned size) +{ + OtSoCDbgCtrlState *s = opaque; + (void)size; + uint32_t val32 = 0; + + hwaddr reg = R32_OFF(addr); + switch (reg) { + case R_JTAG_TRACE_DEBUG_POLICY_CATEGORY: + val32 = (uint32_t)s->soc_dbg_policy.cat; + break; + case R_JTAG_TRACE_DEBUG_POLICY_VALID_RELOCKED: + val32 = (uint32_t)s->soc_dbg_policy.relocked; + break; + case R_JTAG_CONTROL: + val32 = s->boot_continue ? R_JTAG_CONTROL_BOOT_CONTINUE_MASK : 0u; + break; + case R_JTAG_STATUS: + val32 = s->regs.status; + break; + case R_JTAG_BOOT_STATUS: + if (ot_soc_dbg_ctrl_lc_test(s, OT_LC_DFT_EN)) { + val32 = ot_soc_dbg_ctrl_get_jtag_boot_status(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: BootStatus disabled (no DFT)\n", __func__, + s->ot_id); + val32 = 0; + } + break; + case R_JTAG_TRACE_SOC_DBG_STATE: + if (ot_soc_dbg_ctrl_lc_test(s, OT_LC_DFT_EN)) { + OtLcCtrlClass *lc = OT_LC_CTRL_GET_CLASS(s->lc_ctrl); + val32 = + lc->get_soc_dbg_state(s->lc_ctrl, (unsigned)s->soc_dbg_state); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: SocDbgState disabled (no DFT)\n", __func__, + s->ot_id); + val32 = 0u; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Bad offset 0x%02x\n", __func__, + s->ot_id, (uint32_t)addr); + val32 = 0; + break; + } + + trace_ot_soc_dbg_ctrl_jtag_io_read_out(s->ot_id, (uint32_t)addr, + REG_NAME(JTAG, reg), val32); + + return (uint32_t)val32; +} + +static void ot_soc_dbg_ctrl_jtag_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + OtSoCDbgCtrlState *s = opaque; + (void)size; + uint32_t val32 = (uint32_t)value; + + hwaddr reg = R32_OFF(addr); + + trace_ot_soc_dbg_ctrl_jtag_io_write(s->ot_id, (uint32_t)addr, + REG_NAME(JTAG, reg), val32); + + switch (reg) { + case R_JTAG_CONTROL: + s->boot_continue = (bool)(val32 & R_JTAG_CONTROL_BOOT_CONTINUE_MASK); + SCHEDULE_FSM(s); + break; + case R_JTAG_TRACE_DEBUG_POLICY_CATEGORY: + case R_JTAG_TRACE_DEBUG_POLICY_VALID_RELOCKED: + case R_JTAG_STATUS: + case R_JTAG_BOOT_STATUS: + case R_JTAG_TRACE_SOC_DBG_STATE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: R/O register 0x%02x (%s)\n", + __func__, s->ot_id, (uint32_t)addr, REG_NAME(JTAG, reg)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s, Bad offset 0x%02x\n", __func__, + s->ot_id, (uint32_t)addr); + break; + } +} + +static Property ot_soc_dbg_ctrl_properties[] = { + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSoCDbgCtrlState, ot_id), + DEFINE_PROP_LINK("lc-ctrl", OtSoCDbgCtrlState, lc_ctrl, TYPE_OT_LC_CTRL, + OtLcCtrlState *), + DEFINE_PROP_BOOL("dft-ignore", OtSoCDbgCtrlState, dft_ignore, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static const MemoryRegionOps ot_soc_dbg_ctrl_core_ops = { + .read = &ot_soc_dbg_ctrl_core_read, + .write = &ot_soc_dbg_ctrl_core_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static const MemoryRegionOps ot_soc_dbg_ctrl_jtag_ops = { + .read = &ot_soc_dbg_ctrl_jtag_read, + .write = &ot_soc_dbg_ctrl_jtag_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static void ot_soc_dbg_ctrl_reset_enter(Object *obj, ResetType type) +{ + OtSoCDbgCtrlClass *c = OT_SOC_DBG_CTRL_GET_CLASS(obj); + OtSoCDbgCtrlState *s = OT_SOC_DBG_CTRL(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + ot_shadow_reg_init(&s->regs.debug_policy.valid, OT_MULTIBITBOOL4_FALSE); + ot_shadow_reg_init(&s->regs.debug_policy.category, + OT_SOC_DBG_CATEGORY_LOCKED); + s->regs.debug_policy.relocked = OT_MULTIBITBOOL4_FALSE; + + ibex_irq_set(&s->continue_cpu_boot[CONTINUE_CPU_BOOT_DONE], (int)false); + ibex_irq_set(&s->debug_policy, 0); + + memset(&s->soc_dbg_policy, 0, sizeof(s->soc_dbg_policy)); + + CHANGE_STATE(s, IDLE); + s->fsm_state = ST_IDLE; + s->fsm_tick_count = 0u; + s->pwr_boot_status.i32 = 0; + s->lc_broadcast_bm = 0u; + s->soc_dbg_state = OT_SOC_DBG_ST_PROD; + s->halt_cpu_boot = false; + s->boot_continue = false; + s->fatal_alert_bm = 0u; + s->recov_alert_bm = 0u; + + ot_soc_dbg_ctrl_update_alerts(s, 0u); +} + +static void ot_soc_dbg_ctrl_reset_exit(Object *obj, ResetType type) +{ + OtSoCDbgCtrlClass *c = OT_SOC_DBG_CTRL_GET_CLASS(obj); + OtSoCDbgCtrlState *s = OT_SOC_DBG_CTRL(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + qemu_bh_cancel(s->fsm_tick_bh); + + /* this fake ROM signal is hardcoded to true and never updated. */ + ibex_irq_set(&s->continue_cpu_boot[CONTINUE_CPU_BOOT_GOOD], (int)true); + + ot_soc_dbg_ctrl_update_policy(s); + + SCHEDULE_FSM(s); +} + +static void ot_soc_dbg_ctrl_realize(DeviceState *dev, Error **errp) +{ + OtSoCDbgCtrlState *s = OT_SOC_DBG_CTRL(dev); + (void)errp; + + g_assert(s->ot_id); + g_assert(s->lc_ctrl); +} + +static void ot_soc_dbg_ctrl_init(Object *obj) +{ + OtSoCDbgCtrlState *s = OT_SOC_DBG_CTRL(obj); + + memory_region_init_io(&s->core, obj, &ot_soc_dbg_ctrl_core_ops, s, + TYPE_OT_SOC_DBG_CTRL, REGS_CORE_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->core); + + memory_region_init_io(&s->jtag, obj, &ot_soc_dbg_ctrl_jtag_ops, s, + TYPE_OT_SOC_DBG_CTRL, REGS_JTAG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->jtag); + + for (unsigned ix = 0; ix < NUM_ALERTS; ix++) { + ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); + } + ibex_qdev_init_irq(obj, &s->debug_policy, OT_SOC_DBG_DEBUG_POLICY); + ibex_qdev_init_irqs(obj, s->continue_cpu_boot, OT_SOC_DBG_CONTINUE_CPU_BOOT, + ARRAY_SIZE(s->continue_cpu_boot)); + + qdev_init_gpio_in_named(DEVICE(obj), &ot_soc_dbg_ctrl_halt_cpu_boot, + OT_SOC_DBG_HALT_CPU_BOOT, 1); + qdev_init_gpio_in_named(DEVICE(obj), &ot_soc_dbg_ctrl_lc_broadcast, + OT_SOC_DBG_LC_BCAST, OT_LC_BROADCAST_COUNT); + qdev_init_gpio_in_named(DEVICE(obj), &ot_soc_dbg_ctrl_soc_dbg_state, + OT_SOC_DBG_STATE, 1); + qdev_init_gpio_in_named(DEVICE(obj), &ot_soc_dbg_ctrl_boot_status, + OT_SOC_DBG_BOOT_STATUS, 1u); + + s->fsm_tick_bh = qemu_bh_new(&ot_soc_dbg_ctrl_fsm_tick, s); +} + +static void ot_soc_dbg_ctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_soc_dbg_ctrl_realize; + device_class_set_props(dc, ot_soc_dbg_ctrl_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSoCDbgCtrlClass *sc = OT_SOC_DBG_CTRL_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_soc_dbg_ctrl_reset_enter, NULL, + &ot_soc_dbg_ctrl_reset_exit, + &sc->parent_phases); +} + +static const TypeInfo ot_soc_dbg_ctrl_info = { + .name = TYPE_OT_SOC_DBG_CTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtSoCDbgCtrlState), + .instance_init = &ot_soc_dbg_ctrl_init, + .class_size = sizeof(OtSoCDbgCtrlClass), + .class_init = &ot_soc_dbg_ctrl_class_init, +}; + +static void ot_soc_dbg_ctrl_register_types(void) +{ + type_register_static(&ot_soc_dbg_ctrl_info); +} + +type_init(ot_soc_dbg_ctrl_register_types); diff --git a/hw/opentitan/ot_socdbg_ctrl.c b/hw/opentitan/ot_socdbg_ctrl.c deleted file mode 100644 index bd4feeae7bf8b..0000000000000 --- a/hw/opentitan/ot_socdbg_ctrl.c +++ /dev/null @@ -1,886 +0,0 @@ -/* - * QEMU OpenTitan SoC Debug Controller - * - * Copyright (c) 2024-2025 Rivos, Inc. - * - * Author(s): - * Loïc Lefort - * Emmanuel Blot - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "hw/opentitan/ot_alert.h" -#include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_lc_ctrl.h" -#include "hw/opentitan/ot_pwrmgr.h" -#include "hw/opentitan/ot_socdbg_ctrl.h" -#include "hw/qdev-properties.h" -#include "hw/registerfields.h" -#include "hw/riscv/ibex_common.h" -#include "hw/riscv/ibex_gpio.h" -#include "hw/riscv/ibex_irq.h" -#include "trace.h" -#include "trace/trace-hw_opentitan.h" - - -/* clang-format off */ - -/* registers on core bus */ -REG32(CORE_INTR_STATE, 0x00) - SHARED_FIELD(INTR_DEBUG_ATTENTION, 0u, 1u) -REG32(CORE_INTR_ENABLE, 0x04u) -REG32(CORE_INTR_TEST, 0x08u) -REG32(CORE_ALERT_TEST, 0x0cu) - FIELD(CORE_ALERT_TEST, FATAL_FAULT, 0u, 1u) -REG32(CORE_DEBUG_POLICY_CTRL, 0x10u) - /* 4 bits as it seems to be relock (1) + policy (3) */ - FIELD(CORE_DEBUG_POLICY_CTRL, LEVEL, 0u, 4u) -REG32(CORE_DEBUG_POLICY_VALID, 0x14u) - FIELD(CORE_DEBUG_POLICY_VALID, VALID, 0u, 1u) -REG32(CORE_STATUS_MBX, 0x18u) - /* shared by CORE_STATUS_MBX and DMI_JTAG_STATUS */ - SHARED_FIELD(AUTH_DEBUG_INTENT_SET, 0u, 1u) - SHARED_FIELD(AUTH_WINDOW_OPEN, 4u, 1u) - SHARED_FIELD(AUTH_WINDOW_CLOSED, 5u, 1u) - SHARED_FIELD(AUTH_UNLOCK_SUCCESS, 6u, 1u) - SHARED_FIELD(AUTH_UNLOCK_FAILED, 7u, 1u) - /* this is not HW-connected to CORE_DEBUG_POLICY_CTRL */ - SHARED_FIELD(CURRENT_POLICY, 8u, 4u) - SHARED_FIELD(REQUESTED_POLICY, 12u, 4u) - -/* registers on DMI bus */ -REG32(DMI_CONTROL, 0x0) - FIELD(DMI_CONTROL, BOOT_CONTINUE, 0u, 1u) -REG32(DMI_JTAG_STATUS, 0x4) -REG32(DMI_JTAG_BOOT_STATUS, 0x8) - -/* boot_status_bm fields */ -REG16(BOOT_STATUS, 0x0) - FIELD(BOOT_STATUS, MAIN_CLK_STATUS, 0u, 1u) - FIELD(BOOT_STATUS, IO_CLK_STATUS, 1u, 1u) - FIELD(BOOT_STATUS, USB_CLK_STATUS, 2u, 1u) - FIELD(BOOT_STATUS, OTP_DONE, 3u, 1u) - FIELD(BOOT_STATUS, LC_DONE, 4u, 1u) - FIELD(BOOT_STATUS, ROM_CTRL_DONE, 5u, 3u) - FIELD(BOOT_STATUS, ROM_CTRL_GOOD, 8u, 3u) - FIELD(BOOT_STATUS, CPU_FETCH_EN, 11u, 1u) - -/* socdbg_bm fields */ -REG16(SOCDBG, 0x0) - FIELD(SOCDBG, A0_DEBUG, 0u, 1u) - FIELD(SOCDBG, A0_FORCE_RAW, 1u, 1u) - FIELD(SOCDBG, HALT_CPU_BOOT, 2u, 1u) - -/* debug_policy, dbg_locked, dbg_unlocked fields */ -SHARED_FIELD(POLICY_CAT, 0u, 2u) -SHARED_FIELD(POLICY_RELOCK, 2u, 1u) -SHARED_FIELD(POLICY_UNUSED, 3u, 1u) - -/* clang-format on */ - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) - -#define R_CORE_LAST_REG (R_CORE_STATUS_MBX) -#define REGS_CORE_COUNT (R_CORE_LAST_REG + 1u) -#define REGS_CORE_SIZE (REGS_CORE_COUNT * sizeof(uint32_t)) - -#define R_DMI_LAST_REG (R_DMI_JTAG_BOOT_STATUS) -#define REGS_DMI_COUNT (R_DMI_LAST_REG + 1u) -#define REGS_DMI_SIZE (REGS_DMI_COUNT * sizeof(uint32_t)) - -#define CORE_ALERT_TEST_MASK (R_CORE_ALERT_TEST_FATAL_FAULT_MASK) -#define STATUS_MASK \ - (AUTH_DEBUG_INTENT_SET_MASK | AUTH_WINDOW_OPEN_MASK | \ - AUTH_WINDOW_CLOSED_MASK | AUTH_UNLOCK_SUCCESS_MASK | \ - AUTH_UNLOCK_FAILED_MASK | CURRENT_POLICY_MASK | REQUESTED_POLICY_MASK) - -#define DEFAULT_DBG_UNLOCKED 0u -#define DEFAULT_DBG_LOCKED 7u - -enum { - CPU_BOOT_GOOD, - CPU_BOOT_DONE, - CPU_BOOT_COUNT, -} OtSoCDbgCpuBoot; - -typedef enum { - ST_IDLE, - ST_CHECK_LC_ST, - ST_WAIT4_DFT_EN, - ST_CHECK_HALT_PIN, - ST_CHECK_JTAG_GO, - ST_CONTINUE_BOOT, - ST_HALT_DONE, -} OtSoCDbgCtrlFsmState; - -struct OtSoCDbgCtrlState { - SysBusDevice parent_obj; - - MemoryRegion core; - MemoryRegion dmi; - IbexIRQ irq; - IbexIRQ alert; - IbexIRQ policy; - IbexIRQ cpu_boot[CPU_BOOT_COUNT]; /* "continue_cpu_boot" */ - QEMUBH *fsm_tick_bh; - - uint32_t regs[REGS_CORE_COUNT]; - OtSoCDbgCtrlFsmState fsm_state; - OtSoCDbgState socdbg_state; - unsigned debug_policy; - unsigned fsm_tick_count; - uint16_t boot_status_bm; /* BOOT_STATUS fields */ - uint16_t socdbg_bm; /* SOCDBG fields */ - uint16_t lc_broadcast_bm; /* OtLcCtrlBroadcast fields */ - bool boot_continue; - bool debug_valid; - - char *ot_id; - uint8_t dbg_locked; - uint8_t dbg_unlocked; - bool halt_function; - bool dft_ignore; -}; - -struct OtSoCDbgCtrlClass { - SysBusDeviceClass parent_class; - ResettablePhases parent_phases; -}; - -#define ROM_MASK ((1u << (R_BOOT_STATUS_ROM_CTRL_DONE_LENGTH - 1u)) - 1u) - -#define REG_NAME(_kind_, _reg_) \ - ((((_reg_) <= REGS_##_kind_##_COUNT) && REG_##_kind_##_NAMES[_reg_]) ? \ - REG_##_kind_##_NAMES[_reg_] : \ - "?") - -#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) - -static const char *REG_CORE_NAMES[REGS_CORE_COUNT] = { - /* clang-format off */ - REG_NAME_ENTRY(CORE_INTR_STATE), - REG_NAME_ENTRY(CORE_INTR_ENABLE), - REG_NAME_ENTRY(CORE_INTR_TEST), - REG_NAME_ENTRY(CORE_ALERT_TEST), - REG_NAME_ENTRY(CORE_DEBUG_POLICY_CTRL), - REG_NAME_ENTRY(CORE_DEBUG_POLICY_VALID), - REG_NAME_ENTRY(CORE_STATUS_MBX), - /* clang-format on */ -}; - -static const char *REG_DMI_NAMES[REGS_DMI_COUNT] = { - /* clang-format off */ - REG_NAME_ENTRY(DMI_CONTROL), - REG_NAME_ENTRY(DMI_JTAG_STATUS), - REG_NAME_ENTRY(DMI_JTAG_BOOT_STATUS), - /* clang-format on */ -}; -#undef REG_NAME_ENTRY - -#define LC_NAME_ENTRY(_st_) [_st_] = stringify(_st_) -static const char *LC_BROADCAST_NAMES[] = { - /* clang-format off */ - LC_NAME_ENTRY(OT_LC_RAW_TEST_RMA), - LC_NAME_ENTRY(OT_LC_DFT_EN), - LC_NAME_ENTRY(OT_LC_NVM_DEBUG_EN), - LC_NAME_ENTRY(OT_LC_HW_DEBUG_EN), - LC_NAME_ENTRY(OT_LC_CPU_EN), - LC_NAME_ENTRY(OT_LC_KEYMGR_EN), - LC_NAME_ENTRY(OT_LC_ESCALATE_EN), - LC_NAME_ENTRY(OT_LC_CHECK_BYP_EN), - LC_NAME_ENTRY(OT_LC_CREATOR_SEED_SW_RW_EN), - LC_NAME_ENTRY(OT_LC_OWNER_SEED_SW_RW_EN), - LC_NAME_ENTRY(OT_LC_ISO_PART_SW_RD_EN), - LC_NAME_ENTRY(OT_LC_ISO_PART_SW_WR_EN), - LC_NAME_ENTRY(OT_LC_SEED_HW_RD_EN), - /* clang-format on */ -}; -#undef LC_NAME_ENTRY - -#define LC_BCAST_NAME(_bit_) \ - (((unsigned)(_bit_)) < ARRAY_SIZE(LC_BROADCAST_NAMES) ? \ - LC_BROADCAST_NAMES[(_bit_)] : \ - "?") - -#define SOCDBG_NAME_ENTRY(_st_) [OT_SOCDBG_ST_##_st_] = stringify(_st_) -static const char *SOCDBG_NAMES[] = { - /* clang-format off */ - SOCDBG_NAME_ENTRY(RAW), - SOCDBG_NAME_ENTRY(PRE_PROD), - SOCDBG_NAME_ENTRY(PROD), - /* clang-format on */ -}; -#undef SOCDBG_NAME_ENTRY - -#define SOCDBG_NAME(_st_) \ - (((unsigned)(_st_)) < ARRAY_SIZE(SOCDBG_NAMES) ? SOCDBG_NAMES[(_st_)] : "?") - -#define STATE_NAME_ENTRY(_st_) [ST_##_st_] = stringify(_st_) -static const char *STATE_NAMES[] = { - /* clang-format off */ - STATE_NAME_ENTRY(IDLE), - STATE_NAME_ENTRY(CHECK_LC_ST), - STATE_NAME_ENTRY(WAIT4_DFT_EN), - STATE_NAME_ENTRY(CHECK_HALT_PIN), - STATE_NAME_ENTRY(CHECK_JTAG_GO), - STATE_NAME_ENTRY(CONTINUE_BOOT), - STATE_NAME_ENTRY(HALT_DONE), - /* clang-format on */ -}; -#undef STATE_NAME_ENTRY -#define STATE_NAME(_st_) \ - ((_st_) >= 0 && (_st_) < ARRAY_SIZE(STATE_NAMES) ? STATE_NAMES[(_st_)] : \ - "?") -#define CHANGE_STATE(_s_, _st_) \ - ot_socdbg_ctrl_change_state_line(_s_, ST_##_st_, __LINE__) -#define SCHEDULE_FSM(_s_) ot_socdbg_ctrl_schedule_fsm(_s_, __func__, __LINE__) - -static void ot_socdbg_ctrl_core_update_irq(OtSoCDbgCtrlState *s) -{ - uint32_t levels = s->regs[R_CORE_INTR_STATE] & s->regs[R_CORE_INTR_ENABLE]; - - int level = (int)(bool)(levels & INTR_DEBUG_ATTENTION_MASK); - if (level != ibex_irq_get_level(&s->irq)) { - trace_ot_socdbg_ctrl_core_update_irq(s->ot_id, level); - } - - ibex_irq_set(&s->irq, level); -} - -static void ot_socdbg_ctrl_change_state_line( - OtSoCDbgCtrlState *s, OtSoCDbgCtrlFsmState state, int line) -{ - trace_ot_socdbg_ctrl_change_state(s->ot_id, line, STATE_NAME(s->fsm_state), - s->fsm_state, STATE_NAME(state), state); - - s->fsm_state = state; -} - -static void -ot_socdbg_ctrl_schedule_fsm(OtSoCDbgCtrlState *s, const char *func, int line) -{ - s->fsm_tick_count += 1u; - trace_ot_socdbg_ctrl_schedule_fsm(s->ot_id, func, line, s->fsm_tick_count); - qemu_bh_schedule(s->fsm_tick_bh); -} - -static void ot_socdbg_ctrl_tick_fsm(OtSoCDbgCtrlState *s) -{ - bool cpu_boot_done = false; - - trace_ot_socdbg_ctrl_tick_fsm(s->ot_id, STATE_NAME(s->fsm_state), - s->boot_status_bm, s->lc_broadcast_bm, - s->socdbg_bm, s->dft_ignore, - s->boot_continue); - - switch (s->fsm_state) { - case ST_IDLE: - if (s->boot_status_bm & R_BOOT_STATUS_LC_DONE_MASK) { - CHANGE_STATE(s, CHECK_LC_ST); - } - break; - case ST_CHECK_LC_ST: - if ((s->lc_broadcast_bm & (1u << OT_LC_RAW_TEST_RMA)) && - !s->dft_ignore) { - CHANGE_STATE(s, WAIT4_DFT_EN); - } else { - CHANGE_STATE(s, CONTINUE_BOOT); - } - break; - case ST_WAIT4_DFT_EN: - if (s->lc_broadcast_bm & (1u << OT_LC_DFT_EN)) { - CHANGE_STATE(s, CHECK_HALT_PIN); - } - break; - case ST_CHECK_HALT_PIN: - if (s->socdbg_bm & R_SOCDBG_HALT_CPU_BOOT_MASK) { - CHANGE_STATE(s, CHECK_JTAG_GO); - } else { - CHANGE_STATE(s, CONTINUE_BOOT); - } - break; - case ST_CHECK_JTAG_GO: - if (s->boot_continue) { - CHANGE_STATE(s, CONTINUE_BOOT); - } - break; - case ST_CONTINUE_BOOT: - CHANGE_STATE(s, HALT_DONE); - break; - case ST_HALT_DONE: - cpu_boot_done = true; - break; - default: - /* it does not seem there is a special state for this case */ - ibex_irq_set(&s->alert, (int)true); - return; - } - - /* as with PwrMgr, use simple boolean value, not MuBi4 */ - int cpu_boot_done_i = (int)cpu_boot_done; - if (ibex_irq_get_level(&s->cpu_boot[CPU_BOOT_DONE]) != cpu_boot_done_i) { - trace_ot_socdbg_ctrl_cpu_boot_done(s->ot_id, cpu_boot_done_i); - } - ibex_irq_set(&s->cpu_boot[CPU_BOOT_DONE], cpu_boot_done_i); -} - -static void ot_socdbg_ctrl_update(OtSoCDbgCtrlState *s) -{ - OtSoCDbgState socdbg_state = - ((s->socdbg_bm & R_SOCDBG_A0_DEBUG_MASK) && - (s->socdbg_bm & R_SOCDBG_A0_FORCE_RAW_MASK)) ? - OT_SOCDBG_ST_RAW : - s->socdbg_state; - - switch (socdbg_state) { - case OT_SOCDBG_ST_RAW: - s->debug_policy = - s->lc_broadcast_bm & (1u << OT_LC_DFT_EN) || - s->lc_broadcast_bm & (1u << OT_LC_HW_DEBUG_EN) || - (s->socdbg_bm & R_SOCDBG_A0_DEBUG_MASK) ? - s->dbg_unlocked : - s->dbg_locked; - s->debug_valid = - (bool)((s->boot_status_bm & R_BOOT_STATUS_LC_DONE_MASK) || - (s->socdbg_bm & R_SOCDBG_A0_DEBUG_MASK)); - break; - case OT_SOCDBG_ST_PRE_PROD: - s->debug_policy = s->dbg_unlocked; - s->debug_valid = (bool)(s->boot_status_bm & R_BOOT_STATUS_LC_DONE_MASK); - break; - case OT_SOCDBG_ST_PROD: - s->debug_policy = s->regs[R_CORE_DEBUG_POLICY_CTRL] & - (POLICY_CAT_MASK | POLICY_RELOCK_MASK); - s->debug_valid = (bool)s->regs[R_CORE_DEBUG_POLICY_VALID]; - break; - default: - s->debug_policy = s->dbg_locked; - s->debug_valid = false; - } - - int policy = (int)((s->debug_policy & OT_SOCDBG_DEBUG_POLICY_MASK) | - (s->debug_valid ? OT_SOCDBG_DEBUG_VALID_MASK : 0)); - - int prev_policy = ibex_irq_get_level(&s->policy); - if (prev_policy != policy) { - trace_ot_socdbg_ctrl_update(s->ot_id, SOCDBG_NAME(socdbg_state), - STATE_NAME(s->fsm_state), s->debug_policy, - s->debug_valid); - } - ibex_irq_set(&s->policy, policy); -} - -static void ot_socdbg_ctrl_fsm_tick(void *opaque) -{ - OtSoCDbgCtrlState *s = opaque; - - OtSoCDbgCtrlFsmState fsm_state = s->fsm_state; - g_assert(s->fsm_tick_count); - while (s->fsm_tick_count) { - ot_socdbg_ctrl_update(s); - s->fsm_tick_count--; - ot_socdbg_ctrl_tick_fsm(s); - } - if (fsm_state != s->fsm_state) { - /* schedule FSM update once more if its state has changed */ - SCHEDULE_FSM(s); - } -} - -static void ot_socdbg_ctrl_a0_debug(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - g_assert(n == 0); - - trace_ot_socdbg_ctrl_rcv(s->ot_id, "A0_DEBUG", 0, ibex_gpio_repr(level)); - - /* expect an Ibex GPIO signal */ - g_assert(ibex_gpio_check(level)); - - if (ibex_gpio_level(level)) { - s->socdbg_bm |= R_SOCDBG_A0_DEBUG_MASK; - } else { - s->socdbg_bm &= ~R_SOCDBG_A0_DEBUG_MASK; - } - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_halt_cpu_boot(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - g_assert(n == 0); - - trace_ot_socdbg_ctrl_rcv(s->ot_id, "HALT_CPU_BOOT", 0, - ibex_gpio_repr(level)); - - /* expect an Ibex GPIO signal */ - g_assert(ibex_gpio_check(level)); - - /* active low */ - if (!ibex_gpio_level(level)) { - s->socdbg_bm |= R_SOCDBG_HALT_CPU_BOOT_MASK; - } else { - s->socdbg_bm &= ~R_SOCDBG_HALT_CPU_BOOT_MASK; - } - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_lc_broadcast(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - unsigned bcast = (unsigned)n; - g_assert(bcast < OT_LC_BROADCAST_COUNT); - g_assert(!ibex_gpio_check(level)); - - trace_ot_socdbg_ctrl_rcv(s->ot_id, LC_BCAST_NAME(bcast), bcast, - level ? '1' : '0'); - - switch (n) { - case OT_LC_RAW_TEST_RMA: - case OT_LC_DFT_EN: - case OT_LC_HW_DEBUG_EN: - case OT_LC_CPU_EN: - if (level) { - s->lc_broadcast_bm |= (1u << bcast); - } else { - s->lc_broadcast_bm &= (~1u << bcast); - } - break; - /* NOLINTBEGIN(bugprone-branch-clone) */ - case OT_LC_NVM_DEBUG_EN: - case OT_LC_KEYMGR_EN: - case OT_LC_ISO_PART_SW_RD_EN: - case OT_LC_ISO_PART_SW_WR_EN: - case OT_LC_OWNER_SEED_SW_RW_EN: - /* do not seem to be routed... */ - break; - case OT_LC_CREATOR_SEED_SW_RW_EN: - case OT_LC_SEED_HW_RD_EN: - case OT_LC_ESCALATE_EN: - case OT_LC_CHECK_BYP_EN: - /* verbatim from RTL: "Use unused singals to make lint clean" */ - - /* why do we explictly route signals that are then discarded? */ - break; - /* NOLINTEND(bugprone-branch-clone) */ - default: - g_assert_not_reached(); - } - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_boot_status(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - g_assert(n == 0); - - OtPwrMgrBootStatus bs = { .i32 = level }; - trace_ot_socdbg_ctrl_boot_status(s->ot_id, (bool)bs.main_ip_clk_en, - (bool)bs.io_ip_clk_en, (bool)bs.otp_done, - (bool)bs.lc_done, (bool)bs.cpu_fetch_en, - bs.rom_done & ROM_MASK, - bs.rom_good & ROM_MASK); - uint16_t bs_bm = 0; - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, MAIN_CLK_STATUS, bs.main_ip_clk_en); - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, IO_CLK_STATUS, bs.io_ip_clk_en); - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, USB_CLK_STATUS, bs.usb_ip_clk_en); - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, OTP_DONE, bs.otp_done); - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, LC_DONE, bs.lc_done); - bs_bm = - FIELD_DP16(bs_bm, BOOT_STATUS, ROM_CTRL_DONE, bs.rom_done & ROM_MASK); - bs_bm = - FIELD_DP16(bs_bm, BOOT_STATUS, ROM_CTRL_DONE, bs.rom_good & ROM_MASK); - bs_bm = FIELD_DP16(bs_bm, BOOT_STATUS, CPU_FETCH_EN, bs.cpu_fetch_en); - s->boot_status_bm = bs_bm; - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_a0_force_raw(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - g_assert(n == 0); - g_assert(!ibex_gpio_check(level)); - - trace_ot_socdbg_ctrl_rcv(s->ot_id, "FORCE_RAW", 0, level ? '1' : '0'); - - if (level) { - s->socdbg_bm |= R_SOCDBG_A0_FORCE_RAW_MASK; - } else { - s->socdbg_bm &= ~R_SOCDBG_A0_FORCE_RAW_MASK; - } - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_socdbg_state(void *opaque, int n, int level) -{ - OtSoCDbgCtrlState *s = opaque; - - g_assert(n == 0); - g_assert(!ibex_gpio_check(level)); - - switch (level) { - case 0: - s->socdbg_state = OT_SOCDBG_ST_RAW; - break; - case 1: - s->socdbg_state = OT_SOCDBG_ST_PRE_PROD; - break; - case 2: - s->socdbg_state = OT_SOCDBG_ST_PROD; - break; - default: - g_assert_not_reached(); - } - - trace_ot_socdbg_ctrl_rcv(s->ot_id, "SOCDBG_STATE", 0, (char)('0' + level)); - - trace_ot_socdbg_ctrl_socdbg_state(s->ot_id, SOCDBG_NAME(s->socdbg_state)); - - SCHEDULE_FSM(s); -} - -static uint64_t -ot_socdbg_ctrl_core_read(void *opaque, hwaddr addr, unsigned size) -{ - OtSoCDbgCtrlState *s = opaque; - (void)size; - uint32_t val32 = 0; - - hwaddr reg = R32_OFF(addr); - switch (reg) { - /* note: interrupt usage is not specified */ - case R_CORE_INTR_STATE: - case R_CORE_INTR_ENABLE: - case R_CORE_DEBUG_POLICY_CTRL: - case R_CORE_DEBUG_POLICY_VALID: - case R_CORE_STATUS_MBX: - val32 = s->regs[reg]; - break; - case R_CORE_INTR_TEST: - case R_CORE_ALERT_TEST: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(CORE, reg)); - val32 = 0; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, - s->ot_id, addr); - val32 = 0; - break; - } - - uint32_t pc = ibex_get_current_pc(); - trace_ot_socdbg_ctrl_core_io_read_out(s->ot_id, (uint32_t)addr, - REG_NAME(CORE, reg), val32, pc); - - return (uint32_t)val32; -} - -static void ot_socdbg_ctrl_core_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - OtSoCDbgCtrlState *s = opaque; - (void)size; - uint32_t val32 = (uint32_t)value; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - trace_ot_socdbg_ctrl_core_io_write(s->ot_id, (uint32_t)addr, - REG_NAME(CORE, reg), val32, pc); - - switch (reg) { - case R_CORE_INTR_STATE: - val32 &= INTR_DEBUG_ATTENTION_MASK; - s->regs[reg] &= ~val32; /* RW1C */ - ot_socdbg_ctrl_core_update_irq(s); - break; - case R_CORE_INTR_ENABLE: - val32 &= INTR_DEBUG_ATTENTION_MASK; - s->regs[reg] = val32; - ot_socdbg_ctrl_core_update_irq(s); - break; - case R_CORE_INTR_TEST: - val32 &= INTR_DEBUG_ATTENTION_MASK; - s->regs[reg] |= val32; /* RW1S */ - ot_socdbg_ctrl_core_update_irq(s); - break; - case R_CORE_ALERT_TEST: - val32 &= CORE_ALERT_TEST_MASK; - if (val32) { - ibex_irq_set(&s->alert, 1); - } - break; - case R_CORE_DEBUG_POLICY_CTRL: - val32 &= R_CORE_DEBUG_POLICY_CTRL_LEVEL_MASK; - s->regs[reg] = val32; - break; - case R_CORE_DEBUG_POLICY_VALID: - val32 &= R_CORE_DEBUG_POLICY_VALID_VALID_MASK; - s->regs[reg] = val32; - break; - case R_CORE_STATUS_MBX: - val32 &= STATUS_MASK; - s->regs[reg] = val32; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, - s->ot_id, addr); - } -} - -static uint64_t -ot_socdbg_ctrl_dmi_read(void *opaque, hwaddr addr, unsigned size) -{ - OtSoCDbgCtrlState *s = opaque; - (void)size; - uint32_t val32 = 0; - - hwaddr reg = R32_OFF(addr); - switch (reg) { - case R_DMI_CONTROL: - val32 = s->boot_continue ? R_DMI_CONTROL_BOOT_CONTINUE_MASK : 0u; - break; - case R_DMI_JTAG_STATUS: - val32 = s->regs[R_CORE_STATUS_MBX]; /* mirror of the core I/F */ - break; - case R_DMI_JTAG_BOOT_STATUS: - if (s->lc_broadcast_bm & (1u << OT_LC_DFT_EN)) { - val32 = (uint32_t)s->boot_status_bm; - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: BootStatus disabled (no DFT)\n", __func__, - s->ot_id); - val32 = 0; - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, - s->ot_id, addr); - val32 = 0; - break; - } - - trace_ot_socdbg_ctrl_dmi_io_read_out(s->ot_id, (uint32_t)addr, - REG_NAME(DMI, reg), val32); - - return (uint32_t)val32; -} - -static void ot_socdbg_ctrl_dmi_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - OtSoCDbgCtrlState *s = opaque; - (void)size; - uint32_t val32 = (uint32_t)value; - - hwaddr reg = R32_OFF(addr); - - trace_ot_socdbg_ctrl_dmi_io_write(s->ot_id, (uint32_t)addr, - REG_NAME(DMI, reg), val32); - - switch (reg) { - case R_DMI_CONTROL: - s->boot_continue = (bool)(val32 & R_DMI_CONTROL_BOOT_CONTINUE_MASK); - SCHEDULE_FSM(s); - break; - case R_DMI_JTAG_STATUS: - case R_DMI_JTAG_BOOT_STATUS: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", - __func__, s->ot_id, addr, REG_NAME(DMI, reg)); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s, Bad offset 0x%" HWADDR_PRIx "\n", __func__, - s->ot_id, addr); - } -} - -static Property ot_socdbg_ctrl_properties[] = { - DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSoCDbgCtrlState, ot_id), - DEFINE_PROP_UINT8("dbg_unlocked", OtSoCDbgCtrlState, dbg_unlocked, - DEFAULT_DBG_UNLOCKED), - DEFINE_PROP_UINT8("dbg_locked", OtSoCDbgCtrlState, dbg_locked, - DEFAULT_DBG_LOCKED), - DEFINE_PROP_BOOL("halt_function", OtSoCDbgCtrlState, halt_function, true), - DEFINE_PROP_BOOL("dft-ignore", OtSoCDbgCtrlState, dft_ignore, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static const MemoryRegionOps ot_socdbg_ctrl_core_ops = { - .read = &ot_socdbg_ctrl_core_read, - .write = &ot_socdbg_ctrl_core_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static const MemoryRegionOps ot_socdbg_ctrl_dmi_ops = { - .read = &ot_socdbg_ctrl_dmi_read, - .write = &ot_socdbg_ctrl_dmi_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static void ot_socdbg_ctrl_reset_enter(Object *obj, ResetType type) -{ - OtSoCDbgCtrlClass *c = OT_SOCDBG_CTRL_GET_CLASS(obj); - OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(obj); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - memset(s->regs, 0, sizeof(s->regs)); - - ot_socdbg_ctrl_core_update_irq(s); - ibex_irq_set(&s->alert, 0); - ibex_irq_set(&s->cpu_boot[CPU_BOOT_GOOD], (int)false); - ibex_irq_set(&s->cpu_boot[CPU_BOOT_DONE], (int)false); - - CHANGE_STATE(s, IDLE); - s->fsm_tick_count = 0u; - s->socdbg_bm = 0u; - s->boot_status_bm = 0u; - s->lc_broadcast_bm = 0u; - s->socdbg_state = OT_SOCDBG_ST_PROD; - s->debug_policy = s->dbg_locked; - s->debug_valid = false; - s->boot_continue = false; -} - -static void ot_socdbg_ctrl_reset_exit(Object *obj, ResetType type) -{ - OtSoCDbgCtrlClass *c = OT_SOCDBG_CTRL_GET_CLASS(obj); - OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(obj); - - if (c->parent_phases.exit) { - c->parent_phases.exit(obj, type); - } - - qemu_bh_cancel(s->fsm_tick_bh); - - /* - * ROM signal which does not comes from a ROM but from this device to - * signal the status of the Ibex core, but used as a ROM in PwrMgr: - * always on.... - */ - s->boot_status_bm |= ROM_MASK << R_BOOT_STATUS_ROM_CTRL_GOOD_SHIFT; - - ibex_irq_set(&s->cpu_boot[CPU_BOOT_GOOD], (int)true); - - SCHEDULE_FSM(s); -} - -static void ot_socdbg_ctrl_realize(DeviceState *dev, Error **errp) -{ - OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(dev); - (void)errp; - - g_assert(s->ot_id); - - s->dbg_locked &= POLICY_CAT_MASK | POLICY_RELOCK_MASK; - s->dbg_unlocked &= POLICY_CAT_MASK | POLICY_RELOCK_MASK; -} - -static void ot_socdbg_ctrl_init(Object *obj) -{ - OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(obj); - - memory_region_init_io(&s->core, obj, &ot_socdbg_ctrl_core_ops, s, - TYPE_OT_SOCDBG_CTRL, REGS_CORE_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->core); - - memory_region_init_io(&s->dmi, obj, &ot_socdbg_ctrl_dmi_ops, s, - TYPE_OT_SOCDBG_CTRL, REGS_DMI_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->dmi); - - ibex_sysbus_init_irq(obj, &s->irq); - ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); - ibex_qdev_init_irq(obj, &s->policy, OT_SOCDBG_DEBUG_POLICY); - ibex_qdev_init_irqs(obj, s->cpu_boot, OT_SOCDBG_CPU_BOOT, - ARRAY_SIZE(s->cpu_boot)); - - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_halt_cpu_boot, - OT_SOCDBG_HALT_CPU_BOOT, 1); - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_lc_broadcast, - OT_SOCDBG_LC_BCAST, OT_LC_BROADCAST_COUNT); - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_socdbg_state, - OT_SOCDBG_STATE, 1); - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_boot_status, - OT_SOCDBG_BOOT_STATUS, 1u); - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_a0_debug, - OT_SOCDBG_A0_DEBUG_EN, 1); - qdev_init_gpio_in_named(DEVICE(obj), &ot_socdbg_ctrl_a0_force_raw, - OT_SOCDBG_A0_FORCE_RAW, 1u); - - s->fsm_tick_bh = qemu_bh_new(&ot_socdbg_ctrl_fsm_tick, s); -} - -static void ot_socdbg_ctrl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - (void)data; - - dc->realize = &ot_socdbg_ctrl_realize; - device_class_set_props(dc, ot_socdbg_ctrl_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - ResettableClass *rc = RESETTABLE_CLASS(klass); - OtSoCDbgCtrlClass *sc = OT_SOCDBG_CTRL_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_socdbg_ctrl_reset_enter, NULL, - &ot_socdbg_ctrl_reset_exit, - &sc->parent_phases); -} - -static const TypeInfo ot_socdbg_ctrl_info = { - .name = TYPE_OT_SOCDBG_CTRL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OtSoCDbgCtrlState), - .instance_init = &ot_socdbg_ctrl_init, - .class_size = sizeof(OtSoCDbgCtrlClass), - .class_init = &ot_socdbg_ctrl_class_init, -}; - -static void ot_socdbg_ctrl_register_types(void) -{ - type_register_static(&ot_socdbg_ctrl_info); -} - -type_init(ot_socdbg_ctrl_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 0a9fdb940a248..1425460dd3f26 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -520,22 +520,21 @@ ot_soc_proxy_io_read_out(const char * id, uint32_t addr, const char * regname, u ot_soc_proxy_io_write(const char * id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_soc_proxy_update_irq(const char * id, unsigned n, int prev, int next) "%s: #%u %d -> %d" -# ot_socdbg_ctrl.c - -ot_socdbg_ctrl_boot_status(const char *id, bool mclk, bool ioclk, bool otp, bool lc, bool cpu, unsigned rdone, unsigned rgood) "%s: mclk:%u ioclk:%u otp:%u lc:%u cpu_en:%u rom_done:0x%x rom_good:0x%x" -ot_socdbg_ctrl_change_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" -ot_socdbg_ctrl_cpu_boot_done(const char *id, int level) "%s: %d" -ot_socdbg_ctrl_core_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_socdbg_ctrl_core_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_socdbg_ctrl_core_update_irq(const char *id, int new) "%s: %u" -ot_socdbg_ctrl_dmi_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val) "%s: addr=0x%02x (%s), val=0x%x" -ot_socdbg_ctrl_dmi_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val) "%s: addr=0x%02x (%s), val=0x%x" -ot_socdbg_ctrl_rcv(const char *id, const char *src, unsigned n, char sig) "%s: %s #%d: lvl %c" -ot_socdbg_ctrl_schedule_fsm(const char *id, const char *func, int line, unsigned tc) "%s: @ %s:%d (%u)" -ot_socdbg_ctrl_socdbg_state(const char *id, const char *st) "%s: %s" -ot_socdbg_ctrl_tick_fsm(const char *id, const char *st, uint32_t bs, uint32_t lc, uint32_t dbg, bool dfti, bool bc) "%s: %s bs:0x%03x lc:0x%02x dbg:0x%01x dfti:%u bc:%u" -ot_socdbg_ctrl_update(const char *id, const char *socdbgst, const char *fsmst, unsigned policy, bool valid) "%s: [%s] fsm:%s policy:0x%1x valid:%u" - +# ot_soc_dbg_ctrl.c + +ot_soc_dbg_ctrl_boot_status(const char *id, bool mclk, bool ioclk, bool otp, bool lc, bool cpu, unsigned rdone, unsigned rgood) "%s: mclk:%u ioclk:%u otp:%u lc:%u cpu_en:%u rom_done:0x%x rom_good:0x%x" +ot_soc_dbg_ctrl_change_state(const char *id, int line, const char *old, int nold, const char *new, int nnew) "%s: @ %d [%s:%d] -> [%s:%d]" +ot_soc_dbg_ctrl_cpu_boot_done(const char *id, int level) "%s: %d" +ot_soc_dbg_ctrl_core_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_soc_dbg_ctrl_core_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_soc_dbg_ctrl_core_update_irq(const char *id, int new) "%s: %u" +ot_soc_dbg_ctrl_jtag_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val) "%s: addr=0x%02x (%s), val=0x%x" +ot_soc_dbg_ctrl_jtag_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val) "%s: addr=0x%02x (%s), val=0x%x" +ot_soc_dbg_ctrl_rcv(const char *id, const char *src, unsigned n, char sig) "%s: %s #%d: lvl %c" +ot_soc_dbg_ctrl_schedule_fsm(const char *id, const char *func, int line, unsigned tc) "%s: @ %s:%d (%u)" +ot_soc_dbg_ctrl_soc_dbg_state(const char *id, const char *st) "%s: %s" +ot_soc_dbg_ctrl_tick_fsm(const char *id, const char *st, uint16_t lc, bool dfti, bool lcd, bool hcb, bool bc) "%s: %s lc:0x%02x dfti:%u lcd:%u hcb:%u bc:%u" +ot_soc_dbg_ctrl_update_policy(const char *id, const char *socdbgst, const char *fsmst, uint8_t cat_bm, bool relocked) "%s: [%s] fsm:%s cat_bm:0x%1x relocked:%u" # ot_spi_device.c diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 35722c0a338e7..4eb7e6b76b07c 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -53,7 +53,7 @@ config OT_DARJEELING select OT_PWRMGR select OT_RSTMGR select OT_ROM_CTRL - select OT_SOCDBG_CTRL + select OT_SOC_DBG_CTRL select OT_SOC_PROXY select OT_SPI_DEVICE select OT_SPI_HOST diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 35458c5eac23c..42b4933ffd7ab 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -64,8 +64,8 @@ #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/opentitan/ot_rstmgr.h" +#include "hw/opentitan/ot_soc_dbg_ctrl.h" #include "hw/opentitan/ot_soc_proxy.h" -#include "hw/opentitan/ot_socdbg_ctrl.h" #include "hw/opentitan/ot_spi_device.h" #include "hw/opentitan/ot_spi_host.h" #include "hw/opentitan/ot_sram_ctrl.h" @@ -1253,8 +1253,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("silicon_creator_id", 0x4002u), IBEX_DEV_UINT_PROP("product_id", 0x4000u), IBEX_DEV_UINT_PROP("revision_id", 0x1u), + IBEX_DEV_UINT_PROP("kmac-app", 1u), IBEX_DEV_BOOL_PROP("volatile_raw_unlock", true), - IBEX_DEV_UINT_PROP("kmac-app", 1u) + IBEX_DEV_BOOL_PROP("decode_soc_dbg", true) ) }, [OT_DJ_SOC_DEV_ALERT_HANDLER] = { @@ -1286,19 +1287,17 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), }, [OT_DJ_SOC_DEV_SOC_DBG_CTRL] = { - /* @todo: should be renamed TYPE_OT_SOC_DBG_CTRL */ - .type = TYPE_OT_SOCDBG_CTRL, + .type = TYPE_OT_SOC_DBG_CTRL, .memmap = MEMMAPENTRIES( { .base = 0x30170000u } ), - /* - * @todo: add alert signals once OT_SOC_DBG_CTRL supports them - * - * .gpio = IBEXGPIOCONNDEFS( - * OT_DJ_SOC_GPIO_ALERT(0, 95), - * OT_DJ_SOC_GPIO_ALERT(1, 96) - * ), - */ + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 95), + OT_DJ_SOC_GPIO_ALERT(1, 96) + ), + .link = IBEXDEVICELINKDEFS( + OT_DJ_SOC_DEVLINK("lc-ctrl", LC_CTRL) + ), }, [OT_DJ_SOC_DEV_SPI_HOST0] = { .type = TYPE_OT_SPI_HOST, diff --git a/include/hw/opentitan/ot_common.h b/include/hw/opentitan/ot_common.h index 8e9824935ada1..3970600f2f1f3 100644 --- a/include/hw/opentitan/ot_common.h +++ b/include/hw/opentitan/ot_common.h @@ -58,18 +58,28 @@ #define OT_MULTIBITBOOL4_TRUE 0x6u #define OT_MULTIBITBOOL4_FALSE 0x9u +#define OT_MULTIBITBOOL4_WIDTH 4u +#define OT_MULTIBITBOOL4_MASK ((1u << OT_MULTIBITBOOL4_WIDTH) - 1u) #define OT_MULTIBITBOOL8_TRUE 0x96u #define OT_MULTIBITBOOL8_FALSE 0x69u +#define OT_MULTIBITBOOL8_WIDTH 8u +#define OT_MULTIBITBOOL8_MASK ((1u << OT_MULTIBITBOOL8_WIDTH) - 1u) #define OT_MULTIBITBOOL12_TRUE 0x696u #define OT_MULTIBITBOOL12_FALSE 0x969u +#define OT_MULTIBITBOOL12_WIDTH 12u +#define OT_MULTIBITBOOL12_MASK ((1u << OT_MULTIBITBOOL12_WIDTH) - 1u) #define OT_MULTIBITBOOL16_TRUE 0x9696u #define OT_MULTIBITBOOL16_FALSE 0x6969u +#define OT_MULTIBITBOOL16_WIDTH 16u +#define OT_MULTIBITBOOL16_MASK ((1u << OT_MULTIBITBOOL16_WIDTH) - 1u) #define OT_MULTIBITBOOL_LC4_TRUE 0xau #define OT_MULTIBITBOOL_LC4_FALSE 0x5u +#define OT_MULTIBITBOOL_LC4_WIDTH 4u +#define OT_MULTIBITBOOL_LC4_MASK ((1u << OT_MULTIBITBOOL_LC4_WIDTH) - 1u) typedef uint8_t ot_mb4_t; typedef uint8_t ot_mb8_t; diff --git a/include/hw/opentitan/ot_lc_ctrl.h b/include/hw/opentitan/ot_lc_ctrl.h index c620ce787c2b8..6b733cd2c67f5 100644 --- a/include/hw/opentitan/ot_lc_ctrl.h +++ b/include/hw/opentitan/ot_lc_ctrl.h @@ -39,8 +39,8 @@ OBJECT_DECLARE_TYPE(OtLcCtrlState, OtLcCtrlClass, OT_LC_CTRL) #define OT_LC_A0_FORCE_RAW TYPE_OT_LC_CTRL "-a0-force-raw" /* output lines */ -#define OT_LC_BROADCAST TYPE_OT_LC_CTRL "-broadcast" -#define OT_LC_CTRL_SOCDBG TYPE_OT_LC_CTRL "-socdbg" +#define OT_LC_BROADCAST TYPE_OT_LC_CTRL "-broadcast" +#define OT_LC_SOC_DBG TYPE_OT_LC_CTRL "-soc-dbg" /* Life cycle broadcast signals (booleans) */ typedef enum { @@ -57,11 +57,14 @@ typedef enum { OT_LC_ISO_PART_SW_RD_EN, /* for embed. flash, not used in DJ */ OT_LC_ISO_PART_SW_WR_EN, /* for embed. flash, not used in DJ */ OT_LC_SEED_HW_RD_EN, /* for OTP and embed. flash */ + OT_LC_RMA, /* RMA state */ OT_LC_BROADCAST_COUNT, } OtLcCtrlBroadcast; #define OT_LC_KEYMGR_DIV_BYTES 16u /* 128 bits */ +#define OT_LC_SOC_DBG_STATE_COUNT 3u + typedef struct { uint8_t data[OT_LC_KEYMGR_DIV_BYTES]; } OtLcCtrlKeyMgrDiv; @@ -73,10 +76,21 @@ struct OtLcCtrlClass { /* * Retrieve key manager diversification value. * + * @s the LC controller instance * @div a pointer to a structure that will be filled with the key manager * diversification data. */ void (*get_keymgr_div)(const OtLcCtrlState *s, OtLcCtrlKeyMgrDiv *div); + + /* + * Expose SoC debug state value + * + * @s the LC controller instance + * @state the soc debug state index to obtain the code for + * @return LC soc debug state value or UINT32_MAX if the value of the state + * is unknown + */ + uint32_t (*get_soc_dbg_state)(const OtLcCtrlState *s, unsigned state); }; #endif /* HW_OPENTITAN_OT_LC_CTRL_H */ diff --git a/include/hw/opentitan/ot_pwrmgr.h b/include/hw/opentitan/ot_pwrmgr.h index beeaa220c45a1..ec41e2ac0d058 100644 --- a/include/hw/opentitan/ot_pwrmgr.h +++ b/include/hw/opentitan/ot_pwrmgr.h @@ -52,19 +52,24 @@ typedef enum { OT_PWRMGR_WAKEUP_COUNT, } OtPwrMgrWakeup; +#define OT_PWRMGR_MAX_ROM_CTRL_COUNT 3u +#define OT_PWRMGR_MAX_ROM_COUNT (OT_PWRMGR_MAX_ROM_CTRL_COUNT + 1u) + /* Boot status packed as an IRQ */ typedef union { struct { - unsigned main_ip_clk_en:1; - unsigned io_ip_clk_en:1; - unsigned usb_ip_clk_en:1; /* ignored for now */ - unsigned otp_done:1; - unsigned lc_done:1; - unsigned cpu_fetch_en:1; - unsigned strap_sampled:1; - unsigned light_reset:1; - unsigned rom_done:4; - unsigned rom_good:4; + unsigned main_clk_status:1u; /* in pwr_clk_rsp_t */ + unsigned io_clk_status:1u; /* in pwr_clk_rsp_t */ + unsigned otp_done:1u; + unsigned lc_done:1u; + unsigned cpu_fetch_en:1u; + unsigned strap_sampled:1u; + unsigned light_reset:1u; + /* clang-format off */ + unsigned rom_done:OT_PWRMGR_MAX_ROM_COUNT; + unsigned rom_good:OT_PWRMGR_MAX_ROM_COUNT; + unsigned rom_mask:OT_PWRMGR_MAX_ROM_COUNT; + /* clang-format on */ }; int i32; } OtPwrMgrBootStatus; diff --git a/include/hw/opentitan/ot_socdbg_ctrl.h b/include/hw/opentitan/ot_soc_dbg_ctrl.h similarity index 55% rename from include/hw/opentitan/ot_socdbg_ctrl.h rename to include/hw/opentitan/ot_soc_dbg_ctrl.h index 3976dc57327eb..bb45b04a97b86 100644 --- a/include/hw/opentitan/ot_socdbg_ctrl.h +++ b/include/hw/opentitan/ot_soc_dbg_ctrl.h @@ -25,35 +25,50 @@ * THE SOFTWARE. */ -#ifndef HW_OPENTITAN_OT_SOCDBG_CTRL_H -#define HW_OPENTITAN_OT_SOCDBG_CTRL_H +#ifndef HW_OPENTITAN_OT_SOC_DBG_CTRL_H +#define HW_OPENTITAN_OT_SOC_DBG_CTRL_H #include "qom/object.h" -#define TYPE_OT_SOCDBG_CTRL "ot-socdbg_ctrl" -OBJECT_DECLARE_TYPE(OtSoCDbgCtrlState, OtSoCDbgCtrlClass, OT_SOCDBG_CTRL) +#define TYPE_OT_SOC_DBG_CTRL "ot-soc_dbg_ctrl" +OBJECT_DECLARE_TYPE(OtSoCDbgCtrlState, OtSoCDbgCtrlClass, OT_SOC_DBG_CTRL) /* SocDbg controller states */ typedef enum { - OT_SOCDBG_ST_RAW, - OT_SOCDBG_ST_PRE_PROD, - OT_SOCDBG_ST_PROD, - OT_SOCDBG_ST_COUNT, + OT_SOC_DBG_ST_BLANK, + OT_SOC_DBG_ST_PRE_PROD, + OT_SOC_DBG_ST_PROD, + OT_SOC_DBG_ST_COUNT, } OtSoCDbgState; +/* Signal carried over OT_SOC_DBG_DEBUG_POLICY */ +typedef union { + struct { + /* + * Active categories: + * b0..b1: unused + * b2: CAT2 + * b3: CAT3 + * b4: CAT4 + * b5..b7: unused + */ + uint8_t cat_bm; + bool relocked; + }; + int i32; +} OtSocDbgDebugPolicy; + /* input lines */ -#define OT_SOCDBG_HALT_CPU_BOOT TYPE_OT_SOCDBG_CTRL "-halt-cpu-boot" -#define OT_SOCDBG_LC_BCAST TYPE_OT_SOCDBG_CTRL "-lc-broacast" -#define OT_SOCDBG_STATE TYPE_OT_SOCDBG_CTRL "-socdbg" -#define OT_SOCDBG_BOOT_STATUS TYPE_OT_SOCDBG_CTRL "-boot-status" -#define OT_SOCDBG_A0_DEBUG_EN TYPE_OT_SOCDBG_CTRL "-a0-debug-en" -#define OT_SOCDBG_A0_FORCE_RAW TYPE_OT_SOCDBG_CTRL "-a0-force-raw" +#define OT_SOC_DBG_HALT_CPU_BOOT TYPE_OT_SOC_DBG_CTRL "-halt-cpu-boot" +#define OT_SOC_DBG_LC_BCAST TYPE_OT_SOC_DBG_CTRL "-lc-broadcast" +#define OT_SOC_DBG_STATE TYPE_OT_SOC_DBG_CTRL "-soc-dbg" +#define OT_SOC_DBG_BOOT_STATUS TYPE_OT_SOC_DBG_CTRL "-boot-status" /* output lines */ -#define OT_SOCDBG_CPU_BOOT TYPE_OT_SOCDBG_CTRL "-cpu-boot" -#define OT_SOCDBG_DEBUG_POLICY TYPE_OT_SOCDBG_CTRL "-debug-policy" +#define OT_SOC_DBG_CONTINUE_CPU_BOOT TYPE_OT_SOC_DBG_CTRL "-continue-cpu-boot" +#define OT_SOC_DBG_DEBUG_POLICY TYPE_OT_SOC_DBG_CTRL "-debug-policy" -#define OT_SOCDBG_DEBUG_POLICY_MASK 0x0fu -#define OT_SOCDBG_DEBUG_VALID_MASK 0x80u +#define OT_SOC_DBG_DEBUG_POLICY_MASK 0x0fu +#define OT_SOC_DBG_DEBUG_VALID_MASK 0x80u -#endif /* HW_OPENTITAN_OT_SOCDBG_CTRL_H */ +#endif /* HW_OPENTITAN_OT_SOC_DBG_CTRL_H */ diff --git a/python/qemu/ot/lc_ctrl/const.py b/python/qemu/ot/lc_ctrl/const.py index 5310ade046c63..32d16be97f06a 100644 --- a/python/qemu/ot/lc_ctrl/const.py +++ b/python/qemu/ot/lc_ctrl/const.py @@ -21,7 +21,7 @@ def __init__(self): self._log = getLogger('lc.const') self._states: dict[str, tuple[str, str]] = {} self._tokens: dict[str, str] = {} - self._diversifiers: dict [str, str] = {} + self._diversifiers: dict[str, str] = {} def load_sv(self, svp: TextIO) -> None: """Decode LC information from a System Verilog file. @@ -50,14 +50,14 @@ def load_sv(self, svp: TextIO) -> None: int(trans[otrans[0]]), int(trans[otrans[1]])) self._tokens.update(lcext.get_tokens(False, False)) - socdbg = lcext.get_configuration('SOCDBG') - if socdbg: - for raw in {s for s in socdbg if int(s, 16) == 0}: - del socdbg[raw] - osoc = list(socdbg) - self._states['socdbg'] = osoc[0], osoc[-1] + soc_dbg = lcext.get_configuration('SOC_DBG') + if soc_dbg: + for raw in {s for s in soc_dbg if int(s, 16) == 0}: + del soc_dbg[raw] + osoc = list(soc_dbg) + self._states['soc_dbg'] = osoc[0], osoc[-1] self._log.info("Socdbg first: '%s', last '%s'", - socdbg[osoc[0]], socdbg[osoc[-1]]) + soc_dbg[osoc[0]], soc_dbg[osoc[-1]]) ownership = lcext.get_configuration('OWNERSHIP') if ownership: for raw in {s for s in ownership if int(s, 16) == 0}: diff --git a/python/qemu/ot/otp/lifecycle.py b/python/qemu/ot/otp/lifecycle.py index dff2a14a7e83a..3d33704f5f03a 100644 --- a/python/qemu/ot/otp/lifecycle.py +++ b/python/qemu/ot/otp/lifecycle.py @@ -92,7 +92,7 @@ def load(self, svp: TextIO): mkind, conv = {'lcst': ('LC_STATE', str), 'lccnt': ('LC_TRANSITION_CNT', int), 'ownershipst': ('OWNERSHIP', str), - 'socdbgst': ('SOCDBG', str)}[kind] + 'socdbgst': ('SOC_DBG', str)}[kind] self._tables[mkind] = {} for ref, seq in seqs.items(): seq = ''.join((f'{x:04x}'for x in map(codes.get, seq))) diff --git a/scripts/opentitan/darjeeling-ocd.cfg b/scripts/opentitan/darjeeling-ocd.cfg index 0298ab34523db..c1d180e041f28 100644 --- a/scripts/opentitan/darjeeling-ocd.cfg +++ b/scripts/opentitan/darjeeling-ocd.cfg @@ -13,10 +13,10 @@ transport select jtag set _IRLENGTH 5 # DMI addresses -set _OT_DJ_DEBUG_RV_DM_ADDR 0x0 -set _OT_DJ_DEBUG_MBX_JTAG_ADDR 0x880 -set _OT_DJ_DEBUG_SOCDBG_CTRL_ADDR 0x8c0 -set _OT_DJ_DEBUG_LC_CTRL_ADDR 0xc00 +set _OT_DJ_DEBUG_RV_DM_ADDR 0x0 +set _OT_DJ_DEBUG_MBX_JTAG_ADDR 0x880 +set _OT_DJ_DEBUG_SOC_DBG_CTRL_ADDR 0x8c0 +set _OT_DJ_DEBUG_LC_CTRL_ADDR 0xc00 set _CHIPNAME riscv # part 1, ver 0, lowRISC