[mask_rom] Implement shutdown
1. Implement shutdown initialization and best-effort SW shutdown.
2. Restructure the mask_rom's main in terms of shutdown.
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 8989077..87837d4 100644
--- a/sw/device/lib/base/macros.h
+++ b/sw/device/lib/base/macros.h
@@ -15,11 +15,16 @@
*/
/**
- * A annotation that a switch/case fallthrough is the intended behavior.
+ * An annotation that a switch/case fallthrough is the intended behavior.
*/
#define FALLTHROUGH_INTENDED __attribute__((fallthrough))
/**
+ * A directive to force the compiler to inline a function.
+ */
+#define ALWAYS_INLINE __attribute__((always_inline)) inline
+
+/**
* A variable-argument macro that expands to the number of arguments passed into
* it, between 0 and 31 arguments.
*
@@ -65,4 +70,11 @@
#define OT_ASSERT_SIZE(type, size) \
static_assert(sizeof(type) == UINT32_C(size), "Unexpected size for " #type)
+/**
+ * A macro representing the OpenTitan execution platform.
+ */
+#if __riscv_xlen == 32
+#define OT_PLATFORM_RV32 1
+#endif
+
#endif // OPENTITAN_SW_DEVICE_LIB_BASE_MACROS_H_
diff --git a/sw/device/silicon_creator/lib/drivers/alert.h b/sw/device/silicon_creator/lib/drivers/alert.h
index a6d43ea..98e10e0 100644
--- a/sw/device/silicon_creator/lib/drivers/alert.h
+++ b/sw/device/silicon_creator/lib/drivers/alert.h
@@ -15,6 +15,7 @@
#define ALERT_CLASSES 4
+// TODO(lowRISC/opentitan#7148): Choose values for configuration enums.
// Note: the AlertClass values need to map to a byte.
/**
* Alert Classification Values as stored in OTP.
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h
index 60f8c63..68f037a 100644
--- a/sw/device/silicon_creator/lib/error.h
+++ b/sw/device/silicon_creator/lib/error.h
@@ -5,6 +5,8 @@
#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_ERROR_H_
#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_ERROR_H_
+#include "sw/device/lib/base/bitfield.h"
+
#define USING_ABSL_STATUS
#include "sw/device/silicon_creator/lib/absl_status.h"
#undef USING_ABSL_STATUS
@@ -27,10 +29,20 @@
kModuleSigverify = 0x5653, // ASCII "SV".
kModuleKeymgr = 0x4d4b, // ASCII "KM".
kModuleManifest = 0x414d, // ASCII "MA".
+ kModuleMaskRom = 0x524d, // ASCII "MR".
kModuleRomextimage = 0x4552, // ASCII "RE".
+ kModuleInterrupt = 0x5249, // ASCII "IR".
};
/**
+ * Field definitions for the different fields of the error word.
+ */
+#define ROM_ERROR_FIELD_ERROR ((bitfield_field32_t){.mask = 0xFF, .index = 24})
+#define ROM_ERROR_FIELD_MODULE \
+ ((bitfield_field32_t){.mask = 0xFFFF, .index = 8})
+#define ROM_ERROR_FIELD_STATUS ((bitfield_field32_t){.mask = 0xFF, .index = 0})
+
+/**
* Helper macro for building up error codes.
* @param status_ An appropriate general status code from absl_staus.h.
* @param module_ The module identifier which produces this error.
@@ -62,6 +74,9 @@
X(kErrorAlertBadClass, ERROR_(2, kModuleAlertHandler, kInvalidArgument)), \
X(kErrorAlertBadEnable, ERROR_(3, kModuleAlertHandler, kInvalidArgument)), \
X(kErrorAlertBadEscalation, ERROR_(4, kModuleAlertHandler, kInvalidArgument)), \
+ X(kErrorMaskRomBootFailed, ERROR_(1, kModuleMaskRom, kFailedPrecondition)), \
+ /* The high-byte of kErrorInterrupt is modified with the interrupt cause */ \
+ X(kErrorInterrupt, ERROR_(0, kModuleInterrupt, kUnknown)), \
X(kErrorUnknown, 0xFFFFFFFF)
// clang-format on
diff --git a/sw/device/silicon_creator/lib/meson.build b/sw/device/silicon_creator/lib/meson.build
index ad69718..921ba79 100644
--- a/sw/device/silicon_creator/lib/meson.build
+++ b/sw/device/silicon_creator/lib/meson.build
@@ -59,6 +59,46 @@
),
)
+sw_silicon_creator_lib_shutdown = declare_dependency(
+ link_with: static_library(
+ 'sw_silicon_creator_lib_shutdown',
+ sources: [
+ hw_ip_alert_handler_reg_h,
+ hw_ip_otp_ctrl_reg_h,
+ hw_ip_keymgr_reg_h,
+ hw_ip_sram_ctrl_reg_h,
+ hw_ip_flash_ctrl_reg_h,
+ 'shutdown.c',
+ ],
+ dependencies: [
+ sw_silicon_creator_lib_base_abs_mmio,
+ sw_silicon_creator_lib_driver_alert,
+ ],
+ ),
+)
+
+test('sw_silicon_creator_lib_shutdown_unittest', executable(
+ 'sw_silicon_creator_lib_shutdown_unittest',
+ sources: [
+ hw_ip_alert_handler_reg_h,
+ hw_ip_otp_ctrl_reg_h,
+ hw_ip_keymgr_reg_h,
+ hw_ip_sram_ctrl_reg_h,
+ hw_ip_flash_ctrl_reg_h,
+ 'shutdown.c',
+ 'shutdown_unittest.cc',
+ ],
+ dependencies: [
+ sw_vendor_gtest,
+ sw_silicon_creator_lib_base_mock_abs_mmio,
+ ],
+ native: true,
+ c_args: ['-DMOCK_ABS_MMIO', '-DOT_OFF_TARGET_TEST'],
+ cpp_args: ['-DMOCK_ABS_MMIO', '-DOT_OFF_TARGET_TEST'],
+ ),
+ suite: 'mask_rom',
+)
+
test('sw_silicon_creator_lib_error_unittest', executable(
'sw_silicon_creator_lib_error_unittest',
sources: [
diff --git a/sw/device/silicon_creator/lib/shutdown.c b/sw/device/silicon_creator/lib/shutdown.c
new file mode 100644
index 0000000..c27df60
--- /dev/null
+++ b/sw/device/silicon_creator/lib/shutdown.c
@@ -0,0 +1,268 @@
+// 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/shutdown.h"
+
+#include <assert.h>
+
+#include "sw/device/lib/base/bitfield.h"
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/base/stdasm.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/lifecycle.h"
+
+#include "alert_handler_regs.h"
+#include "flash_ctrl_regs.h"
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "keymgr_regs.h"
+#include "otp_ctrl_regs.h"
+#include "sram_ctrl_regs.h"
+
+static_assert(ALERT_HANDLER_ALERT_CLASS_MULTIREG_COUNT <=
+ OTP_CTRL_PARAM_ROM_ALERT_CLASSIFICATION_SIZE / 4,
+ "More alerts than alert classification OTP words!");
+
+#define NO_MODIFIERS
+
+#ifdef OT_OFF_TARGET_TEST
+// If we're building as a unit test, rename the shutdown functions so they
+// can be mocked and/or tested individually.
+// The unmodified function name will be declared as `extern` so the test
+// program can supply its own implementation. The implementation present
+// in this file will be named `unmocked_${name}` so the test program can
+// invoke it for testing.
+#define SHUTDOWN_FUNC(modifiers_, name_) \
+ extern void name_; \
+ void unmocked_##name_
+#else
+#define SHUTDOWN_FUNC(modifiers_, name_) \
+ static ALWAYS_INLINE modifiers_ void name_
+#endif
+
+// TODO: use the real OTP driver after it's converted to abs_mmio.
+#ifdef OT_OFF_TARGET_TEST
+extern uint32_t otp_read32(uint32_t address);
+#else
+inline uint32_t otp_read32(uint32_t address) {
+ return abs_mmio_read32(TOP_EARLGREY_OTP_CTRL_BASE_ADDR +
+ OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + address);
+}
+#endif
+
+// Convert the alert class to an index.
+// This is required because I expect to change the constant definitions in
+// alert_class_t to have reasonable hamming distances.
+static size_t clsindex(alert_class_t cls) {
+ switch (cls) {
+ case kAlertClassA:
+ return 0;
+ case kAlertClassB:
+ return 1;
+ case kAlertClassC:
+ return 2;
+ case kAlertClassD:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+rom_error_t shutdown_init(lifecycle_state_t lc_state) {
+ // Are we in a lifecycle state which needs alert configuration?
+ uint32_t lc_shift;
+ switch (lc_state) {
+ case kLcStateProd:
+ lc_shift = 0;
+ break;
+ case kLcStateProdEnd:
+ lc_shift = 8;
+ break;
+ case kLcStateDev:
+ lc_shift = 16;
+ break;
+ case kLcStateRma:
+ lc_shift = 24;
+ break;
+ default:
+ // No alert init for any other lifecycle states.
+ return kErrorOk;
+ }
+
+ // Get the enable and escalation settings for all four alert classes.
+ // Each of these OTP words is composed of 4 byte enums with the enable and
+ // escalate configs per alert class (a/b/c/d).
+ uint32_t class_enable = otp_read32(OTP_CTRL_PARAM_ROM_ALERT_CLASS_EN_OFFSET);
+ uint32_t class_escalate =
+ otp_read32(OTP_CTRL_PARAM_ROM_ALERT_ESCALATION_OFFSET);
+ alert_enable_t enable[ALERT_CLASSES];
+ alert_escalate_t escalate[ALERT_CLASSES];
+ for (size_t i = 0; i < ALERT_CLASSES; ++i) {
+ enable[i] = (alert_enable_t)bitfield_field32_read(
+ class_enable, (bitfield_field32_t){.mask = 0xff, .index = i * 8});
+ escalate[i] = (alert_escalate_t)bitfield_field32_read(
+ class_escalate, (bitfield_field32_t){.mask = 0xff, .index = i * 8});
+ }
+
+ // For each alert, read its corresponding OTP word and extract the class
+ // configuration for the current lifecycle state.
+ rom_error_t error = kErrorOk;
+ for (size_t i = 0; i < ALERT_HANDLER_ALERT_CLASS_MULTIREG_COUNT; ++i) {
+ uint32_t value = otp_read32(OTP_CTRL_PARAM_ROM_ALERT_CLASSIFICATION_OFFSET +
+ i * sizeof(uint32_t));
+ alert_class_t cls = (alert_class_t)bitfield_field32_read(
+ value, (bitfield_field32_t){.mask = 0xff, .index = lc_shift});
+ rom_error_t e = alert_configure(i, cls, enable[clsindex(cls)]);
+ if (e != kErrorOk) {
+ // Keep going if there is an error programming one alert. We want to
+ // program them all.
+ error = e;
+ }
+ }
+
+#if 0
+ // TODO(lowRISC/opentitan#7148): Bring local alerts back into the code path.
+ // For each local alert, read its corresponding OTP word and extract the class
+ // configuration for the current lifecycle state.
+ for (size_t i = 0; i < ALERT_HANDLER_LOC_ALERT_CLASS_MULTIREG_COUNT; ++i) {
+ uint32_t value = otp_read32(OTP_CTRL_PARAM_ROM_LOCAL_ALERT_CLASSIFICATION_OFFSET +
+ i * sizeof(uint32_t));
+ alert_class_t cls = (alert_class_t)bitfield_field32_read(
+ value, (bitfield_field32_t){.mask = 0xff, .index = lc_shift});
+ rom_error_t e = alert_local_configure(i, cls, enable[clsindex(cls)]);
+ if (e != kErrorOk) {
+ // Keep going if there is an error programming one alert. We want to
+ // program them all.
+ error = e;
+ }
+ }
+#endif
+
+ // For each alert class, configure the various escalation parameters.
+ const alert_class_t kClasses[] = {
+ kAlertClassA,
+ kAlertClassB,
+ kAlertClassC,
+ kAlertClassD,
+ };
+ alert_class_config_t config;
+ for (size_t i = 0; i < ALERT_CLASSES; ++i) {
+ config.enabled = enable[i];
+ config.escalation = escalate[i];
+ config.accum_threshold = otp_read32(
+ OTP_CTRL_PARAM_ROM_ALERT_ACCUM_THRESH_OFFSET + i * sizeof(uint32_t));
+ config.timeout_cycles = otp_read32(
+ OTP_CTRL_PARAM_ROM_ALERT_TIMEOUT_CYCLES_OFFSET + i * sizeof(uint32_t));
+ for (size_t phase = 0; phase < ARRAYSIZE(config.phase_cycles); ++phase) {
+ config.phase_cycles[phase] = otp_read32(
+ OTP_CTRL_PARAM_ROM_ALERT_PHASE_CYCLES_OFFSET +
+ (i * ARRAYSIZE(config.phase_cycles) + phase) * sizeof(uint32_t));
+ }
+
+ rom_error_t e = alert_class_configure(kClasses[i], &config);
+ if (e != kErrorOk) {
+ // Keep going if there is an error programming an alert class. We want to
+ // program them all.
+ error = e;
+ }
+ }
+ return error;
+}
+
+uint32_t shutdown_redact(rom_error_t reason, shutdown_error_redact_t severity) {
+ uint32_t redacted = (uint32_t)reason;
+ if (reason == kErrorOk) {
+ return 0;
+ }
+ switch (severity) {
+ case kShutdownErrorRedactModule:
+ redacted = bitfield_field32_write(redacted, ROM_ERROR_FIELD_MODULE, 0);
+ FALLTHROUGH_INTENDED;
+ case kShutdownErrorRedactError:
+ redacted = bitfield_field32_write(redacted, ROM_ERROR_FIELD_ERROR, 0);
+ FALLTHROUGH_INTENDED;
+ case kShutdownErrorRedactNone:
+ break;
+ case kShutdownErrorRedactAll:
+ FALLTHROUGH_INTENDED;
+ default:
+ redacted = kErrorUnknown;
+ }
+ return redacted;
+}
+
+SHUTDOWN_FUNC(NO_MODIFIERS, shutdown_software_escalate(void)) {
+ // TODO(lowRISC/opentitan#7148): Use a software alert when available.
+ // For now, signal a fatal_intg_error from SRAM.
+ enum { kBase = TOP_EARLGREY_SRAM_CTRL_MAIN_BASE_ADDR };
+ abs_mmio_write32(kBase + SRAM_CTRL_ALERT_TEST_REG_OFFSET, 1);
+}
+
+SHUTDOWN_FUNC(NO_MODIFIERS, shutdown_keymgr_kill(void)) {
+ enum {
+ kBase = TOP_EARLGREY_KEYMGR_BASE_ADDR,
+ };
+ uint32_t reg = bitfield_bit32_write(0, KEYMGR_CONTROL_START_BIT, true);
+ reg = bitfield_field32_write(reg, KEYMGR_CONTROL_DEST_SEL_FIELD,
+ KEYMGR_CONTROL_DEST_SEL_VALUE_NONE);
+ reg = bitfield_field32_write(reg, KEYMGR_CONTROL_OPERATION_FIELD,
+ KEYMGR_CONTROL_OPERATION_VALUE_DISABLE);
+ abs_mmio_write32(kBase + KEYMGR_CONTROL_REG_OFFSET, reg);
+ abs_mmio_write32(kBase + KEYMGR_SIDELOAD_CLEAR_REG_OFFSET, 1);
+}
+
+SHUTDOWN_FUNC(NO_MODIFIERS, shutdown_flash_kill(void)) {
+ enum { kBase = TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR };
+ // TODO(lowRISC/opentitan#7148): Add flash disable when the hw is implemented.
+ // abs_mmio_write32(kBase + FLASH_CTRL_FLASH_DISABLE_REG_OFFSET, 1);
+}
+
+SHUTDOWN_FUNC(noreturn, shutdown_hang(void)) {
+ enum { kSramCtrlBase = TOP_EARLGREY_SRAM_CTRL_MAIN_BASE_ADDR };
+
+ // Disable SRAM execution and lock the register.
+ abs_mmio_write32(kSramCtrlBase + SRAM_CTRL_EXEC_EN_OFFSET, 0);
+ abs_mmio_write32(kSramCtrlBase + SRAM_CTRL_EXEC_REGWEN_REG_OFFSET, 0);
+
+ // Switch to assembly as RAM (incl. stack) is about to get scrambled.
+#ifdef OT_PLATFORM_RV32
+ while (true) {
+ asm volatile(
+ // Request a new scrambling key, then lock the SRAM control register.
+ "sw %[kRenewKey], %[kCtrlOffset](%[kMainRamCtrlBase]);"
+ "sw zero, %[kRegWriteEn](%[kMainRamCtrlBase]);"
+
+ // TODO(lowRISC/opentitan#7148): restrict the ePMP such that only
+ // ROM may execute. mundaym's suggestion: set entry 2 as a NAPOT
+ // region covering the entire address space, clear all its permission
+ // bits and set the lock bit, and then finally disable RLB to prevent
+ // any further modifications.
+
+ // Generate a halt-maze.
+ "1:"
+ "wfi; wfi; wfi; wfi; j 1b;"
+ "wfi; wfi; j 1b;"
+ "wfi; j 1b;"
+ "wfi;"
+ :
+ : [kRenewKey] "r"(1 << SRAM_CTRL_CTRL_RENEW_SCR_KEY_BIT),
+ [kCtrlOffset] "I"(SRAM_CTRL_CTRL_REG_OFFSET),
+ [kMainRamCtrlBase] "r"(kSramCtrlBase),
+ [kRegWriteEn] "I"(SRAM_CTRL_CTRL_REGWEN_REG_OFFSET));
+ }
+#endif
+}
+
+void shutdown_finalize(rom_error_t reason) {
+ uint32_t redacted_error = shutdown_redact(
+ reason, otp_read32(OTP_CTRL_PARAM_ROM_ERROR_REPORTING_OFFSET));
+ base_printf("boot_fault: 0x%08x\n", redacted_error);
+ shutdown_software_escalate();
+ shutdown_keymgr_kill();
+ shutdown_flash_kill();
+ // If we get here, we'll wait for the watchdog to reset the chip.
+ shutdown_hang();
+}
diff --git a/sw/device/silicon_creator/lib/shutdown.h b/sw/device/silicon_creator/lib/shutdown.h
new file mode 100644
index 0000000..d79c309
--- /dev/null
+++ b/sw/device/silicon_creator/lib/shutdown.h
@@ -0,0 +1,69 @@
+// 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_SHUTDOWN_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_SHUTDOWN_H_
+#include <stdint.h>
+#include <stdnoreturn.h>
+
+#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
+#include "sw/device/silicon_creator/lib/error.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Initializes the Mask ROM shutdown infrastructure.
+ *
+ * Reads the shutdown policy from OTP, and initializes the alert handler.
+ *
+ * @param lc_state: Lifecycle state of the chip.
+ * @return: Any error encountered during initialization.
+ */
+rom_error_t shutdown_init(lifecycle_state_t lc_state);
+
+/**
+ * The error redaction possibilities in increasing severity.
+ *
+ * This value is read from the ROM_ERROR_REPORTING OTP word.
+ *
+ * No error code redaction
+ * Redact the specific error code.
+ * Redact the specific error code and source modules.
+ * Redact all error componens (general code, specific code and module).
+ */
+// TODO(lowRISC/opentitan#7148): Choose values for configuration enums.
+typedef enum shutdown_error_redact {
+ kShutdownErrorRedactNone,
+ kShutdownErrorRedactError,
+ kShutdownErrorRedactModule,
+ kShutdownErrorRedactAll,
+} shutdown_error_redact_t;
+
+/**
+ * Redact an error code.
+ *
+ * @param reason: The error code to be redacted.
+ * @param severity: The redaction severity.
+ * @return: The redacted error code.
+ */
+uint32_t shutdown_redact(rom_error_t reason, shutdown_error_redact_t severity);
+
+/**
+ * Perform a shutdown in the Mask ROM in response to an exceptional condition.
+ *
+ * @param reason: A reason for entering the shutdown state.
+ */
+#ifndef OT_OFF_TARGET_TEST
+// If this is a test, we'll omit `noreturn` so we can call this function
+// from within a test program.
+noreturn
+#endif
+ void
+ shutdown_finalize(rom_error_t reason);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_SHUTDOWN_H_
diff --git a/sw/device/silicon_creator/lib/shutdown_unittest.cc b/sw/device/silicon_creator/lib/shutdown_unittest.cc
new file mode 100644
index 0000000..73011de
--- /dev/null
+++ b/sw/device/silicon_creator/lib/shutdown_unittest.cc
@@ -0,0 +1,421 @@
+// 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/shutdown.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/testing/mask_rom_test.h"
+#include "sw/device/silicon_creator/lib/base/mock_abs_mmio.h"
+#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
+#include "sw/device/silicon_creator/lib/drivers/mock_alert.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "otp_ctrl_regs.h"
+
+// FIXME: I can't get ARRAYSIZE from `memory.h` because the definitions of
+// memcpy and friends there conflict with the standard definitions.
+#define ARRAYSIZE(x) ((sizeof x) / (sizeof x[0]))
+
+namespace shutdown_unittest {
+
+using ::testing::ElementsAre;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::Test;
+
+namespace {
+extern "C" {
+// Dummy out base_printf.
+int base_printf(const char *fmt, ...) { return 0; }
+} // extern "C"
+
+// TODO(lowRISC/opentitan#7148): Refactor mocks into their own headers.
+namespace internal {
+// Create a mock for reading OTP words.
+class MockOtp {
+ public:
+ MOCK_METHOD(uint32_t, otp_read32, (uint32_t));
+ virtual ~MockOtp() {}
+};
+
+// Create a mock for shutdown functions.
+class MockShutdown {
+ public:
+ MOCK_METHOD(void, shutdown_software_escalate, ());
+ MOCK_METHOD(void, shutdown_keymgr_kill, ());
+ MOCK_METHOD(void, shutdown_flash_kill, ());
+ MOCK_METHOD(void, shutdown_hang, ());
+ virtual ~MockShutdown() {}
+};
+
+} // namespace internal
+// Use NiceMock because we aren't interested in the specifics of OTP reads,
+// but we want to mock out calls to otp_read32.
+using MockOtp = mask_rom_test::GlobalMock<testing::NiceMock<internal::MockOtp>>;
+using MockShutdown =
+ mask_rom_test::GlobalMock<testing::StrictMock<internal::MockShutdown>>;
+extern "C" {
+uint32_t otp_read32(uint32_t address) {
+ return MockOtp::Instance().otp_read32(address);
+}
+
+void shutdown_software_escalate(void) {
+ return MockShutdown::Instance().shutdown_software_escalate();
+}
+void shutdown_keymgr_kill(void) {
+ return MockShutdown::Instance().shutdown_keymgr_kill();
+}
+void shutdown_flash_kill(void) {
+ return MockShutdown::Instance().shutdown_flash_kill();
+}
+void shutdown_hang(void) { return MockShutdown::Instance().shutdown_hang(); }
+} // extern "C"
+
+constexpr uint32_t Pack32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
+ uint32_t result = (a << 0) | (b << 8) | (c << 16) | (d << 24);
+ return result;
+}
+
+#define FULL(name, prod, prodend, dev, rma) \
+ { \
+ name, kAlertClass##prod, kAlertClass##prodend, kAlertClass##dev, \
+ kAlertClass##rma \
+ }
+
+#define CLASSIFY(name, prod, prodend, dev, rma) \
+ Pack32(kAlertClass##prod, kAlertClass##prodend, kAlertClass##dev, \
+ kAlertClass##rma)
+// This alert configuration is described in the Mask ROM Shutdown specification:
+// https://docs.google.com/document/d/1V8hRvQnJhsvddieJbRHS3azbPZvoBWxfxPZV_0YA1QU/edit#
+
+// clang-format off
+#define ALERTS(Xmacro) \
+ Xmacro("Uart0FatalFault", C, C, X, X), \
+ Xmacro("Uart1FatalFault", C, C, X, X), \
+ Xmacro("Uart2FatalFault", C, C, X, X), \
+ Xmacro("Uart3FatalFault", C, C, X, X), \
+ Xmacro("GpioFatalFault", C, C, X, X), \
+ Xmacro("SpiDeviceFatalFault", C, C, X, X), \
+ Xmacro("SpiHost0FatalFault", C, C, X, X), \
+ Xmacro("SpiHost1FatalFault", C, C, X, X), \
+ Xmacro("I2c0FatalFault", C, C, X, X), \
+ Xmacro("I2c1FatalFault", C, C, X, X), \
+ Xmacro("I2c2FatalFault", C, C, X, X), \
+ Xmacro("PattgenFatalFault", C, C, X, X), \
+ Xmacro("OtpCtrlFatalMacroError", A, A, X, X), \
+ Xmacro("OtpCtrlFatalCheckError", A, A, X, X), \
+ Xmacro("LcCtrlFatalProgError", A, A, X, X), \
+ Xmacro("LcCtrlFatalStateError", A, A, X, X), \
+ Xmacro("LcCtrlFatalBusIntegError", A, A, X, X), \
+ Xmacro("PwrmgrAonFatalFault", C, C, X, X), \
+ Xmacro("RstmgrAonFatalFault", C, C, X, X), \
+ Xmacro("ClkmgrAonFatalFault", C, C, X, X), \
+ Xmacro("SysrstCtrlAonFatalFault", C, C, X, X), \
+ Xmacro("AdcCtrlAonFatalFault", C, C, X, X), \
+ Xmacro("PwmAonFatalFault", C, C, X, X), \
+ Xmacro("PinmuxAonFatalFault", C, C, X, X), \
+ Xmacro("AonTimerAonFatalFault", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovAs", B, B, X, X), \
+ Xmacro("SensorCtrlAonRecovCg", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovGd", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovTsHi", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovTsLo", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovFla", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOtp", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt0", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt1", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt2", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt3", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt4", C, C, X, X), \
+ Xmacro("SensorCtrlAonRecovOt5", C, C, X, X), \
+ Xmacro("SramCtrlRetAonFatalIntgError", B, B, X, X), \
+ Xmacro("SramCtrlRetAonFatalParityError", B, B, X, X), \
+ Xmacro("FlashCtrlRecovErr", D, D, X, X), \
+ Xmacro("FlashCtrlRecovMpErr", D, D, X, X), \
+ Xmacro("FlashCtrlRecovEccErr", D, D, X, X), \
+ Xmacro("FlashCtrlFatalIntgErr", A, A, X, X), \
+ Xmacro("RvPlicFatalFault", A, A, X, X), \
+ Xmacro("AesRecovCtrlUpdateErr", D, D, X, X), \
+ Xmacro("AesFatalFault", A, A, X, X), \
+ Xmacro("HmacFatalFault", A, A, X, X), \
+ Xmacro("KmacFatalFault", A, A, X, X), \
+ Xmacro("KeymgrFatalFaultErr", A, A, X, X), \
+ Xmacro("KeymgrRecovOperationErr", D, D, X, X), \
+ Xmacro("CsrngFatalAlert", A, A, X, X), \
+ Xmacro("EntropySrcRecovAlert", D, D, X, X), \
+ Xmacro("EntropySrcFatalAlert", A, A, X, X), \
+ Xmacro("Edn0FatalAlert", A, A, X, X), \
+ Xmacro("Edn1FatalAlert", A, A, X, X), \
+ Xmacro("SramCtrlMainFatalIntgError", A, A, X, X), \
+ Xmacro("SramCtrlMainFatalParityError", A, A, X, X), \
+ Xmacro("OtbnFatal", A, A, X, X), \
+ Xmacro("OtbnRecov", D, D, X, X), \
+ Xmacro("RomCtrlFatal", A, A, X, X)
+
+// TODO: find all of the local alerts and define them.
+#define LOC_ALERTS(Xmacro) \
+ Xmacro("LocAlertPingFail", A, A, X, X), \
+ Xmacro("LocEscPingFail", A, A, X, X), \
+ Xmacro("LocAlertIntegrityFail", A, A, X, X), \
+ Xmacro("LocEscIntegrityFail", A, A, X, X), \
+// clang-format on
+
+// TODO: adjust this to match the OTP layout in PR#6921.
+struct OtpConfiguration {
+ uint32_t rom_error_reporting;
+ uint32_t rom_bootstrap_en;
+ uint32_t rom_fault_response;
+ uint32_t rom_alert_class_en;
+ uint32_t rom_alert_escalation;
+ uint32_t rom_alert_classification[80];
+ uint32_t rom_local_alert_classification[16];
+ uint32_t rom_alert_accum_thresh[4];
+ uint32_t rom_alert_timeout_cycles[4];
+ uint32_t rom_alert_phase_cycles[4][4];
+};
+
+struct DefaultAlertClassification {
+ const char *name;
+ alert_class_t prod;
+ alert_class_t prodend;
+ alert_class_t dev;
+ alert_class_t rma;
+};
+
+constexpr OtpConfiguration kOtpConfig = {
+ .rom_error_reporting = (uint32_t)kShutdownErrorRedactNone,
+ .rom_bootstrap_en = 1,
+ .rom_fault_response = 0,
+ .rom_alert_class_en = Pack32(kAlertEnableLocked, kAlertEnableEnabled,
+ kAlertEnableNone, kAlertEnableNone),
+ .rom_alert_escalation = Pack32(kAlertEscalatePhase3, kAlertEscalatePhase3,
+ kAlertEscalateNone, kAlertEscalateNone),
+ .rom_alert_classification = {ALERTS(CLASSIFY)},
+ .rom_local_alert_classification = {LOC_ALERTS(CLASSIFY)},
+ .rom_alert_accum_thresh = {0, 0, 0, 0},
+ .rom_alert_timeout_cycles = {0, 0, 0, 0},
+ .rom_alert_phase_cycles =
+ {
+ {0, 10, 10, 0xFFFFFFFF}, // Class A
+ {0, 10, 10, 0xFFFFFFFF}, // Class B
+ {0, 0, 0, 0}, // Class C
+ {0, 0, 0, 0}, // Class D
+ },
+};
+
+constexpr DefaultAlertClassification kDefaultAlertClassification[] = {
+ ALERTS(FULL),
+};
+
+static_assert(
+ ARRAYSIZE(kDefaultAlertClassification) == kTopEarlgreyAlertIdLast + 1,
+ "The default alert classification doesn't match the number of alerts");
+
+constexpr alert_class_t kClasses[] = {
+ kAlertClassA,
+ kAlertClassB,
+ kAlertClassC,
+ kAlertClassD,
+};
+
+alert_enable_t RomAlertClassEnable(alert_class_t cls) {
+ // Note: these need to match with `rom_alert_class_en` above.
+ switch (cls) {
+ case kAlertClassA:
+ return kAlertEnableLocked;
+ case kAlertClassB:
+ return kAlertEnableEnabled;
+ case kAlertClassC:
+ return kAlertEnableNone;
+ case kAlertClassD:
+ return kAlertEnableNone;
+ // Class X (and all other invalid classes) default to class A's enable
+ // status.
+ default:
+ return kAlertEnableLocked;
+ }
+}
+
+alert_escalate_t RomAlertClassEscalation(alert_class_t cls) {
+ // Note: these need to match with `rom_alert_class_escalation` above.
+ switch (cls) {
+ case kAlertClassA:
+ return kAlertEscalatePhase3;
+ case kAlertClassB:
+ return kAlertEscalatePhase3;
+ case kAlertClassC:
+ return kAlertEscalateNone;
+ case kAlertClassD:
+ return kAlertEscalateNone;
+ // Class X (and all other invalid classes) default to class A's escalate
+ // setting.
+ default:
+ return kAlertEscalatePhase3;
+ }
+}
+
+class ShutdownTest : public mask_rom_test::MaskRomTest {
+ protected:
+
+ void SetupOtpReads() {
+ // Make OTP reads retrieve their values from `otp_config_`.
+ ON_CALL(otp_, otp_read32(::testing::_))
+ .WillByDefault([this](uint32_t address) {
+ // Must be aligned and in the SW_CFG partition.
+ EXPECT_EQ(address % 4, 0);
+ EXPECT_GE(address, OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET);
+ EXPECT_LT(address, OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET +
+ sizeof(this->otp_config_));
+ // Convert the address to a word index.
+ uint32_t index = (address - OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET) / 4;
+ const uint32_t *words =
+ reinterpret_cast<const uint32_t *>(&this->otp_config_);
+ return words[index];
+ });
+ }
+
+ void ExpectClassConfigure() {
+ ExpectClassConfigure(0);
+ ExpectClassConfigure(1);
+ ExpectClassConfigure(2);
+ ExpectClassConfigure(3);
+ }
+
+ void ExpectClassConfigure(size_t i) {
+ alert_class_t expected_cls = kClasses[i];
+ EXPECT_CALL(alert_, alert_class_configure(expected_cls, ::testing::_))
+ .WillOnce(Invoke([this, i](alert_class_t cls,
+ const alert_class_config_t *config) {
+ alert_class_t expected_cls = kClasses[i];
+ // Would like to use testing::FiledsAre, but we need a gtest upgrade
+ // for that.
+ EXPECT_EQ(cls, expected_cls);
+ EXPECT_EQ(config->enabled, RomAlertClassEnable(expected_cls));
+ EXPECT_EQ(config->escalation, RomAlertClassEscalation(expected_cls));
+ EXPECT_EQ(config->accum_threshold,
+ otp_config_.rom_alert_accum_thresh[i]);
+ EXPECT_EQ(config->timeout_cycles,
+ otp_config_.rom_alert_timeout_cycles[i]);
+ EXPECT_THAT(config->phase_cycles,
+ ElementsAre(otp_config_.rom_alert_phase_cycles[i][0],
+ otp_config_.rom_alert_phase_cycles[i][1],
+ otp_config_.rom_alert_phase_cycles[i][2],
+ otp_config_.rom_alert_phase_cycles[i][3]));
+ return kErrorOk;
+ }));
+ }
+
+ OtpConfiguration otp_config_ = kOtpConfig;
+ MockOtp otp_;
+ MockShutdown shutdown_;
+ mask_rom_test::MockAlert alert_;
+};
+
+TEST_F(ShutdownTest, InitializeProd) {
+ SetupOtpReads();
+ uint32_t index = 0;
+ for (const auto &c : kDefaultAlertClassification) {
+ alert_class_t cls = c.prod;
+ alert_enable_t en = RomAlertClassEnable(cls);
+ EXPECT_CALL(alert_, alert_configure(index, cls, en))
+ .WillOnce(Return(kErrorOk));
+ index += 1;
+ }
+ ExpectClassConfigure();
+ EXPECT_EQ(shutdown_init(kLcStateProd), kErrorOk);
+}
+
+TEST_F(ShutdownTest, InitializeProdWithError) {
+ SetupOtpReads();
+ uint32_t index = 0;
+ for (const auto &c : kDefaultAlertClassification) {
+ alert_class_t cls = c.prod;
+ alert_enable_t en = RomAlertClassEnable(cls);
+ // Return an error on index zero. The error should not cause alert
+ // configuation to abort early (ie: still expect the rest of the
+ // alerts to get configured).
+ EXPECT_CALL(alert_, alert_configure(index, cls, en))
+ .WillOnce(Return(index == 0 ? kErrorUnknown : kErrorOk));
+ index += 1;
+ }
+ ExpectClassConfigure();
+ // We expect to get the error from alert configuration.
+ EXPECT_EQ(shutdown_init(kLcStateProd), kErrorUnknown);
+}
+
+TEST_F(ShutdownTest, InitializeProdEnd) {
+ SetupOtpReads();
+ uint32_t index = 0;
+ for (const auto &c : kDefaultAlertClassification) {
+ alert_class_t cls = c.prodend;
+ alert_enable_t en = RomAlertClassEnable(cls);
+ EXPECT_CALL(alert_, alert_configure(index, cls, en))
+ .WillOnce(Return(kErrorOk));
+ index += 1;
+ }
+ ExpectClassConfigure();
+ EXPECT_EQ(shutdown_init(kLcStateProdEnd), kErrorOk);
+}
+
+TEST_F(ShutdownTest, InitializeDev) {
+ SetupOtpReads();
+ uint32_t index = 0;
+ for (const auto &c : kDefaultAlertClassification) {
+ alert_class_t cls = c.dev;
+ alert_enable_t en = RomAlertClassEnable(cls);
+ EXPECT_CALL(alert_, alert_configure(index, cls, en))
+ .WillOnce(Return(kErrorOk));
+ index += 1;
+ }
+ ExpectClassConfigure();
+ EXPECT_EQ(shutdown_init(kLcStateDev), kErrorOk);
+}
+
+TEST_F(ShutdownTest, InitializeRma) {
+ SetupOtpReads();
+ uint32_t index = 0;
+ for (const auto &c : kDefaultAlertClassification) {
+ alert_class_t cls = c.rma;
+ alert_enable_t en = RomAlertClassEnable(cls);
+ EXPECT_CALL(alert_, alert_configure(index, cls, en))
+ .WillOnce(Return(kErrorOk));
+ index += 1;
+ }
+ ExpectClassConfigure();
+ EXPECT_EQ(shutdown_init(kLcStateRma), kErrorOk);
+}
+
+TEST(ShutdownModule, RedactErrors) {
+ EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactNone), 0);
+ EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactError), 0);
+ EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactModule), 0);
+ EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactAll), 0);
+
+ EXPECT_EQ(shutdown_redact(kErrorUartBadBaudRate, kShutdownErrorRedactNone),
+ 0x02415503);
+ EXPECT_EQ(shutdown_redact(kErrorUartBadBaudRate, kShutdownErrorRedactError),
+ 0x00415503);
+ EXPECT_EQ(shutdown_redact(kErrorUartBadBaudRate, kShutdownErrorRedactModule),
+ 0x00000003);
+ EXPECT_EQ(shutdown_redact(kErrorUartBadBaudRate, kShutdownErrorRedactAll),
+ 0xFFFFFFFF);
+}
+
+TEST_F(ShutdownTest, ShutdownFinalize) {
+ SetupOtpReads();
+ EXPECT_CALL(shutdown_, shutdown_software_escalate());
+ EXPECT_CALL(shutdown_, shutdown_keymgr_kill());
+ EXPECT_CALL(shutdown_, shutdown_flash_kill());
+ EXPECT_CALL(shutdown_, shutdown_hang());
+
+ // In the RV32 environment, finalize should never return.
+ // In the X86_64 unittest environment, verify that all of the various
+ // kill functions were called.
+ shutdown_finalize(kErrorUnknown);
+}
+
+} // namespace
+} // namespace shutdown_unittest
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.c b/sw/device/silicon_creator/mask_rom/mask_rom.c
index 5398e21..34a4e9d 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom.c
+++ b/sw/device/silicon_creator/mask_rom/mask_rom.c
@@ -8,6 +8,7 @@
#include <stdint.h>
#include "sw/device/lib/arch/device.h"
+#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/stdasm.h"
@@ -15,17 +16,51 @@
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/print.h"
#include "sw/device/silicon_creator/lib/drivers/keymgr.h"
+#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
#include "sw/device/silicon_creator/lib/drivers/uart.h"
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/lib/shutdown.h"
#include "sw/device/silicon_creator/lib/sigverify.h"
#include "sw/device/silicon_creator/mask_rom/romextimage.h"
#include "sw/device/silicon_creator/mask_rom/sigverify_keys.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
-void mask_rom_exception_handler(void) { wait_for_interrupt(); }
-void mask_rom_nmi_handler(void) { wait_for_interrupt(); }
+static inline rom_error_t mask_rom_irq_error(void) {
+ uint32_t mcause;
+ CSR_READ(CSR_REG_MCAUSE, &mcause);
+ // Shuffle the mcause bits into the uppermost byte of the word and report
+ // the cause as kErrorInterrupt.
+ // Based on the ibex verilog, it appears that the most significant bit
+ // indicates whether the cause is an exception (0) or external interrupt (1),
+ // and the 5 least significant bits indicate which exception/interrupt.
+ //
+ // Preserve the MSB and shift the 7 LSBs into the upper byte.
+ // (we preserve 7 instead of 5 because the verilog hardcodes the unused bits
+ // as zero and those would be the next bits used should the number of
+ // interrupt causes increase).
+ mcause = (mcause & 0x80000000) | ((mcause & 0x7f) << 24);
+ return kErrorInterrupt + mcause;
+}
-void mask_rom_boot(void) {
+void mask_rom_interrupt_handler(void) {
+ shutdown_finalize(mask_rom_irq_error());
+}
+
+// We only need a single handler for all mask ROM interrupts, but we want to
+// keep distinct symbols to make writing tests easier. In the mask ROM,
+// alias all interrupt handler symbols to the single handler.
+void mask_rom_exception_handler(void)
+ __attribute__((alias("mask_rom_interrupt_handler")));
+
+void mask_rom_nmi_handler(void)
+ __attribute__((alias("mask_rom_interrupt_handler")));
+
+rom_error_t mask_rom_boot(void) {
+ // Initialize the shutdown policy according to lifecycle state.
+ lifecycle_state_t lc_state = lifecycle_state_get();
+ shutdown_init(lc_state);
+
// Initialize pinmux configuration so we can use the UART.
pinmux_init();
@@ -37,7 +72,8 @@
});
// FIXME: what (if anything) should we print at startup?
- base_printf("MaskROM\r\n");
+ base_printf("OpenTitan: \"version-tag\"\r\n");
+ base_printf("lc_state: %s\r\n", lifecycle_state_name[lc_state]);
// boot_reason = read_boot_reason(); // Boot Policy Module
@@ -74,9 +110,7 @@
while (true) {
// TODO: Should we load the entropy_reseed_interval from OTP?
const uint16_t reseed_interval = 0x100;
- if (keymgr_init(reseed_interval) != kErrorOk) {
- break;
- }
+ RETURN_IF_ERROR(keymgr_init(reseed_interval));
// Check ROM_EXT Manifest (2.c.ii)
// **Open Q:** Integration with Secure Boot Hardware
@@ -93,20 +127,12 @@
const manifest_t *manifest;
manifest_signed_region_t signed_region;
const sigverify_rsa_key_t *key;
- if (romextimage_manifest_get(kFlashSlotA, &manifest) != kErrorOk) {
- break;
- }
- if (manifest_signed_region_get(manifest, &signed_region) != kErrorOk) {
- break;
- }
- if (sigverify_rsa_key_get(sigverify_rsa_key_id_get(&manifest->modulus),
- &key) != kErrorOk) {
- break;
- }
- if (sigverify_rsa_verify(signed_region.start, signed_region.length,
- &manifest->signature, key) != kErrorOk) {
- break;
- }
+ RETURN_IF_ERROR(romextimage_manifest_get(kFlashSlotA, &manifest));
+ RETURN_IF_ERROR(manifest_signed_region_get(manifest, &signed_region));
+ RETURN_IF_ERROR(sigverify_rsa_key_get(
+ sigverify_rsa_key_id_get(&manifest->modulus), &key));
+ RETURN_IF_ERROR(sigverify_rsa_verify(
+ signed_region.start, signed_region.length, &manifest->signature, key));
// // Manifest Failure (check Boot Policy)
// // **Open Q:** Does this need different logic to the check after
@@ -136,10 +162,8 @@
// CreatorRootKey (2.c.iv)
// - This is only allowed to be done if we have verified the signature on
// the current ROM_EXT.
- if (keymgr_state_advance_to_creator(
- &manifest->binding_value, manifest->max_key_version) != kErrorOk) {
- break;
- }
+ RETURN_IF_ERROR(keymgr_state_advance_to_creator(&manifest->binding_value,
+ manifest->max_key_version));
// Lock down Peripherals based on descriptors in ROM_EXT manifest.
// - This does not cover key-related lockdown, which is done in
@@ -151,8 +175,9 @@
// **Open Q:** Do we need to prevent access to Mask ROM after final jump?
// pmp_unlock_rom_ext(); // Hardened Jump Module.
+ // TODO(#6653): The keymgr lands in a disabled state with error code 0xe.
+ // RETURN_IF_ERROR(keymgr_state_creator_check());
if (keymgr_state_creator_check() != kErrorOk) {
- // TODO(#6653): The keymgr lands in a disabled state with error code 0xe.
base_printf("ERROR keymgr: Failed to reach creator state.\n");
}
@@ -180,10 +205,10 @@
}
}
- // Boot failed for all ROM_EXTs allowed by boot policy
- // boot_failed(boot_policy); // Boot Policy Module
- asm volatile("unimp");
- while (true) {
- wait_for_interrupt();
- }
+ return kErrorMaskRomBootFailed;
+}
+
+void mask_rom_main(void) {
+ rom_error_t error = mask_rom_boot();
+ shutdown_finalize(error);
}
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.h b/sw/device/silicon_creator/mask_rom/mask_rom.h
index d52b92b..969bf2d 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom.h
+++ b/sw/device/silicon_creator/mask_rom/mask_rom.h
@@ -37,25 +37,14 @@
/**
* The first C function executed by the Mask ROM (defined in `mask_rom.c`)
*/
-noreturn void mask_rom_boot(void);
+noreturn void mask_rom_main(void);
/**
* Mask ROM hardware exception handler.
- *
- * This may not be able to be implemented in C.
*/
MASK_ROM_VECTOR_FUNCTION
MASK_ROM_INTERRUPT_HANDLER_ABI
-void mask_rom_exception_handler(void);
-
-/**
- * Mask ROM non-maskable interrupt handler.
- *
- * This may not be able to be implemented in C.
- */
-MASK_ROM_VECTOR_FUNCTION
-MASK_ROM_INTERRUPT_HANDLER_ABI
-void mask_rom_nmi_handler(void);
+void mask_rom_interrupt_handler(void);
#ifdef __cplusplus
} // extern "C"
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom_start.S b/sw/device/silicon_creator/mask_rom/mask_rom_start.S
index cd3bbab..dd3dcd7 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom_start.S
+++ b/sw/device/silicon_creator/mask_rom/mask_rom_start.S
@@ -56,43 +56,44 @@
// Exception and User Software Interrupt Handler.
.extern mask_rom_exception_handler
+ .extern mask_rom_interrupt_handler
j mask_rom_exception_handler
// Supervisor Software Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Reserved.
- unimp
+ j mask_rom_interrupt_handler
// Machine Software Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// User Timer Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Supervisor Timer Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Reserved.
- unimp
+ j mask_rom_interrupt_handler
// Machine Timer Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// User External Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Supervisor External Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Reserved.
- unimp
+ j mask_rom_interrupt_handler
// Machine External Interrupt Handler.
- unimp
+ j mask_rom_interrupt_handler
// Reserved.
- unimp
- unimp
- unimp
- unimp
+ j mask_rom_interrupt_handler
+ j mask_rom_interrupt_handler
+ j mask_rom_interrupt_handler
+ j mask_rom_interrupt_handler
// Vendor Interrupt Handlers:
// On Ibex interrupt ids 30-16 are for "fast" interrupts.
.rept 15
- unimp
+ j mask_rom_interrupt_handler
.endr
// On Ibex interrupt id 31 is for non-maskable interrupts.
@@ -276,8 +277,8 @@
/**
* Jump to C Code
*/
- .extern mask_rom_boot
- tail mask_rom_boot
+ .extern mask_rom_main
+ tail mask_rom_main
// Set size so this function can be disassembled.
.size _mask_rom_start_boot, .-_mask_rom_start_boot
diff --git a/sw/device/silicon_creator/mask_rom/meson.build b/sw/device/silicon_creator/mask_rom/meson.build
index e927bdb..9ad384b 100644
--- a/sw/device/silicon_creator/mask_rom/meson.build
+++ b/sw/device/silicon_creator/mask_rom/meson.build
@@ -49,9 +49,11 @@
freestanding_headers,
sw_silicon_creator_lib_driver_hmac,
sw_silicon_creator_lib_driver_keymgr,
+ sw_silicon_creator_lib_driver_lifecycle,
sw_silicon_creator_lib_driver_uart,
sw_silicon_creator_lib_fake_deps,
sw_silicon_creator_lib_manifest,
+ sw_silicon_creator_lib_shutdown,
sw_silicon_creator_mask_rom_sigverify,
sw_silicon_creator_mask_rom_romextimage,
sw_lib_crt,