[mask_rom] Alert Handler Driver
The alert handler driver allows assigning individual alerts to alert
classes and configuring the escalations for each alert class.
Signed-off-by: Chris Frantz <cfrantz@google.com>
diff --git a/sw/device/lib/base/macros.h b/sw/device/lib/base/macros.h
index 5abe723..8989077 100644
--- a/sw/device/lib/base/macros.h
+++ b/sw/device/lib/base/macros.h
@@ -15,6 +15,11 @@
*/
/**
+ * A annotation that a switch/case fallthrough is the intended behavior.
+ */
+#define FALLTHROUGH_INTENDED __attribute__((fallthrough))
+
+/**
* A variable-argument macro that expands to the number of arguments passed into
* it, between 0 and 31 arguments.
*
diff --git a/sw/device/silicon_creator/lib/drivers/alert.c b/sw/device/silicon_creator/lib/drivers/alert.c
new file mode 100644
index 0000000..0ff4881
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/alert.c
@@ -0,0 +1,216 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/lib/drivers/alert.h"
+
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "alert_handler_regs.h"
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+enum {
+ kBase = TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR,
+};
+
+rom_error_t alert_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled) {
+ if (index >= ALERT_HANDLER_ALERT_CLASS_MULTIREG_COUNT) {
+ return kErrorAlertBadIndex;
+ }
+ index *= 4;
+
+ switch (cls) {
+ case kAlertClassA:
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_ALERT_CLASS_0_CLASS_A_0_VALUE_CLASSA);
+ break;
+ case kAlertClassB:
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_ALERT_CLASS_0_CLASS_A_0_VALUE_CLASSB);
+ break;
+ case kAlertClassC:
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_ALERT_CLASS_0_CLASS_A_0_VALUE_CLASSC);
+ break;
+ case kAlertClassD:
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_ALERT_CLASS_0_CLASS_A_0_VALUE_CLASSD);
+ break;
+ case kAlertClassX:
+ return kErrorOk;
+ default:
+ return kErrorAlertBadClass;
+ }
+
+ switch (enabled) {
+ case kAlertEnableNone:
+ break;
+ case kAlertEnableLocked:
+ // Enable, then lock.
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_EN_0_REG_OFFSET + index, 1);
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET + index,
+ 0);
+ break;
+ case kAlertEnableEnabled:
+ abs_mmio_write32(kBase + ALERT_HANDLER_ALERT_EN_0_REG_OFFSET + index, 1);
+ break;
+ default:
+ return kErrorAlertBadEnable;
+ }
+
+ return kErrorOk;
+}
+
+rom_error_t alert_local_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled) {
+ if (index >= ALERT_HANDLER_LOC_ALERT_CLASS_MULTIREG_COUNT) {
+ return kErrorAlertBadIndex;
+ }
+ index *= 4;
+
+ switch (cls) {
+ case kAlertClassA:
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_LOC_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_LOC_ALERT_CLASS_0_CLASS_LA_0_VALUE_CLASSA);
+ break;
+ case kAlertClassB:
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_LOC_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_LOC_ALERT_CLASS_0_CLASS_LA_0_VALUE_CLASSB);
+ break;
+ case kAlertClassC:
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_LOC_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_LOC_ALERT_CLASS_0_CLASS_LA_0_VALUE_CLASSC);
+ break;
+ case kAlertClassD:
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_LOC_ALERT_CLASS_0_REG_OFFSET + index,
+ ALERT_HANDLER_LOC_ALERT_CLASS_0_CLASS_LA_0_VALUE_CLASSD);
+ break;
+ case kAlertClassX:
+ return kErrorOk;
+ default:
+ return kErrorAlertBadClass;
+ }
+
+ switch (enabled) {
+ case kAlertEnableNone:
+ break;
+ case kAlertEnableLocked:
+ // Enable, then lock.
+ abs_mmio_write32(kBase + ALERT_HANDLER_LOC_ALERT_EN_0_REG_OFFSET + index,
+ 1);
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_LOC_ALERT_REGWEN_0_REG_OFFSET + index, 0);
+ break;
+ case kAlertEnableEnabled:
+ abs_mmio_write32(kBase + ALERT_HANDLER_LOC_ALERT_EN_0_REG_OFFSET + index,
+ 1);
+ break;
+ default:
+ return kErrorAlertBadEnable;
+ }
+
+ return kErrorOk;
+}
+
+rom_error_t alert_class_configure(alert_class_t cls,
+ const alert_class_config_t *config) {
+ uint32_t offset = 0;
+ uint32_t reg = 0;
+
+ // Each escalation signal should be asserted in its corresponding phase.
+ reg = bitfield_field32_write(reg, ALERT_HANDLER_CLASSA_CTRL_MAP_E0_FIELD, 0);
+ reg = bitfield_field32_write(reg, ALERT_HANDLER_CLASSA_CTRL_MAP_E1_FIELD, 1);
+ reg = bitfield_field32_write(reg, ALERT_HANDLER_CLASSA_CTRL_MAP_E2_FIELD, 2);
+ reg = bitfield_field32_write(reg, ALERT_HANDLER_CLASSA_CTRL_MAP_E3_FIELD, 3);
+
+ // All of the alert class register blocks are identical but at different
+ // offsets. We'll treat everything like Class A, but add in the offset
+ // to the other classes.
+ switch (cls) {
+ case kAlertClassA:
+ offset = ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET -
+ ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET;
+ break;
+ case kAlertClassB:
+ offset = ALERT_HANDLER_CLASSB_CTRL_REG_OFFSET -
+ ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET;
+ break;
+ case kAlertClassC:
+ offset = ALERT_HANDLER_CLASSC_CTRL_REG_OFFSET -
+ ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET;
+ break;
+ case kAlertClassD:
+ offset = ALERT_HANDLER_CLASSD_CTRL_REG_OFFSET -
+ ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET;
+ break;
+ case kAlertClassX:
+ default:
+ return kErrorAlertBadClass;
+ }
+ switch (config->enabled) {
+ case kAlertEnableLocked:
+ reg = bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_LOCK_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEnableEnabled:
+ reg = bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_EN_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEnableNone:
+ break;
+ default:
+ return kErrorAlertBadEnable;
+ }
+ switch (config->escalation) {
+ case kAlertEscalatePhase3:
+ reg =
+ bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_EN_E3_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEscalatePhase2:
+ reg =
+ bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_EN_E2_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEscalatePhase1:
+ reg =
+ bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_EN_E1_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEscalatePhase0:
+ reg =
+ bitfield_bit32_write(reg, ALERT_HANDLER_CLASSA_CTRL_EN_E0_BIT, true);
+ FALLTHROUGH_INTENDED;
+ case kAlertEscalateNone:
+ break;
+ default:
+ return kErrorAlertBadEscalation;
+ }
+
+ abs_mmio_write32(kBase + ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET + offset, reg);
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_CLASSA_ACCUM_THRESH_REG_OFFSET + offset,
+ config->accum_threshold);
+ abs_mmio_write32(kBase + ALERT_HANDLER_CLASSA_TIMEOUT_CYC_REG_OFFSET + offset,
+ config->timeout_cycles);
+ for (size_t i = 0; i < 4; ++i) {
+ abs_mmio_write32(
+ kBase + ALERT_HANDLER_CLASSA_PHASE0_CYC_REG_OFFSET + offset + i * 4,
+ config->phase_cycles[i]);
+ }
+ if (config->enabled == kAlertEnableLocked) {
+ // Lock the alert configuration if it is configured to be locked.
+ abs_mmio_write32(kBase + ALERT_HANDLER_CLASSA_REGWEN_REG_OFFSET + offset,
+ 0);
+ }
+ return kErrorOk;
+}
+
+rom_error_t alert_ping_enable(void) {
+ // Enable the ping timer, then lock it.
+ abs_mmio_write32(kBase + ALERT_HANDLER_PING_TIMER_EN_REG_OFFSET, 1);
+ abs_mmio_write32(kBase + ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
+ return kErrorOk;
+}
diff --git a/sw/device/silicon_creator/lib/drivers/alert.h b/sw/device/silicon_creator/lib/drivers/alert.h
new file mode 100644
index 0000000..a6d43ea
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/alert.h
@@ -0,0 +1,123 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_ALERT_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_ALERT_H_
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sw/device/silicon_creator/lib/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALERT_CLASSES 4
+
+// Note: the AlertClass values need to map to a byte.
+/**
+ * Alert Classification Values as stored in OTP.
+ */
+typedef enum AlertClass {
+ /***
+ * Alert class X is a special class which means "not configured"
+ */
+ kAlertClassX,
+ kAlertClassA,
+ kAlertClassB,
+ kAlertClassC,
+ kAlertClassD,
+} alert_class_t;
+
+// Note: the AlertEnable values need to map to a byte.
+/**
+ * Alert Enable Values as stored in OTP.
+ */
+typedef enum AlertEnable {
+ kAlertEnableNone,
+ kAlertEnableEnabled,
+ kAlertEnableLocked,
+} alert_enable_t;
+
+/**
+ * Alert Escalation Policy as stored in OTP. Note that each phase implies the
+ * prior phases are also enabled.
+ */
+typedef enum AlertEscalate {
+ kAlertEscalateNone,
+ kAlertEscalatePhase0,
+ kAlertEscalatePhase1,
+ kAlertEscalatePhase2,
+ kAlertEscalatePhase3,
+} alert_escalate_t;
+
+/**
+ * Alert class configuration struct.
+ */
+typedef struct AlertClassConfig {
+ /**
+ * Whether or not this alert class enabled.
+ */
+ alert_enable_t enabled;
+ /**
+ * The escalation configuration for this alert class.
+ */
+ alert_escalate_t escalation;
+ /**
+ * The accumlation threshold for this alert class.
+ */
+ uint32_t accum_threshold;
+ /**
+ * The timeout cycles for this alert class.
+ */
+ uint32_t timeout_cycles;
+ /**
+ * The phase cycles for this alert class.
+ */
+ uint32_t phase_cycles[4];
+} alert_class_config_t;
+
+/**
+ * Configure a single alert.
+ *
+ * Configures and optionally lock an alert's class configuration.
+ *
+ * @param index: The alert number.
+ * @param cls: Class of the alert.
+ * @param enabled: Whether or not to enable and/or lock the alert.
+ */
+rom_error_t alert_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled);
+
+/**
+ * Configure a single local alert.
+ *
+ * Configures and optionally lock a local alert's class configuration.
+ *
+ * @param index: The local alert number.
+ * @param cls: Class of the alert.
+ * @param enabled: Whether or not to enable and/or lock the alert.
+ */
+rom_error_t alert_local_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled);
+/**
+ * Configure an alert class.
+ *
+ * Configures an alert class to the specified config.
+ *
+ * @param cls: Class of the alert (alert class X is not permitted here).
+ * @param config: The alert class' configuration.
+ */
+rom_error_t alert_class_configure(alert_class_t cls,
+ const alert_class_config_t *config);
+
+/**
+ * Enable the ping timer mechanism.
+ */
+rom_error_t alert_ping_enable(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_ALERT_H_
diff --git a/sw/device/silicon_creator/lib/drivers/alert_functest.c b/sw/device/silicon_creator/lib/drivers/alert_functest.c
new file mode 100644
index 0000000..337a418
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/alert_functest.c
@@ -0,0 +1,103 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "sw/device/lib/arch/device.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/runtime/hart.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
+#include "sw/device/silicon_creator/lib/drivers/alert.h"
+#include "sw/device/silicon_creator/lib/drivers/rstmgr.h"
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/lib/test_main.h"
+
+#include "alert_handler_regs.h"
+#include "flash_ctrl_regs.h"
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "otp_ctrl_regs.h"
+#include "rstmgr_regs.h"
+
+// The reset reason value is really a bitfield. The power-on-reset indicator
+// is defined by rstmgr_regs.h.
+#define RESET_REASON_POR (1 << RSTMGR_RESET_INFO_POR_BIT)
+// FIXME: I don't know where the HW_REQ field of the reset reason register
+// is defined. I observe a value of 4 for alerts.
+#define RESET_REASON_ALERT \
+ ((4 << RSTMGR_RESET_INFO_HW_REQ_OFFSET) | RESET_REASON_POR)
+
+enum {
+ kAlertBase = TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR,
+ kOtpBase = TOP_EARLGREY_OTP_CTRL_BASE_ADDR,
+ kFlashBase = TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR,
+};
+
+rom_error_t alert_no_escalate_test(void) {
+ // Configure class B alerts for phase 0 only and disable NMI signalling.
+ alert_class_config_t config = {
+ .enabled = kAlertEnableEnabled,
+ .escalation = kAlertEscalatePhase0,
+ .accum_threshold = 0,
+ .timeout_cycles = 0,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+ LOG_INFO("Configure OtpCtrlFatalMacroError as class B");
+ RETURN_IF_ERROR(alert_configure(kTopEarlgreyAlertIdOtpCtrlFatalMacroError,
+ kAlertClassB, kAlertEnableLocked));
+ LOG_INFO("Configure class B alerts");
+ RETURN_IF_ERROR(alert_class_configure(kAlertClassB, &config));
+ LOG_INFO("Generate alert via test regs");
+ abs_mmio_write32(kOtpBase + OTP_CTRL_ALERT_TEST_REG_OFFSET, 1);
+ uint32_t count =
+ abs_mmio_read32(kAlertBase + ALERT_HANDLER_CLASSB_ACCUM_CNT_REG_OFFSET);
+ return count == 1 ? kErrorOk : kErrorUnknown;
+}
+
+rom_error_t alert_escalate_test(void) {
+ // Configure class A alerts for full escalation.
+ alert_class_config_t config = {
+ .enabled = kAlertEnableEnabled,
+ .escalation = kAlertEscalatePhase3,
+ .accum_threshold = 0,
+ .timeout_cycles = 0,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+
+ LOG_INFO("Configure FlashCtrlFatalIntgErr as class A");
+ RETURN_IF_ERROR(alert_configure(kTopEarlgreyAlertIdFlashCtrlFatalIntgErr,
+ kAlertClassA, kAlertEnableEnabled));
+ LOG_INFO("Configure class A alerts");
+ RETURN_IF_ERROR(alert_class_configure(kAlertClassA, &config));
+ LOG_INFO("Generate alert via test regs");
+ abs_mmio_write32(kFlashBase + FLASH_CTRL_ALERT_TEST_REG_OFFSET, 8);
+ return kErrorUnknown;
+}
+
+const test_config_t kTestConfig;
+
+bool test_main(void) {
+ rom_error_t result = kErrorOk;
+ uint32_t reason = rstmgr_reason_get();
+ rstmgr_alert_info_enable();
+
+ LOG_INFO("reset_info = %08x", reason);
+ if (reason == RESET_REASON_POR) {
+ EXECUTE_TEST(result, alert_no_escalate_test);
+ EXECUTE_TEST(result, alert_escalate_test);
+ // We should never get here - the escalate test should cause a reset
+ // and we should see a reset reason of 0x21.
+ LOG_ERROR("Test failure: should have reset before this line.");
+ result = kErrorUnknown;
+ } else if (reason == RESET_REASON_ALERT) {
+ LOG_INFO("Detected reset after escalation test");
+ } else {
+ LOG_ERROR("Unknown reset reason");
+ result = kErrorUnknown;
+ }
+ return result == kErrorOk;
+}
diff --git a/sw/device/silicon_creator/lib/drivers/alert_unittest.cc b/sw/device/silicon_creator/lib/drivers/alert_unittest.cc
new file mode 100644
index 0000000..97bcd0b
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/alert_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/lib/drivers/alert.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/silicon_creator/lib/base/mock_abs_mmio.h"
+
+#include "alert_handler_regs.h" // Generated.
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+namespace alert_unittest {
+namespace {
+using testing::Each;
+using testing::Eq;
+using testing::Test;
+
+class AlertTest : public mask_rom_test::MaskRomTest {
+ protected:
+ uint32_t base_ = TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR;
+ mask_rom_test::MockAbsMmio mmio_;
+};
+
+class InitTest : public AlertTest {};
+
+TEST_F(InitTest, AlertConfigureAlertBadIndex) {
+ EXPECT_EQ(alert_configure(ALERT_HANDLER_ALERT_CLASS_MULTIREG_COUNT,
+ kAlertClassA, kAlertEnableNone),
+ kErrorAlertBadIndex);
+}
+
+TEST_F(InitTest, AlertConfigureAlertBadClass) {
+ EXPECT_EQ(alert_configure(0, (alert_class_t)-1, kAlertEnableNone),
+ kErrorAlertBadClass);
+}
+
+TEST_F(InitTest, AlertConfigureAlertBadEnable) {
+ // We expect the alert to get configured as class A, but then to
+ // experience an error when evaluating the enable parameter.
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET, 0);
+ EXPECT_EQ(alert_configure(0, kAlertClassA, (alert_enable_t)-1),
+ kErrorAlertBadEnable);
+}
+
+TEST_F(InitTest, AlertConfigureAlertClassXNoOperation) {
+ EXPECT_EQ(alert_configure(0, kAlertClassX, kAlertEnableNone), kErrorOk);
+}
+
+TEST_F(InitTest, LocalAlertConfigureAlertClassXNoOperation) {
+ EXPECT_EQ(alert_local_configure(0, kAlertClassX, kAlertEnableNone), kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigure0AsClassA) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_CLASS_0_REG_OFFSET, 0);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_EN_0_REG_OFFSET, 1);
+ EXPECT_EQ(alert_configure(0, kAlertClassA, kAlertEnableEnabled), kErrorOk);
+}
+
+TEST_F(InitTest, LocalAlertConfigure0AsClassA) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_CLASS_0_REG_OFFSET,
+ 0);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_EN_0_REG_OFFSET, 1);
+ EXPECT_EQ(alert_local_configure(0, kAlertClassA, kAlertEnableEnabled),
+ kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigure1AsClassB) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_CLASS_1_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_EN_1_REG_OFFSET, 1);
+ EXPECT_EQ(alert_configure(1, kAlertClassB, kAlertEnableEnabled), kErrorOk);
+}
+
+TEST_F(InitTest, LocalAlertConfigure1AsClassB) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_CLASS_1_REG_OFFSET,
+ 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_EN_1_REG_OFFSET, 1);
+ EXPECT_EQ(alert_local_configure(1, kAlertClassB, kAlertEnableEnabled),
+ kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigure2AsClassC) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_CLASS_2_REG_OFFSET, 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_EN_2_REG_OFFSET, 1);
+ EXPECT_EQ(alert_configure(2, kAlertClassC, kAlertEnableEnabled), kErrorOk);
+}
+
+TEST_F(InitTest, LocalAlertConfigure2AsClassC) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_CLASS_2_REG_OFFSET,
+ 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_EN_2_REG_OFFSET, 1);
+ EXPECT_EQ(alert_local_configure(2, kAlertClassC, kAlertEnableEnabled),
+ kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigure3AsClassDLocked) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_CLASS_3_REG_OFFSET, 3);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_EN_3_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_ALERT_REGWEN_3_REG_OFFSET, 0);
+ EXPECT_EQ(alert_configure(3, kAlertClassD, kAlertEnableLocked), kErrorOk);
+}
+
+TEST_F(InitTest, LocalAlertConfigure3AsClassDLocked) {
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_CLASS_3_REG_OFFSET,
+ 3);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_EN_3_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_LOC_ALERT_REGWEN_3_REG_OFFSET,
+ 0);
+ EXPECT_EQ(alert_local_configure(3, kAlertClassD, kAlertEnableLocked),
+ kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigureClassXBadClass) {
+ alert_class_config_t config{};
+ EXPECT_EQ(alert_class_configure(kAlertClassX, &config), kErrorAlertBadClass);
+}
+
+TEST_F(InitTest, AlertConfigureClassA) {
+ alert_class_config_t config = {
+ .enabled = kAlertEnableLocked,
+ .escalation = kAlertEscalatePhase3,
+ .accum_threshold = 1,
+ .timeout_cycles = 2,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_CTRL_REG_OFFSET,
+ {
+ {ALERT_HANDLER_CLASSA_CTRL_EN_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_LOCK_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E3_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E2_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E1_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E0_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E0_OFFSET, 0},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E1_OFFSET, 1},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E2_OFFSET, 2},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E3_OFFSET, 3},
+ });
+ EXPECT_ABS_WRITE32(mmio_,
+ base_ + ALERT_HANDLER_CLASSA_ACCUM_THRESH_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_TIMEOUT_CYC_REG_OFFSET,
+ 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_PHASE0_CYC_REG_OFFSET,
+ 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_PHASE1_CYC_REG_OFFSET,
+ 10);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_PHASE2_CYC_REG_OFFSET,
+ 100);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_PHASE3_CYC_REG_OFFSET,
+ 1000);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSA_REGWEN_REG_OFFSET, 0);
+ EXPECT_EQ(alert_class_configure(kAlertClassA, &config), kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigureClassB) {
+ alert_class_config_t config = {
+ .enabled = kAlertEnableEnabled,
+ .escalation = kAlertEscalatePhase2,
+ .accum_threshold = 1,
+ .timeout_cycles = 2,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_CTRL_REG_OFFSET,
+ {
+ {ALERT_HANDLER_CLASSA_CTRL_EN_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_LOCK_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E3_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E2_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E1_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E0_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E0_OFFSET, 0},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E1_OFFSET, 1},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E2_OFFSET, 2},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E3_OFFSET, 3},
+ });
+ EXPECT_ABS_WRITE32(mmio_,
+ base_ + ALERT_HANDLER_CLASSB_ACCUM_THRESH_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_TIMEOUT_CYC_REG_OFFSET,
+ 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_PHASE0_CYC_REG_OFFSET,
+ 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_PHASE1_CYC_REG_OFFSET,
+ 10);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_PHASE2_CYC_REG_OFFSET,
+ 100);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSB_PHASE3_CYC_REG_OFFSET,
+ 1000);
+ EXPECT_EQ(alert_class_configure(kAlertClassB, &config), kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigureClassC) {
+ alert_class_config_t config = {
+ .enabled = kAlertEnableEnabled,
+ .escalation = kAlertEscalatePhase1,
+ .accum_threshold = 1,
+ .timeout_cycles = 2,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_CTRL_REG_OFFSET,
+ {
+ {ALERT_HANDLER_CLASSA_CTRL_EN_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_LOCK_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E3_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E2_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E1_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E0_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E0_OFFSET, 0},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E1_OFFSET, 1},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E2_OFFSET, 2},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E3_OFFSET, 3},
+ });
+ EXPECT_ABS_WRITE32(mmio_,
+ base_ + ALERT_HANDLER_CLASSC_ACCUM_THRESH_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_TIMEOUT_CYC_REG_OFFSET,
+ 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_PHASE0_CYC_REG_OFFSET,
+ 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_PHASE1_CYC_REG_OFFSET,
+ 10);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_PHASE2_CYC_REG_OFFSET,
+ 100);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSC_PHASE3_CYC_REG_OFFSET,
+ 1000);
+ EXPECT_EQ(alert_class_configure(kAlertClassC, &config), kErrorOk);
+}
+
+TEST_F(InitTest, AlertConfigureClassD) {
+ alert_class_config_t config = {
+ .enabled = kAlertEnableEnabled,
+ .escalation = kAlertEscalateNone,
+ .accum_threshold = 1,
+ .timeout_cycles = 2,
+ .phase_cycles = {1, 10, 100, 1000},
+ };
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_CTRL_REG_OFFSET,
+ {
+ {ALERT_HANDLER_CLASSA_CTRL_EN_BIT, true},
+ {ALERT_HANDLER_CLASSA_CTRL_LOCK_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E3_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E2_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E1_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_EN_E0_BIT, false},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E0_OFFSET, 0},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E1_OFFSET, 1},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E2_OFFSET, 2},
+ {ALERT_HANDLER_CLASSA_CTRL_MAP_E3_OFFSET, 3},
+ });
+ EXPECT_ABS_WRITE32(mmio_,
+ base_ + ALERT_HANDLER_CLASSD_ACCUM_THRESH_REG_OFFSET, 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_TIMEOUT_CYC_REG_OFFSET,
+ 2);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_PHASE0_CYC_REG_OFFSET,
+ 1);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_PHASE1_CYC_REG_OFFSET,
+ 10);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_PHASE2_CYC_REG_OFFSET,
+ 100);
+ EXPECT_ABS_WRITE32(mmio_, base_ + ALERT_HANDLER_CLASSD_PHASE3_CYC_REG_OFFSET,
+ 1000);
+ EXPECT_EQ(alert_class_configure(kAlertClassD, &config), kErrorOk);
+}
+
+} // namespace
+} // namespace alert_unittest
diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build
index 318e65a..86ee4cd 100644
--- a/sw/device/silicon_creator/lib/drivers/meson.build
+++ b/sw/device/silicon_creator/lib/drivers/meson.build
@@ -192,3 +192,57 @@
),
suite: 'mask_rom',
)
+
+# Mask ROM alert handler driver
+sw_silicon_creator_lib_driver_alert = declare_dependency(
+ link_with: static_library(
+ 'sw_silicon_creator_lib_driver_alert',
+ sources: [
+ hw_ip_alert_handler_reg_h,
+ 'alert.c',
+ ],
+ dependencies: [
+ sw_silicon_creator_lib_base_abs_mmio,
+ ],
+ ),
+)
+
+test('sw_silicon_creator_lib_driver_alert_unittest', executable(
+ 'sw_silicon_creator_lib_driver_alert_unittest',
+ sources: [
+ 'alert_unittest.cc',
+ hw_ip_alert_handler_reg_h,
+ 'alert.c',
+ ],
+ dependencies: [
+ sw_vendor_gtest,
+ sw_silicon_creator_lib_base_mock_abs_mmio,
+ ],
+ native: true,
+ c_args: ['-DMOCK_ABS_MMIO'],
+ cpp_args: ['-DMOCK_ABS_MMIO'],
+ ),
+ suite: 'mask_rom',
+)
+
+sw_silicon_creator_lib_driver_alert_functest = declare_dependency(
+ link_with: static_library(
+ 'sw_silicon_creator_lib_driver_alert_functest',
+ sources: [
+ hw_ip_rstmgr_reg_h,
+ hw_ip_otp_ctrl_reg_h,
+ hw_ip_alert_handler_reg_h,
+ hw_ip_flash_ctrl_reg_h,
+ 'alert_functest.c'
+ ],
+ dependencies: [
+ sw_silicon_creator_lib_driver_alert,
+ sw_silicon_creator_lib_driver_rstmgr,
+ ],
+ ),
+)
+mask_rom_tests += {
+ 'sw_silicon_creator_lib_driver_alert_functest': {
+ 'library': sw_silicon_creator_lib_driver_alert_functest,
+ }
+}
diff --git a/sw/device/silicon_creator/lib/drivers/mock_alert.h b/sw/device/silicon_creator/lib/drivers/mock_alert.h
new file mode 100644
index 0000000..897346a
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/mock_alert.h
@@ -0,0 +1,57 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_MOCK_ALERT_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_MOCK_ALERT_H_
+
+#include "sw/device/lib/testing/mask_rom_test.h"
+#include "sw/device/silicon_creator/lib/drivers/alert.h"
+
+namespace mask_rom_test {
+namespace internal {
+
+/**
+ * Mock class for alert.c.
+ */
+class MockAlert {
+ public:
+ MOCK_METHOD(rom_error_t, alert_configure,
+ (size_t, alert_class_t, alert_enable_t));
+ MOCK_METHOD(rom_error_t, alert_local_configure,
+ (size_t, alert_class_t, alert_enable_t));
+ MOCK_METHOD(rom_error_t, alert_class_configure,
+ (alert_class_t, const alert_class_config_t *));
+ MOCK_METHOD(rom_error_t, alert_ping_enable, ());
+ virtual ~MockAlert() {}
+};
+
+} // namespace internal
+
+using MockAlert = GlobalMock<testing::StrictMock<internal::MockAlert>>;
+
+extern "C" {
+
+rom_error_t alert_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled) {
+ return MockAlert::Instance().alert_configure(index, cls, enabled);
+}
+
+rom_error_t alert_local_configure(size_t index, alert_class_t cls,
+ alert_enable_t enabled) {
+ return MockAlert::Instance().alert_local_configure(index, cls, enabled);
+}
+
+rom_error_t alert_class_configure(alert_class_t cls,
+ const alert_class_config_t *config) {
+ return MockAlert::Instance().alert_class_configure(cls, config);
+}
+
+rom_error_t alert_ping_enable(void) {
+ return MockAlert::Instance().alert_ping_enable();
+}
+
+} // extern "C"
+} // namespace mask_rom_test
+
+#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_MOCK_ALERT_H_
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h
index 1db127a..222a6d6 100644
--- a/sw/device/silicon_creator/lib/error.h
+++ b/sw/device/silicon_creator/lib/error.h
@@ -21,12 +21,13 @@
*/
enum module_ {
kModuleUnknown = 0,
- kModuleUart = 0x4155, // ASCII "UA".
- kModuleHmac = 0x4d48, // ASCII "HM".
- kModuleSigverify = 0x5653, // ASCII "SV".
- kModuleKeymgr = 0x4d4b, // ASCII "KM".
- kModuleManifest = 0x414d, // ASCII "MA".
- kModuleRomextimage = 0x4552, // ASCII "RE".
+ kModuleAlertHandler = 0x4148, // ASCII "AH".
+ kModuleUart = 0x4155, // ASCII "UA".
+ kModuleHmac = 0x4d48, // ASCII "HM".
+ kModuleSigverify = 0x5653, // ASCII "SV".
+ kModuleKeymgr = 0x4d4b, // ASCII "KM".
+ kModuleManifest = 0x414d, // ASCII "MA".
+ kModuleRomextimage = 0x4552, // ASCII "RE".
};
/**
@@ -54,6 +55,10 @@
X(kErrorManifestInternal, ERROR_(1, kModuleManifest, kInternal)), \
X(kErrorRomextimageInvalidArgument, ERROR_(1, kModuleRomextimage, kInvalidArgument)), \
X(kErrorRomextimageInternal, ERROR_(2, kModuleRomextimage, kInternal)), \
+ X(kErrorAlertBadIndex, ERROR_(1, kModuleAlertHandler, kInvalidArgument)), \
+ X(kErrorAlertBadClass, ERROR_(2, kModuleAlertHandler, kInvalidArgument)), \
+ X(kErrorAlertBadEnable, ERROR_(3, kModuleAlertHandler, kInvalidArgument)), \
+ X(kErrorAlertBadEscalation, ERROR_(4, kModuleAlertHandler, kInvalidArgument)), \
X(kErrorUnknown, 0xFFFFFFFF)
// clang-format on
@@ -82,5 +87,4 @@
#ifdef __cplusplus
}
#endif
-
#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_ERROR_H_