| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/lib/dif/dif_alert_handler.h" |
| |
| #include <assert.h> |
| |
| #include "sw/device/lib/base/bitfield.h" |
| |
| #include "alert_handler_regs.h" // Generated. |
| |
| static_assert(ALERT_HANDLER_PARAM_N_CLASSES == 4, |
| "Expected four alert classes!"); |
| static_assert(ALERT_HANDLER_PARAM_N_ESC_SEV == 4, |
| "Expected four escalation signals!"); |
| static_assert(ALERT_HANDLER_PARAM_N_PHASES == 4, |
| "Expected four escalation phases!"); |
| |
| dif_alert_handler_result_t dif_alert_handler_init( |
| dif_alert_handler_params_t params, dif_alert_handler_t *handler) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| // TODO: We currently don't support more than 16 alerts correctly. |
| // See: #3826 |
| if (params.alert_count > 16) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| // For now, the hardware is hardwired to four signals. |
| if (params.escalation_signal_count != ALERT_HANDLER_PARAM_N_ESC_SEV) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| handler->params = params; |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| /** |
| * Classifies alerts for a single alert class. Returns `false` if any of the |
| * provided configuration is invalid. |
| */ |
| DIF_WARN_UNUSED_RESULT |
| static bool classify_alerts(const dif_alert_handler_t *handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->alerts == NULL && class->alerts_len != 0) { |
| return false; |
| } |
| |
| uint32_t enable_reg = mmio_region_read32( |
| handler->params.base_addr, ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET); |
| uint32_t alerts_reg = |
| mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET); |
| |
| for (int i = 0; i < class->alerts_len; ++i) { |
| if (class->alerts[i] >= handler->params.alert_count) { |
| return false; |
| } |
| |
| // Note: the value in alerts[i] corresponds directly to the bit index within |
| // the register. (I.e., alert N has enable bit N). |
| enable_reg = bitfield_bit32_write(enable_reg, class->alerts[i], true); |
| |
| uint32_t classification; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSA; |
| break; |
| case kDifAlertHandlerClassB: |
| classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSB; |
| break; |
| case kDifAlertHandlerClassC: |
| classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSC; |
| break; |
| case kDifAlertHandlerClassD: |
| classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSD; |
| break; |
| default: |
| return false; |
| } |
| |
| // TODO: Currently, we assume all fields are of equal width. |
| // See: #3826 |
| uint32_t field_width = bitfield_popcount32( |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_MASK); |
| uint32_t field_offset = field_width * class->alerts[i]; |
| |
| alerts_reg = bitfield_field32_write( |
| alerts_reg, |
| (bitfield_field32_t){ |
| .mask = ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_MASK, |
| .index = field_offset, |
| }, |
| classification); |
| } |
| |
| mmio_region_write32_shadowed(handler->params.base_addr, |
| ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET, |
| enable_reg); |
| mmio_region_write32_shadowed(handler->params.base_addr, |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET, |
| alerts_reg); |
| return true; |
| } |
| |
| /** |
| * Classifies local alerts for a single alert class. Returns `false` if any of |
| * the provided configuration is invalid. |
| */ |
| DIF_WARN_UNUSED_RESULT |
| static bool classify_local_alerts( |
| const dif_alert_handler_t *handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->local_alerts == NULL && class->local_alerts_len != 0) { |
| return false; |
| } |
| |
| uint32_t enable_reg = |
| mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_REG_OFFSET); |
| uint32_t alerts_reg = |
| mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_REG_OFFSET); |
| |
| for (int i = 0; i < class->local_alerts_len; ++i) { |
| uint32_t classification; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSA; |
| break; |
| case kDifAlertHandlerClassB: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSB; |
| break; |
| case kDifAlertHandlerClassC: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSC; |
| break; |
| case kDifAlertHandlerClassD: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSD; |
| break; |
| default: |
| return false; |
| } |
| |
| bitfield_bit32_index_t enable_bit; |
| bitfield_field32_t field; |
| switch (class->local_alerts[i]) { |
| case kDifAlertHandlerLocalAlertAlertPingFail: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_EN_LA_0_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationPingFail: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_1_EN_LA_1_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_1_CLASS_LA_1_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertAlertIntegrityFail: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_2_EN_LA_2_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_2_CLASS_LA_2_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationIntegrityFail: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_3_EN_LA_3_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_3_CLASS_LA_3_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedUpdateError: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_4_EN_LA_4_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_4_CLASS_LA_4_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedStorageError: |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_4_EN_LA_4_BIT; |
| field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_4_CLASS_LA_4_FIELD; |
| break; |
| default: |
| return false; |
| } |
| |
| enable_reg = bitfield_bit32_write(enable_reg, enable_bit, true); |
| alerts_reg = bitfield_field32_write(alerts_reg, field, classification); |
| } |
| |
| mmio_region_write32_shadowed(handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_REG_OFFSET, |
| enable_reg); |
| mmio_region_write32_shadowed( |
| handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_REG_OFFSET, alerts_reg); |
| return true; |
| } |
| |
| /** |
| * Converts a toggle_t to bool. |
| * |
| * Returns false if `toggle` is out of range. |
| */ |
| DIF_WARN_UNUSED_RESULT |
| static bool toggle_to_bool(dif_alert_handler_toggle_t toggle, bool *flag) { |
| switch (toggle) { |
| case kDifAlertHandlerToggleEnabled: |
| *flag = true; |
| break; |
| case kDifAlertHandlerToggleDisabled: |
| *flag = false; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Configures the control registers of a particular alert handler class. |
| */ |
| DIF_WARN_UNUSED_RESULT |
| static bool configure_class(const dif_alert_handler_t *handler, |
| const dif_alert_handler_class_config_t *class) { |
| ptrdiff_t reg_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| |
| // NOTE: from this point on, we assume that Class A's constants are |
| // representative of all alert class control register layouts. |
| |
| // We're going to configure the entire register, so there's no point in |
| // reading it. In particular, this makes sure that the enable bits for each |
| // escalation signal default to disabled. |
| uint32_t ctrl_reg = 0; |
| |
| // Configure the escalation protocol enable flag. |
| bool use_escalation_protocol; |
| if (!toggle_to_bool(class->use_escalation_protocol, |
| &use_escalation_protocol)) { |
| return false; |
| } |
| ctrl_reg = |
| bitfield_bit32_write(ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT, |
| use_escalation_protocol); |
| |
| // Configure the escalation protocol auto-lock flag. |
| bool automatic_locking; |
| if (!toggle_to_bool(class->automatic_locking, &automatic_locking)) { |
| return false; |
| } |
| ctrl_reg = bitfield_bit32_write( |
| ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT, automatic_locking); |
| |
| if (class->phase_signals == NULL && class->phase_signals_len != 0) { |
| return false; |
| } |
| // Configure the escalation signals for each escalation phase. In particular, |
| // if an escalation phase is configured, it is also enabled. |
| for (int i = 0; i < class->phase_signals_len; ++i) { |
| if (class->phase_signals[i].signal >= |
| handler->params.escalation_signal_count) { |
| return false; |
| } |
| |
| bitfield_bit32_index_t enable_bit; |
| bitfield_field32_t map_field; |
| switch (class->phase_signals[i].phase) { |
| case kDifAlertHandlerClassStatePhase0: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase1: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase2: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase3: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD; |
| break; |
| default: |
| return false; |
| } |
| |
| ctrl_reg = bitfield_bit32_write(ctrl_reg, enable_bit, true); |
| ctrl_reg = bitfield_field32_write(ctrl_reg, map_field, |
| class->phase_signals[i].signal); |
| } |
| |
| mmio_region_write32_shadowed(handler->params.base_addr, reg_offset, ctrl_reg); |
| |
| // Configure the class accumulator threshold. |
| ptrdiff_t acc_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| acc_offset = ALERT_HANDLER_CLASSA_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| acc_offset = ALERT_HANDLER_CLASSB_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| acc_offset = ALERT_HANDLER_CLASSC_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| acc_offset = ALERT_HANDLER_CLASSD_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| mmio_region_write32_shadowed(handler->params.base_addr, acc_offset, |
| class->accumulator_threshold); |
| |
| // Configure the class IRQ deadline. |
| ptrdiff_t deadline_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| deadline_offset = ALERT_HANDLER_CLASSA_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| deadline_offset = ALERT_HANDLER_CLASSB_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| deadline_offset = ALERT_HANDLER_CLASSC_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| deadline_offset = ALERT_HANDLER_CLASSD_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| mmio_region_write32_shadowed(handler->params.base_addr, deadline_offset, |
| class->irq_deadline_cycles); |
| |
| return true; |
| } |
| |
| /** |
| * Configures phase durations of a particular class. |
| */ |
| DIF_WARN_UNUSED_RESULT |
| static bool configure_phase_durations( |
| const dif_alert_handler_t *handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->phase_durations == NULL && class->phase_durations_len != 0) { |
| return false; |
| } |
| |
| for (int i = 0; i < class->phase_durations_len; ++i) { |
| // To save on writing a fairly ridiculous `if` chain, we use a lookup table |
| // that leverages the numeric values of enum constants. |
| static const ptrdiff_t kRegOffsets |
| [ALERT_HANDLER_PARAM_N_CLASSES][ALERT_HANDLER_PARAM_N_PHASES] = { |
| [kDifAlertHandlerClassA] = |
| { |
| ALERT_HANDLER_CLASSA_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassB] = |
| { |
| ALERT_HANDLER_CLASSB_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassC] = |
| { |
| ALERT_HANDLER_CLASSC_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassD] = |
| { |
| ALERT_HANDLER_CLASSD_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| }; |
| |
| if (class->alert_class >= ALERT_HANDLER_PARAM_N_CLASSES) { |
| return false; |
| } |
| |
| // NOTE: phase need not be an escalation phase! |
| dif_alert_handler_class_state_t phase = class->phase_durations[i].phase; |
| if (phase < kDifAlertHandlerClassStatePhase0 || |
| phase > kDifAlertHandlerClassStatePhase3) { |
| return false; |
| } |
| ptrdiff_t reg_offset = |
| kRegOffsets[class->alert_class] |
| [phase - kDifAlertHandlerClassStatePhase0]; |
| |
| mmio_region_write32_shadowed(handler->params.base_addr, reg_offset, |
| class->phase_durations[i].cycles); |
| } |
| |
| return true; |
| } |
| |
| dif_alert_handler_config_result_t dif_alert_handler_configure( |
| const dif_alert_handler_t *handler, dif_alert_handler_config_t config) { |
| if (handler == NULL) { |
| return kDifAlertHandlerConfigBadArg; |
| } |
| // Check that the provided ping timeout actually fits in the timeout register, |
| // which is smaller than a native word length. |
| if (config.ping_timeout > |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK) { |
| return kDifAlertHandlerConfigBadArg; |
| } |
| if (config.classes == NULL && config.classes_len != 0) { |
| return kDifAlertHandlerConfigBadArg; |
| } |
| |
| bool is_locked; |
| dif_alert_handler_result_t result = |
| dif_alert_handler_is_locked(handler, &is_locked); |
| if (result != kDifAlertHandlerOk) { |
| return (dif_alert_handler_config_result_t)result; |
| } |
| if (is_locked) { |
| return kDifAlertHandlerConfigLocked; |
| } |
| |
| for (int i = 0; i < config.classes_len; ++i) { |
| if (!classify_alerts(handler, &config.classes[i])) { |
| return kDifAlertHandlerConfigError; |
| } |
| if (!classify_local_alerts(handler, &config.classes[i])) { |
| return kDifAlertHandlerConfigError; |
| } |
| if (!configure_class(handler, &config.classes[i])) { |
| return kDifAlertHandlerConfigError; |
| } |
| if (!configure_phase_durations(handler, &config.classes[i])) { |
| return kDifAlertHandlerConfigError; |
| } |
| } |
| |
| uint32_t ping_timeout_reg = bitfield_field32_write( |
| 0, |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_FIELD, |
| config.ping_timeout); |
| mmio_region_write32_shadowed( |
| handler->params.base_addr, |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_REG_OFFSET, ping_timeout_reg); |
| |
| return kDifAlertHandlerConfigOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_lock( |
| const dif_alert_handler_t *handler) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write( |
| 1, ALERT_HANDLER_PING_TIMER_EN_SHADOWED_PING_TIMER_EN_SHADOWED_BIT, true); |
| mmio_region_write32_shadowed(handler->params.base_addr, |
| ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET, |
| reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_is_locked( |
| const dif_alert_handler_t *handler, bool *is_locked) { |
| if (handler == NULL || is_locked == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = |
| mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET); |
| *is_locked = bitfield_bit32_read( |
| reg, ALERT_HANDLER_PING_TIMER_EN_SHADOWED_PING_TIMER_EN_SHADOWED_BIT); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| DIF_WARN_UNUSED_RESULT |
| static bool irq_index(dif_alert_handler_class_t class, |
| bitfield_bit32_index_t *index) { |
| switch (class) { |
| case kDifAlertHandlerClassA: |
| *index = ALERT_HANDLER_INTR_COMMON_CLASSA_BIT; |
| break; |
| case kDifAlertHandlerClassB: |
| *index = ALERT_HANDLER_INTR_COMMON_CLASSB_BIT; |
| break; |
| case kDifAlertHandlerClassC: |
| *index = ALERT_HANDLER_INTR_COMMON_CLASSC_BIT; |
| break; |
| case kDifAlertHandlerClassD: |
| *index = ALERT_HANDLER_INTR_COMMON_CLASSD_BIT; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_is_pending( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| bool *is_pending) { |
| if (handler == NULL || is_pending == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!irq_index(alert_class, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_STATE_REG_OFFSET); |
| *is_pending = bitfield_bit32_read(reg, index); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_acknowledge( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!irq_index(alert_class, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_STATE_REG_OFFSET); |
| reg = bitfield_bit32_write(reg, index, true); // Write-one clear. |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_STATE_REG_OFFSET, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_get_enabled( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| dif_alert_handler_toggle_t *state) { |
| if (handler == NULL || state == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!irq_index(alert_class, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET); |
| *state = bitfield_bit32_read(reg, index) ? kDifAlertHandlerToggleEnabled |
| : kDifAlertHandlerToggleDisabled; |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_set_enabled( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| dif_alert_handler_toggle_t state) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!irq_index(alert_class, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bool flag; |
| if (!toggle_to_bool(state, &flag)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET); |
| reg = bitfield_bit32_write(reg, index, flag); |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_force( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!irq_index(alert_class, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write(0, index, true); |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_TEST_REG_OFFSET, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_disable_all( |
| const dif_alert_handler_t *handler, |
| dif_alert_handler_irq_snapshot_t *snapshot) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| if (snapshot != NULL) { |
| *snapshot = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET); |
| } |
| |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET, 0); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_irq_restore_all( |
| const dif_alert_handler_t *handler, |
| const dif_alert_handler_irq_snapshot_t *snapshot) { |
| if (handler == NULL || snapshot == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_INTR_ENABLE_REG_OFFSET, *snapshot); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_alert_is_cause( |
| const dif_alert_handler_t *handler, dif_alert_handler_alert_t alert, |
| bool *is_cause) { |
| if (handler == NULL || is_cause == NULL || |
| alert >= handler->params.alert_count) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET); |
| *is_cause = bitfield_bit32_read(reg, alert); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_alert_acknowledge( |
| const dif_alert_handler_t *handler, dif_alert_handler_alert_t alert) { |
| if (handler == NULL || alert >= handler->params.alert_count) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write(0, alert, true); |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| DIF_WARN_UNUSED_RESULT |
| static bool loc_alert_index(dif_alert_handler_local_alert_t alert, |
| bitfield_bit32_index_t *index) { |
| switch (alert) { |
| case kDifAlertHandlerLocalAlertAlertPingFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_0_LA_0_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationPingFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_1_LA_1_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertAlertIntegrityFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_2_LA_2_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationIntegrityFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_3_LA_3_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedUpdateError: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_4_LA_4_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedStorageError: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_5_LA_5_BIT; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_local_alert_is_cause( |
| const dif_alert_handler_t *handler, dif_alert_handler_local_alert_t alert, |
| bool *is_cause) { |
| if (handler == NULL || is_cause == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!loc_alert_index(alert, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_CAUSE_0_REG_OFFSET); |
| *is_cause = bitfield_bit32_read(reg, index); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_local_alert_acknowledge( |
| const dif_alert_handler_t *handler, dif_alert_handler_local_alert_t alert) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| bitfield_bit32_index_t index; |
| if (!loc_alert_index(alert, &index)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write(0, index, true); |
| mmio_region_write32(handler->params.base_addr, |
| ALERT_HANDLER_LOC_ALERT_CAUSE_0_REG_OFFSET, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| DIF_WARN_UNUSED_RESULT |
| static bool get_clear_enable_reg_offset(dif_alert_handler_class_t class, |
| ptrdiff_t *reg_offset) { |
| switch (class) { |
| case kDifAlertHandlerClassA: |
| *reg_offset = ALERT_HANDLER_CLASSA_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| *reg_offset = ALERT_HANDLER_CLASSB_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| *reg_offset = ALERT_HANDLER_CLASSC_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| *reg_offset = ALERT_HANDLER_CLASSD_CLR_REGWEN_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_escalation_can_clear( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| bool *can_clear) { |
| if (handler == NULL || can_clear == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| if (!get_clear_enable_reg_offset(alert_class, ®_offset)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, reg_offset); |
| *can_clear = bitfield_bit32_read( |
| reg, ALERT_HANDLER_CLASSA_CLR_REGWEN_CLASSA_CLR_REGWEN_BIT); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_escalation_disable_clearing( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| if (!get_clear_enable_reg_offset(alert_class, ®_offset)) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write( |
| 0, ALERT_HANDLER_CLASSA_CLR_REGWEN_CLASSA_CLR_REGWEN_BIT, true); |
| mmio_region_write32(handler->params.base_addr, reg_offset, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_escalation_clear( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class) { |
| if (handler == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_CLR_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_CLR_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_CLR_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_CLR_REG_OFFSET; |
| break; |
| default: |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = |
| bitfield_bit32_write(0, ALERT_HANDLER_CLASSA_CLR_CLASSA_CLR_BIT, true); |
| mmio_region_write32(handler->params.base_addr, reg_offset, reg); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_get_accumulator( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| uint16_t *alerts) { |
| if (handler == NULL || alerts == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| bitfield_field32_t field; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSA_ACCUM_CNT_CLASSA_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSB_ACCUM_CNT_CLASSB_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSC_ACCUM_CNT_CLASSC_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSD_ACCUM_CNT_CLASSD_ACCUM_CNT_FIELD; |
| break; |
| default: |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, reg_offset); |
| *alerts = bitfield_field32_read(reg, field); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_get_escalation_counter( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| uint32_t *cycles) { |
| if (handler == NULL || cycles == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_ESC_CNT_REG_OFFSET; |
| break; |
| default: |
| return kDifAlertHandlerBadArg; |
| } |
| |
| *cycles = mmio_region_read32(handler->params.base_addr, reg_offset); |
| |
| return kDifAlertHandlerOk; |
| } |
| |
| dif_alert_handler_result_t dif_alert_handler_get_class_state( |
| const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class, |
| dif_alert_handler_class_state_t *state) { |
| if (handler == NULL || state == NULL) { |
| return kDifAlertHandlerBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| bitfield_field32_t field; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSB_STATE_CLASSB_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSC_STATE_CLASSC_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSD_STATE_CLASSD_STATE_FIELD; |
| break; |
| default: |
| return kDifAlertHandlerBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(handler->params.base_addr, reg_offset); |
| switch (bitfield_field32_read(reg, field)) { |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_IDLE: |
| *state = kDifAlertHandlerClassStateIdle; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TIMEOUT: |
| *state = kDifAlertHandlerClassStateTimeout; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE0: |
| *state = kDifAlertHandlerClassStatePhase0; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE1: |
| *state = kDifAlertHandlerClassStatePhase1; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE2: |
| *state = kDifAlertHandlerClassStatePhase2; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE3: |
| *state = kDifAlertHandlerClassStatePhase3; |
| break; |
| case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TERMINAL: |
| *state = kDifAlertHandlerClassStateTerminal; |
| break; |
| default: |
| return kDifAlertHandlerError; |
| } |
| |
| return kDifAlertHandlerOk; |
| } |