blob: 7e0f86f9898c55250d47a9c4906018c3ac16312f [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This test triggers a regfile integrity error from the SV side for any
// random IP instance. The alert handler is programmed to trigger a specific
// alert for the corresponding IP.
//
// The test checks that the alert handler state indicates the correct alert
// prior to the alert, which is checked in the alert triggered NMI. The
// test also checks that the alert handler cleared that state after reset.
// It also checks that for some IPs the corresponding bit in the fatal error
// CSR is set in the interrupt, and it is also cleared after reset.
//
// For extra checking, the rstmgr is configured to capture the alert info
// on reset, which is also used to check the alert cause is as expected.
//
// If the fault is injected in the retention SRAM, we perform an additional
// access check to make sure that local escalation blocks any SRAM accesses
// correctly.
//
// As a backup the aon timer is programmed to bark and bite, but these are
// expected not to happen since the escalation takes precedence.
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/math.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_aes.h"
#include "sw/device/lib/dif/dif_alert_handler.h"
#include "sw/device/lib/dif/dif_aon_timer.h"
#include "sw/device/lib/dif/dif_clkmgr.h"
#include "sw/device/lib/dif/dif_flash_ctrl.h"
#include "sw/device/lib/dif/dif_kmac.h"
#include "sw/device/lib/dif/dif_lc_ctrl.h"
#include "sw/device/lib/dif/dif_otp_ctrl.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rom_ctrl.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_core_ibex.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/dif/dif_sram_ctrl.h"
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/irq.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/alert_handler_testutils.h"
#include "sw/device/lib/testing/aon_timer_testutils.h"
#include "sw/device/lib/testing/flash_ctrl_testutils.h"
#include "sw/device/lib/testing/rand_testutils.h"
#include "sw/device/lib/testing/rstmgr_testutils.h"
#include "sw/device/lib/testing/rv_plic_testutils.h"
#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "alert_handler_regs.h"
#include "flash_ctrl_regs.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
OTTF_DEFINE_TEST_CONFIG();
// This location will be update from SV to contain the expected alert.
static volatile const uint8_t kExpectedAlertNumber = 0;
// Used for checking whether a load access exception has occurred.
static volatile bool load_access_exception_seen = false;
// Used for checking whether a regular alert interrupt has been seen.
static volatile bool alert_irq_seen = false;
// The function to check the fault status.
typedef void (*FaultCheckerFunction)(bool, const char *inst, const char *type);
typedef struct fault_checker {
FaultCheckerFunction function;
const char *ip_inst;
const char *type;
} fault_checker_t;
// This preserves the fault checker across multiple resets.
OT_SECTION(".non_volatile_scratch") uint64_t nv_fault_checker[3];
// This is the fault checker to be used. It is saved and retrieved from flash
// to preserve it across resets.
fault_checker_t fault_checker;
// Alert class to use for the test. Will be chosen randomly by the test SW.
static volatile dif_alert_handler_class_t alert_class_to_use;
static volatile uint32_t reset_count;
enum {
// Counter for resets.
kCounterReset,
// Counter for regular interrupts.
kCounterInterrupt,
// Counter for NMIs.
kCounterNmi,
};
/**
* Program the alert handler to escalate on alerts through NMI and then reset.
* Also program the aon timer with:
* - bark after escalation starts, so the interrupt is suppressed by escalation,
* - bite after escalation reset, so we should not get timer reset.
*/
enum {
// Note that the escalation phase times below define the length of each phase,
// not when they start.
// The starting time is given by the aggregate of previous phase lengths, and
// is noted with @ below.
// @0 us -> in this phase we will not do anything so that the exception
// handlers have time to execute.
kEscalationPhase0Micros = 200,
// @200 us -> in this phase we will raise an NMI
kEscalationPhase1Micros = 200,
// @400 us -> in this phase we will assert lc_escalate_en
kEscalationPhase2Micros = 200,
// @600 us -> in this phase we will reset the chip
kEscalationPhase3Micros = 200,
// These are set so that both events happen in Phase2 of the escalation
// protocol, which asserts lc_escalate_en. That should prevent the Wdog
// from running and sending out an NMI on its own (we check in the NMI
// handler below that this does not happen).
kWdogBarkMicros = 450,
kWdogBiteMicros = 500,
kTestTimeout = 1000, // 1000 us
kMaxResets = 2,
kMaxInterrupts = 30,
};
static_assert(
kWdogBarkMicros < kWdogBiteMicros &&
kWdogBarkMicros > (kEscalationPhase0Micros + kEscalationPhase1Micros),
"The wdog bite shall after the NMI phase when lc_escalate_en is asserted");
/**
* SRAM addresses used in the test below.
*/
enum {
kSramMainStart = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR,
kSramRetStart = TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR,
};
/**
* Objects to access the peripherals used in this test via dif API.
*/
static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
static dif_aes_t aes;
static dif_alert_handler_t alert_handler;
static dif_aon_timer_t aon_timer;
static dif_clkmgr_t clkmgr;
static dif_flash_ctrl_state_t flash_ctrl_state;
static dif_kmac_t kmac;
static dif_lc_ctrl_t lc_ctrl;
static dif_otp_ctrl_t otp_ctrl;
static dif_pwrmgr_t pwrmgr;
static dif_rom_ctrl_t rom_ctrl;
static dif_rstmgr_t rstmgr;
static dif_rv_core_ibex_t rv_core_ibex;
static dif_rv_plic_t plic;
static dif_sram_ctrl_t sram_ctrl_main;
static dif_sram_ctrl_t sram_ctrl_ret;
static const char *sparse_fsm_check = "prim_sparse_fsm_flop";
static const char *we_check = "prim_reg_we_check";
static const char *rst_cnsty_check = "rst_cnsty_check";
static const char *flash_fatal_check = "flash_fatal_check";
static void save_fault_checker(fault_checker_t *fault_checker) {
uint32_t function_addr = (uint32_t)(fault_checker->function);
uint32_t ip_inst_addr = (uint32_t)(fault_checker->ip_inst);
uint32_t type_addr = (uint32_t)(fault_checker->type);
CHECK(flash_ctrl_testutils_write(
&flash_ctrl_state,
(uint32_t)(&nv_fault_checker[0]) - TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR,
0, &function_addr, kDifFlashCtrlPartitionTypeData, 1));
CHECK(flash_ctrl_testutils_write(
&flash_ctrl_state,
(uint32_t)(&nv_fault_checker[1]) - TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR,
0, &ip_inst_addr, kDifFlashCtrlPartitionTypeData, 1));
CHECK(flash_ctrl_testutils_write(
&flash_ctrl_state,
(uint32_t)(&nv_fault_checker[2]) - TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR,
0, &type_addr, kDifFlashCtrlPartitionTypeData, 1));
}
static void restore_fault_checker(fault_checker_t *fault_checker) {
fault_checker->function = (FaultCheckerFunction)nv_fault_checker[0];
fault_checker->ip_inst = (char *)nv_fault_checker[1];
fault_checker->type = (char *)nv_fault_checker[2];
}
static const char *no_name = "unidentified";
// It would be handy to generate these.
static const char *adc_ctrl_inst_name = "adc_ctrl";
static const char *aes_inst_name = "aes";
static const char *aon_timer_inst_name = "aon_timer";
static const char *clkmgr_inst_name = "clkmgr";
static const char *csrng_inst_name = "csrng";
static const char *edn0_inst_name = "edn0";
static const char *edn1_inst_name = "edn1";
static const char *entropy_src_inst_name = "entropy_src";
// TODO test u_eflash.u_flash alert 37, 38?
static const char *flash_ctrl_inst_name = "flash_ctrl";
static const char *gpio_inst_name = "gpio";
static const char *hmac_inst_name = "hmac";
static const char *i2c0_inst_name = "i2c0";
static const char *i2c1_inst_name = "i2c1";
static const char *i2c2_inst_name = "i2c2";
static const char *keymgr_inst_name = "keymgr";
static const char *kmac_inst_name = "kmac";
// TODO: test lc_ctrl fatal_state, alert 17.
static const char *lc_ctrl_inst_name = "lc_ctrl";
static const char *otbn_inst_name = "otbn";
// TODO test fatal macro, and fatal prim, alerts 11 and 14.
// They don't have onehot_checkers, cause both u_reg, and u_reg_tap checkers?
static const char *otp_ctrl_inst_name = "otp_ctrl";
static const char *pattgen_inst_name = "pattgen";
static const char *pinmux_inst_name = "pinmux";
static const char *pwm_inst_name = "pwm";
static const char *pwrmgr_inst_name = "pwrmgr";
static const char *rom_ctrl_inst_name = "rom_ctrl";
// TODO: test rstmgr fatal consistency, alert 24.
static const char *rstmgr_inst_name = "rstmgr";
// TODO: test rv_core_ibex fatal SW error, alert 57.
static const char *rv_core_ibex_inst_name = "rv_core_ibex";
static const char *rv_dm_inst_name = "rv_dm";
static const char *rv_plic_inst_name = "rv_plic";
static const char *rv_timer_inst_name = "rv_timer";
static const char *sensor_ctrl_inst_name = "sensor_ctrl";
static const char *spi_host0_inst_name = "spi_host0";
static const char *spi_host1_inst_name = "spi_host1";
static const char *spi_device_inst_name = "spi_device";
static const char *sram_ctrl_main_inst_name = "sram_ctrl_main";
static const char *sram_ctrl_ret_inst_name = "sram_ctrl_ret";
static const char *sysrst_ctrl_inst_name = "sysrst_ctrl";
static const char *uart0_inst_name = "uart0";
static const char *uart1_inst_name = "uart1";
static const char *uart2_inst_name = "uart2";
static const char *uart3_inst_name = "uart3";
static const char *usbdev_inst_name = "usbdev";
static void trivial_fault_checker(bool enable, const char *ip_inst,
const char *type) {
CHECK(enable == enable);
}
static void aes_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the aes integrity fatal error code.
bool status;
CHECK_DIF_OK(dif_aes_get_status(&aes, kDifAesStatusAlertFatalFault, &status));
CHECK(status == enable, "For %s got 0x%x, expected 0x%x", ip_inst, status,
enable);
}
static void clkmgr_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the clkmgr integrity fatal error code.
dif_clkmgr_fatal_err_codes_t codes;
CHECK_DIF_OK(dif_clkmgr_fatal_err_code_get_codes(&clkmgr, &codes));
uint32_t expected = enable ? kDifClkmgrFatalErrTypeRegfileIntegrity : 0;
CHECK(codes == expected, "For %s got codes 0x%x, expected 0x%x", ip_inst,
codes, expected);
}
static void flash_ctrl_fault_checker(bool enable, const char *ip_inst,
const char *type) {
dif_flash_ctrl_faults_t faults;
CHECK_DIF_OK(dif_flash_ctrl_get_faults(&flash_ctrl_state, &faults));
uint32_t fault_code = (type == we_check) ? faults.register_integrity_error
: faults.host_gnt_error;
CHECK(fault_code == enable, "For %s got codes 0x%x, expected 0x%x", ip_inst,
fault_code, enable);
}
/*
// TODO(#14518) hmac cannot read fault_status register.
static void hmac_fault_checker(bool enable) {
// Check the hmac integrity fatal error code.
dif_kmac_status_t status;
CHECK_DIF_OK(dif_kmac_get_status(&keymgr, &codes));
if (enable) {
CHECK(status.faults == 1, "Got faults 0x%x", status.faults);
} else {
CHECK(status.faults == 0, "Got codes 0x%x", status.faults);
}
}
*/
/*
// TODO(#14518) keymgr cannot read fault_status register.
static void keymgr_fault_checker(bool enable) {
// Check the keymgr integrity fatal error code.
dif_kmac_status_t status;
CHECK_DIF_OK(dif_kmac_get_status(&keymgr, &codes));
if (enable) {
CHECK(status.faults == 1, "Got faults 0x%x", status.faults);
} else {
CHECK(status.faults == 0, "Got codes 0x%x", status.faults);
}
}
*/
static void kmac_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the kmac integrity fatal error code.
dif_kmac_status_t status;
uint32_t expected = enable ? 1 : 0;
CHECK_DIF_OK(dif_kmac_get_status(&kmac, &status));
CHECK(status.faults == expected, "For %s got codes 0x%x, expected 0x%x",
ip_inst, status.faults, expected);
}
// This discriminates between the two faults based on type.
static void lc_ctrl_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the lc_ctrl integrity fatal error code.
dif_lc_ctrl_status_t status;
CHECK_DIF_OK(dif_lc_ctrl_get_status(&lc_ctrl, &status));
bitfield_field32_t relevant_field = {
.mask = UINT32_MAX, .index = kDifLcCtrlStatusCodeTooManyTransitions};
uint32_t mask = bitfield_field32_write(0, relevant_field, UINT32_MAX);
uint32_t relevant_status = status & mask;
uint32_t bus_integ_error =
bitfield_bit32_write(0, kDifLcCtrlStatusCodeBusIntegError, true);
uint32_t state_error =
bitfield_bit32_write(0, kDifLcCtrlStatusCodeCorrupt, true);
uint32_t expected_status =
enable ? (type == we_check ? bus_integ_error : state_error) : 0;
CHECK(relevant_status == expected_status,
"For %s got codes 0x%x, expected 0x%x", ip_inst, relevant_status,
expected_status);
}
/*
// TODO(#14518) otbn cannot read fault_status register.
static void otbn_ctrl_fault_checker(bool enable) {
// Check the otbn integrity fatal error code.
dif_otbn_err_bits_t codes;
// TODO we seem to be missing a dif to read FATAL_ALERT_CAUSE.
CHECK_DIF_OK(dif_otbn_get_err_bits(&otbn, &codes));
if (enable) {
CHECK(status.codes == kDifOtbn??, "Got codes 0x%x", codes);
} else {
CHECK(codes == 0, "Got codes 0x%x", codes);
}
}
*/
static void otp_ctrl_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the otp_ctrl integrity fatal error code.
dif_otp_ctrl_status_t status;
uint32_t expected_codes;
uint32_t relevant_codes;
uint32_t relevant_mask =
bitfield_bit32_write(UINT32_MAX, kDifOtpCtrlStatusCodeDaiIdle, false);
relevant_mask = bitfield_bit32_write(
relevant_mask, kDifOtpCtrlStatusCodeCheckPending, false);
CHECK_DIF_OK(dif_otp_ctrl_get_status(&otp_ctrl, &status));
relevant_codes = status.codes & relevant_mask;
dif_otp_ctrl_status_code_t exp_err = (type == we_check)
? kDifOtpCtrlStatusCodeBusIntegError
: kDifOtpCtrlStatusCodeDaiError;
expected_codes = enable ? (1 << exp_err) : 0;
CHECK(relevant_codes == expected_codes,
"For %s got codes 0x%x, expected 0x%x", ip_inst, relevant_codes,
expected_codes);
}
static void pwrmgr_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the pwrmgr integrity fatal error code.
dif_pwrmgr_fatal_err_codes_t codes;
CHECK_DIF_OK(dif_pwrmgr_fatal_err_code_get_codes(&pwrmgr, &codes));
uint32_t expected_codes = enable ? kDifPwrmgrFatalErrTypeRegfileIntegrity : 0;
CHECK(codes == expected_codes, "For %s got codes 0x%x, expected 0x%x",
ip_inst, codes, expected_codes);
}
static void rom_ctrl_fault_checker(bool enable, const char *ip_inst,
const char *type) {
dif_rom_ctrl_fatal_alert_causes_t codes;
CHECK_DIF_OK(dif_rom_ctrl_get_fatal_alert_cause(&rom_ctrl, &codes));
uint32_t expected_codes =
enable ? bitfield_bit32_write(0, kDifRomCtrlFatalAlertCauseIntegrityError,
true)
: 0;
CHECK(codes == expected_codes, "For %s got codes 0x%x, expected 0x%x",
ip_inst, codes, expected_codes);
}
static void rstmgr_fault_checker(bool enable, const char *ip_inst,
const char *type) {
// Check the rstmgr integrity fatal error code.
dif_rstmgr_fatal_err_codes_t codes;
CHECK_DIF_OK(dif_rstmgr_fatal_err_code_get_codes(&rstmgr, &codes));
uint32_t fault_code = (type == we_check)
? kDifRstmgrFatalErrTypeRegfileIntegrity
: kDifRstmgrFatalErrTypeResetConsistency;
uint32_t expected_codes = enable ? fault_code : 0;
CHECK(codes == expected_codes, "For %s got codes 0x%x, expected 0x%x",
ip_inst, codes, expected_codes);
}
static void rv_core_ibex_fault_checker(bool enable, const char *ip_inst,
const char *type) {
dif_rv_core_ibex_error_status_t codes;
CHECK_DIF_OK(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &codes));
// TODO determine which bit is set by onehot_checker:
// kDifRvCoreIbexErrorStatusRegisterTransmissionIntegrity or
// kDifRvCoreIbexErrorStatusFatalResponseIntegrity or
// kDifRvCoreIbexErrorStatusFatalInternalError
uint32_t expected_codes =
enable ? kDifRvCoreIbexErrorStatusRegisterTransmissionIntegrity : 0;
CHECK(codes == expected_codes, "For %s got codes 0x%x, expected 0x%x",
ip_inst, codes, expected_codes);
}
/**
* Load access error exception handler.
*
* Handles load access error exceptions on Ibex.
* This is needed for the SRAM fault check that tries to access
* the retention SRAM after escalation to make sure the access
* is correctly blocked.
*
*/
void ottf_load_store_fault_handler(void) {
LOG_INFO("At load access error handler");
uint32_t mtval = ibex_mtval_read();
CHECK(mtval == kSramRetStart, "Unexpected mtval: expected 0x%x, got 0x%x",
kSramRetStart, mtval);
load_access_exception_seen = true;
LOG_INFO("Load access error handler exiting");
}
static void generic_sram_ctrl_fault_checker(const dif_sram_ctrl_t *sram_ctrl,
bool enable, const char *ip_inst,
const char *type) {
dif_sram_ctrl_status_bitfield_t codes;
CHECK_DIF_OK(dif_sram_ctrl_get_status(sram_ctrl, &codes));
uint32_t expected_codes = enable ? kDifSramCtrlStatusBusIntegErr : 0;
CHECK(codes == expected_codes, "For %s got codes 0x%x, expected 0x%x",
ip_inst, codes, expected_codes);
}
static void sram_ctrl_main_fault_checker(bool enable, const char *ip_inst,
const char *type) {
generic_sram_ctrl_fault_checker(&sram_ctrl_main, enable, ip_inst, type);
}
static void sram_ctrl_ret_fault_checker(bool enable, const char *ip_inst,
const char *type) {
generic_sram_ctrl_fault_checker(&sram_ctrl_ret, enable, ip_inst, type);
}
/**
* External ISR.
*
* Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
* line to the CPU, which results in a call to this OTTF ISR. This ISR
* overrides the default OTTF implementation.
*/
void ottf_external_isr(void) {
dif_rv_plic_irq_id_t irq_id;
LOG_INFO("At regular external ISR");
// There may be multiple interrupts due to the alert firing, so this keeps an
// interrupt counter and errors-out if there are too many interrupts.
// Increment the interrupt count and detect overflows.
uint32_t interrupt_count =
flash_ctrl_testutils_counter_get(kCounterInterrupt);
if (interrupt_count > kMaxInterrupts) {
restore_fault_checker(&fault_checker);
CHECK(false, "For %s, reset count %d got too many interrupts (%d)",
fault_checker.ip_inst, reset_count, interrupt_count);
}
flash_ctrl_testutils_counter_set_at_least(
&flash_ctrl_state, kCounterInterrupt, interrupt_count + 1);
CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
top_earlgrey_plic_peripheral_t peripheral = (top_earlgrey_plic_peripheral_t)
top_earlgrey_plic_interrupt_for_peripheral[irq_id];
if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
uint32_t irq =
(irq_id - (dif_rv_plic_irq_id_t)
kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired);
// We should not get aon timer interrupts since escalation suppresses them.
CHECK(false, "Unexpected aon timer interrupt %d", irq);
} else if (peripheral == kTopEarlgreyPlicPeripheralAlertHandler) {
// Don't acknowledge the interrupt to alert_handler so it escalates.
CHECK(fault_checker.function);
CHECK(fault_checker.ip_inst);
CHECK(fault_checker.type);
// Fatal alerts are only cleared by reset.
fault_checker.function(/*enable=*/true, fault_checker.ip_inst,
fault_checker.type);
}
// Disable these interrupts from alert_handler so they don't keep happening
// until NMI.
uint32_t irq =
(irq_id - (dif_rv_plic_irq_id_t)kTopEarlgreyPlicIrqIdAlertHandlerClassa);
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(&alert_handler, irq,
kDifToggleDisabled));
// Disable this interrupt to prevent it from continuously firing. This
// should not prevent escalation from continuing.
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&plic, irq_id, kPlicTarget,
kDifToggleDisabled));
uint16_t accum_count;
CHECK_DIF_OK(dif_alert_handler_get_accumulator(
&alert_handler, alert_class_to_use, &accum_count));
LOG_INFO("Accumulator count %d", accum_count);
// Complete the IRQ by writing the IRQ source to the Ibex specific CC
// register.
CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
// Notify test function that the alert IRQ has been seen
alert_irq_seen = true;
LOG_INFO("Regular external ISR exiting");
}
/**
* External NMI ISR.
*
* Handles NMI interrupts on Ibex for either escalation or watchdog.
*/
void ottf_external_nmi_handler(void) {
dif_rv_core_ibex_nmi_state_t nmi_state = (dif_rv_core_ibex_nmi_state_t){0};
LOG_INFO("At NMI handler");
// Increment the nmi interrupt count.
uint32_t nmi_count = flash_ctrl_testutils_counter_get(kCounterNmi);
if (nmi_count > kMaxInterrupts) {
LOG_INFO("Saturating nmi interrupts at %d", nmi_count);
} else {
flash_ctrl_testutils_counter_set_at_least(&flash_ctrl_state, kCounterNmi,
nmi_count + 1);
}
// Check that this NMI was due to an alert handler escalation, and not due
// to a watchdog bark, since escalation suppresses the watchdog.
CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
&rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
CHECK(nmi_state.alert_enabled && nmi_state.alert_raised,
"Alert handler NMI state not expected:\n\t"
"alert_enable:%x\n\talert_raised:%x\n",
nmi_state.alert_enabled, nmi_state.alert_raised);
CHECK(nmi_state.wdog_enabled && !nmi_state.wdog_barked,
"Watchdog NMI state not expected:\n\t"
"wdog_enabled:%x\n\twdog_barked:%x\n",
nmi_state.wdog_enabled, nmi_state.wdog_barked);
// Check the class.
dif_alert_handler_class_state_t state;
CHECK_DIF_OK(dif_alert_handler_get_class_state(&alert_handler,
alert_class_to_use, &state));
CHECK(state == kDifAlertHandlerClassStatePhase1, "Wrong phase %d", state);
// Check this gets the expected alert.
bool is_cause = false;
CHECK_DIF_OK(dif_alert_handler_alert_is_cause(
&alert_handler, kExpectedAlertNumber, &is_cause));
CHECK(is_cause);
// Acknowledge the cause, which doesn't affect escalation.
CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler,
kExpectedAlertNumber));
LOG_INFO("NMI handler exiting");
}
/**
* Initialize the peripherals used in this test.
*/
static void init_peripherals(void) {
CHECK_DIF_OK(
dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
CHECK_DIF_OK(dif_alert_handler_init(
mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR),
&alert_handler));
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
CHECK_DIF_OK(dif_clkmgr_init(
mmio_region_from_addr(TOP_EARLGREY_CLKMGR_AON_BASE_ADDR), &clkmgr));
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash_ctrl_state,
mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR)));
CHECK_DIF_OK(
dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac));
CHECK_DIF_OK(dif_lc_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_BASE_ADDR), &lc_ctrl));
CHECK_DIF_OK(dif_otp_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp_ctrl));
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
CHECK_DIF_OK(dif_rom_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_ROM_CTRL_REGS_BASE_ADDR), &rom_ctrl));
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
CHECK_DIF_OK(dif_rv_core_ibex_init(
mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR),
&rv_core_ibex));
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &plic));
CHECK_DIF_OK(dif_sram_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_MAIN_REGS_BASE_ADDR),
&sram_ctrl_main));
CHECK_DIF_OK(dif_sram_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_RET_AON_REGS_BASE_ADDR),
&sram_ctrl_ret));
}
/**
* Program the alert handler to escalate on alerts and reset on phase 2,
* and to start escalation after timing out due to an unacknowledged
* interrupt.
*/
static void alert_handler_config(void) {
alert_class_to_use = (dif_alert_handler_class_t)rand_testutils_gen32_range(
kDifAlertHandlerClassA, kDifAlertHandlerClassD);
dif_alert_handler_alert_t alerts[] = {kExpectedAlertNumber};
dif_alert_handler_class_t alert_classes[] = {alert_class_to_use};
dif_alert_handler_escalation_phase_t esc_phases[] = {
{.phase = kDifAlertHandlerClassStatePhase0,
.signal = 0xFFFFFFFF, // do not trigger any signal, just wait.
.duration_cycles =
alert_handler_testutils_get_cycles_from_us(kEscalationPhase0Micros)},
{.phase = kDifAlertHandlerClassStatePhase1,
.signal = 0, // NMI
.duration_cycles =
alert_handler_testutils_get_cycles_from_us(kEscalationPhase1Micros)},
{.phase = kDifAlertHandlerClassStatePhase2,
.signal = 1, // lc_escalate_en
.duration_cycles =
alert_handler_testutils_get_cycles_from_us(kEscalationPhase2Micros)},
{.phase = kDifAlertHandlerClassStatePhase3,
.signal = 3, // reset
.duration_cycles = alert_handler_testutils_get_cycles_from_us(
kEscalationPhase3Micros)}};
// This test does not leverage the IRQ timeout feature of the alert
// handler, hence deadline_cycles is set to zero. Rather, it triggers
// escalation right away if an alert event is seen, hence threshold = 0;
uint32_t deadline_cycles = 0;
uint32_t threshold = 0;
LOG_INFO("Configuring class %d with %d cycles and %d occurrences",
alert_class_to_use, deadline_cycles, threshold);
dif_alert_handler_class_config_t class_config[] = {{
.auto_lock_accumulation_counter = kDifToggleDisabled,
.accumulator_threshold = threshold,
.irq_deadline_cycles = deadline_cycles,
.escalation_phases = esc_phases,
.escalation_phases_len = ARRAYSIZE(esc_phases),
.crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
}};
dif_alert_handler_class_t classes[] = {alert_class_to_use};
dif_alert_handler_config_t config = {
.alerts = alerts,
.alert_classes = alert_classes,
.alerts_len = ARRAYSIZE(alerts),
.classes = classes,
.class_configs = class_config,
.classes_len = ARRAYSIZE(class_config),
.ping_timeout = 0,
};
alert_handler_testutils_configure_all(&alert_handler, config,
kDifToggleEnabled);
// Enables all alert handler irqs. This allows us to implicitly check that
// we do not get spurious IRQs from the classes that are unused.
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassc, kDifToggleEnabled));
CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
&alert_handler, kDifAlertHandlerIrqClassd, kDifToggleEnabled));
}
static void set_aon_timers(const dif_aon_timer_t *aon_timer) {
uint32_t bark_cycles =
aon_timer_testutils_get_aon_cycles_from_us(kWdogBarkMicros);
uint32_t bite_cycles =
aon_timer_testutils_get_aon_cycles_from_us(kWdogBiteMicros);
LOG_INFO(
"Wdog will bark after %u us (%u cycles) and bite after %u us (%u cycles)",
(uint32_t)kWdogBarkMicros, bark_cycles, (uint32_t)kWdogBiteMicros,
bite_cycles);
// Setup the wdog bark and bite timeouts.
aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles,
/*pause_in_sleep=*/false);
}
/**
* Execute the aon timer interrupt test.
*/
static void execute_test(const dif_aon_timer_t *aon_timer) {
alert_handler_config();
// Select the fault_checker function, depending on kExpectedAlertNumber.
switch (kExpectedAlertNumber) {
case kTopEarlgreyAlertIdAdcCtrlAonFatalFault: {
fault_checker_t fc = {trivial_fault_checker, adc_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdAesFatalFault: {
fault_checker_t fc = {aes_fault_checker, aes_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdAonTimerAonFatalFault: {
fault_checker_t fc = {trivial_fault_checker, aon_timer_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdClkmgrAonFatalFault: {
fault_checker_t fc = {clkmgr_fault_checker, clkmgr_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdCsrngFatalAlert: {
fault_checker_t fc = {trivial_fault_checker, csrng_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdEdn0FatalAlert: {
fault_checker_t fc = {trivial_fault_checker, edn0_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdEdn1FatalAlert: {
fault_checker_t fc = {trivial_fault_checker, edn1_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdEntropySrcFatalAlert: {
fault_checker_t fc = {trivial_fault_checker, entropy_src_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdFlashCtrlFatalErr: {
fault_checker_t fc = {flash_ctrl_fault_checker, flash_ctrl_inst_name,
flash_fatal_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdFlashCtrlFatalStdErr: {
fault_checker_t fc = {flash_ctrl_fault_checker, flash_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdGpioFatalFault: {
fault_checker_t fc = {trivial_fault_checker, gpio_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdHmacFatalFault: {
fault_checker_t fc = {trivial_fault_checker, hmac_inst_name, we_check};
// TODO(#14518)
LOG_INFO("Expected alert %d hmac fault check is yet unimplemented",
kExpectedAlertNumber);
/*
fault_checker = {hmac_fault_checker, hmac_inst, we_check};
*/
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdI2c0FatalFault: {
fault_checker_t fc = {trivial_fault_checker, i2c0_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdI2c1FatalFault: {
fault_checker_t fc = {trivial_fault_checker, i2c1_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdI2c2FatalFault: {
fault_checker_t fc = {trivial_fault_checker, i2c2_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdKeymgrFatalFaultErr: {
fault_checker_t fc = {trivial_fault_checker, keymgr_inst_name, we_check};
// TODO(#14518)
LOG_INFO("Expected alert %d keymgr fault check is yet unimplemented",
kExpectedAlertNumber);
/*
fault_checker = keymgr_fault_checker;
*/
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdKmacFatalFaultErr: {
fault_checker_t fc = {kmac_fault_checker, kmac_inst_name, we_check};
fault_checker = fc;
} break;
// TODO add mechanism to inject kTopEarlgreyAlertIdLcCtrlFatalProgError by
// forcing otp_prog_err_o from lc_ctrl_fsm
case kTopEarlgreyAlertIdLcCtrlFatalStateError: {
fault_checker_t fc = {lc_ctrl_fault_checker, lc_ctrl_inst_name,
sparse_fsm_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdLcCtrlFatalBusIntegError: {
fault_checker_t fc = {lc_ctrl_fault_checker, lc_ctrl_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdOtbnFatal: {
fault_checker_t fc = {trivial_fault_checker, otbn_inst_name, we_check};
// TODO(#14518)
LOG_INFO("Expected alert %d otbn fault check is yet unimplemented",
kExpectedAlertNumber);
/*
fault_checker = otbn_fault_checker;
*/
fault_checker = fc;
} break;
// TODO add mechanism to inject:
// kTopEarlgreyAlertIdOtpCtrlFatalMacroError uncorrectable ecc from macro
// In any partition otp_ctrl_part_unbuf error_q = MacroEccUncorrError
// kTopEarlgreyAlertIdOtpCtrlFatalPrimOtpAlert force at prim_otp interface
// u_otp output fatal_alert_o.
// forcing otp_prog_err_o from lc_ctrl_fsm and
// kTopEarlgreyAlertIdLcCtrlFatalStateError using sparse fsm.
// alerts, and corresponding CSR bit to check.
case kTopEarlgreyAlertIdOtpCtrlFatalCheckError: {
fault_checker_t fc = {otp_ctrl_fault_checker, otp_ctrl_inst_name,
sparse_fsm_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdOtpCtrlFatalBusIntegError: {
fault_checker_t fc = {otp_ctrl_fault_checker, otp_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdPattgenFatalFault: {
fault_checker_t fc = {trivial_fault_checker, pattgen_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdPinmuxAonFatalFault: {
fault_checker_t fc = {trivial_fault_checker, pinmux_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdPwmAonFatalFault: {
fault_checker_t fc = {trivial_fault_checker, pwm_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdPwrmgrAonFatalFault: {
fault_checker_t fc = {pwrmgr_fault_checker, pwrmgr_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRomCtrlFatal: {
fault_checker_t fc = {rom_ctrl_fault_checker, rom_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRstmgrAonFatalCnstyFault: {
fault_checker_t fc = {rstmgr_fault_checker, rstmgr_inst_name,
rst_cnsty_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRstmgrAonFatalFault: {
fault_checker_t fc = {rstmgr_fault_checker, rstmgr_inst_name, we_check};
fault_checker = fc;
} break;
// TODO kTopEarlgreyAlertIdRvCoreIbexFatalSwErr write to fatal_sw_err
case kTopEarlgreyAlertIdRvCoreIbexFatalHwErr: {
fault_checker_t fc = {rv_core_ibex_fault_checker, rv_core_ibex_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRvDmFatalFault: {
fault_checker_t fc = {trivial_fault_checker, rv_dm_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRvPlicFatalFault: {
fault_checker_t fc = {trivial_fault_checker, rv_plic_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdRvTimerFatalFault: {
fault_checker_t fc = {trivial_fault_checker, rv_timer_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSensorCtrlFatalAlert: {
fault_checker_t fc = {trivial_fault_checker, sensor_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSpiDeviceFatalFault: {
fault_checker_t fc = {trivial_fault_checker, spi_device_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSpiHost0FatalFault: {
fault_checker_t fc = {trivial_fault_checker, spi_host0_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSpiHost1FatalFault: {
fault_checker_t fc = {trivial_fault_checker, spi_host1_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSramCtrlMainFatalError: {
fault_checker_t fc = {sram_ctrl_main_fault_checker,
sram_ctrl_main_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSramCtrlRetAonFatalError: {
fault_checker_t fc = {sram_ctrl_ret_fault_checker,
sram_ctrl_ret_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdSysrstCtrlAonFatalFault: {
fault_checker_t fc = {trivial_fault_checker, sysrst_ctrl_inst_name,
we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdUart0FatalFault: {
fault_checker_t fc = {trivial_fault_checker, uart0_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdUart1FatalFault: {
fault_checker_t fc = {trivial_fault_checker, uart1_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdUart2FatalFault: {
fault_checker_t fc = {trivial_fault_checker, uart2_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdUart3FatalFault: {
fault_checker_t fc = {trivial_fault_checker, uart3_inst_name, we_check};
fault_checker = fc;
} break;
case kTopEarlgreyAlertIdUsbdevFatalFault: {
fault_checker_t fc = {trivial_fault_checker, usbdev_inst_name, we_check};
fault_checker = fc;
} break;
default: {
LOG_ERROR("Unexpected fault");
}
}
// Save the fault_checker to flash.
save_fault_checker(&fault_checker);
// Make sure we can receive both the watchdog and alert NMIs.
CHECK_DIF_OK(
dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceAlert));
CHECK_DIF_OK(
dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceWdog));
set_aon_timers(aon_timer);
LOG_INFO("Expected alert is %d for %s", kExpectedAlertNumber,
fault_checker.ip_inst);
// Trigger the SV side to inject fault.
// DO NOT CHANGE THIS: it is used to notify the SV side.
LOG_INFO("Ready for fault injection");
// FlashCtrlFatalErr test requires host read request.
if (kExpectedAlertNumber == kTopEarlgreyAlertIdFlashCtrlFatalErr) {
enum {
kNumTestWords = 16,
kNumTestBytes = kNumTestWords * sizeof(uint32_t),
};
uint32_t host_data[kNumTestWords];
// Send host request to trigger host grant from flash_ctrl.
mmio_region_memcpy_from_mmio32(
mmio_region_from_addr(TOP_EARLGREY_EFLASH_BASE_ADDR),
FLASH_CTRL_PARAM_BYTES_PER_BANK, &host_data, kNumTestBytes);
}
IBEX_SPIN_FOR(alert_irq_seen, kTestTimeout);
LOG_INFO("Alert IRQ seen");
if (kExpectedAlertNumber == kTopEarlgreyAlertIdSramCtrlRetAonFatalError) {
LOG_INFO("Check that the retention SRAM blocks accesses");
uint32_t data = *((uint32_t *)kSramRetStart);
LOG_INFO("Read from address 0x%0x with expected error gets 0x%x",
kSramRetStart, data);
CHECK(load_access_exception_seen,
"We expect this access to trigger a load access exception");
}
wait_for_interrupt();
CHECK(false, "This should not be reached");
}
void check_alert_dump() {
dif_rstmgr_alert_info_dump_segment_t dump[DIF_RSTMGR_ALERT_INFO_MAX_SIZE];
size_t seg_size;
alert_info_t actual_info;
CHECK_DIF_OK(dif_rstmgr_alert_info_dump_read(
&rstmgr, dump, DIF_RSTMGR_ALERT_INFO_MAX_SIZE, &seg_size));
LOG_INFO("DUMP SIZE %d", seg_size);
for (int i = 0; i < seg_size; i++) {
LOG_INFO("DUMP:%d: 0x%x", i, dump[i]);
}
actual_info = alert_info_dump_to_struct(dump, seg_size);
LOG_INFO("The alert info crash dump:");
alert_info_to_string(&actual_info);
// Check alert cause.
for (int i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) {
if (i == kExpectedAlertNumber) {
CHECK(actual_info.alert_cause[i], "Expected alert cause %d to be set", i);
} else {
// It is possible some alerts can trigger others; for example, some
// lc_ctrl faults lead to otp_ctrl faults.
if (actual_info.alert_cause[i]) {
LOG_INFO("Unexpected alert cause %d, may be triggered by %d", i,
kExpectedAlertNumber);
}
}
}
}
bool test_main(void) {
// Enable global and external IRQ at Ibex.
irq_global_ctrl(true);
irq_external_ctrl(true);
init_peripherals();
// Enable all the interrupts used in this test.
rv_plic_testutils_irq_range_enable(
&plic, kPlicTarget, kTopEarlgreyPlicIrqIdAonTimerAonWkupTimerExpired,
kTopEarlgreyPlicIrqIdAonTimerAonWdogTimerBark);
rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
kTopEarlgreyPlicIrqIdAlertHandlerClassa,
kTopEarlgreyPlicIrqIdAlertHandlerClassd);
// Enable access to flash for storing info across resets.
LOG_INFO("Setting default region accesses");
flash_ctrl_testutils_default_region_access(&flash_ctrl_state,
/*rd_en*/ true,
/*prog_en*/ true,
/*erase_en*/ true,
/*scramble_en*/ false,
/*ecc_en*/ false,
/*he_en*/ false);
// Get the flash maintained reset counter.
reset_count = flash_ctrl_testutils_counter_get(kCounterReset);
LOG_INFO("Reset counter value: %u", reset_count);
if (reset_count > kMaxResets) {
restore_fault_checker(&fault_checker);
CHECK(false, "Ip %d Got too many resets (%d)", fault_checker.ip_inst,
reset_count);
}
// Increment reset counter to know where we are.
flash_ctrl_testutils_counter_set_at_least(&flash_ctrl_state, kCounterReset,
reset_count + 1);
// Check if there was a HW reset caused by the escalation.
dif_rstmgr_reset_info_bitfield_t rst_info;
rst_info = rstmgr_testutils_reason_get();
rstmgr_testutils_reason_clear();
CHECK(rst_info == kDifRstmgrResetInfoPor ||
rst_info == kDifRstmgrResetInfoEscalation,
"Wrong reset reason %02X", rst_info);
if (rst_info == kDifRstmgrResetInfoPor) {
LOG_INFO("Booting for the first time, starting test");
// Enable rstmgr alert info capture.
CHECK_DIF_OK(dif_rstmgr_alert_info_set_enabled(&rstmgr, kDifToggleEnabled));
execute_test(&aon_timer);
} else if (rst_info == kDifRstmgrResetInfoEscalation) {
restore_fault_checker(&fault_checker);
LOG_INFO("Booting for the second time due to escalation reset");
int interrupt_count = flash_ctrl_testutils_counter_get(kCounterInterrupt);
int nmi_count = flash_ctrl_testutils_counter_get(kCounterNmi);
LOG_INFO("Interrupt count %d", interrupt_count);
LOG_INFO("NMI count %d", nmi_count);
// ISRs should not run if flash_ctrl or sram_ctrl_main get a fault because
// flash or sram accesses are blocked in those cases. For lc_ctrl fatal
// state the lc_ctrl blocks the CPU.
if (kExpectedAlertNumber == kTopEarlgreyAlertIdFlashCtrlFatalStdErr ||
kExpectedAlertNumber == kTopEarlgreyAlertIdSramCtrlMainFatalError ||
kExpectedAlertNumber == kTopEarlgreyAlertIdLcCtrlFatalStateError ||
kExpectedAlertNumber == kTopEarlgreyAlertIdOtpCtrlFatalCheckError) {
CHECK(interrupt_count == 0,
"Expected regular ISR should not run for flash_ctrl, lc_ctrl fatal "
"state, or sram_ctrl_main faults");
CHECK(nmi_count == 0,
"Expected nmi should not run for flash_ctrl, lc_ctrl fatal state, "
"or sram_ctrl_main faults");
} else {
CHECK(interrupt_count == 1, "Expected exactly one regular interrupt");
CHECK(nmi_count > 0, "Expected at least one nmi");
}
// Check the alert handler cause is cleared.
bool is_cause = true;
CHECK_DIF_OK(dif_alert_handler_alert_is_cause(
&alert_handler, kExpectedAlertNumber, &is_cause));
CHECK(!is_cause);
// Check the fault register is clear.
fault_checker.function(/*enable=*/false, fault_checker.ip_inst,
fault_checker.type);
check_alert_dump();
return true;
} else {
LOG_ERROR("Unexpected rst_info=0x%x", rst_info);
return false;
}
return false;
}