[sw/silicon_creator] Use OTP library in shutdown module
1. Follow the redaction policy in #7353 (no redaction in RMA and
test unlocked lifecycle states).
2. Use the OTP library to read from OTP.
The OTP and lifecycle libraries use sec_mmio underneath the hood.
Since sec_mmio can trigger a call to shutdown_finalize we need to
avoid calling it from shutdown_finalize. To acheive this a new
shutdown_redact_policy function has been added that determines
the redaction policy to apply without using either library.
This change required an increase in the number of registers that
`sec_mmio_ctx` has capacity to track. I've bumped it up to 200 for
now.
Signed-off-by: Michael Munday <mike.munday@lowrisc.org>
diff --git a/sw/device/silicon_creator/lib/base/sec_mmio.c b/sw/device/silicon_creator/lib/base/sec_mmio.c
index 486d42d..c9192b6 100644
--- a/sw/device/silicon_creator/lib/base/sec_mmio.c
+++ b/sw/device/silicon_creator/lib/base/sec_mmio.c
@@ -4,6 +4,8 @@
#include "sw/device/silicon_creator/lib/base/sec_mmio.h"
+#include <assert.h>
+
#include "sw/device/lib/base/memory.h"
#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
@@ -14,12 +16,17 @@
// FIXME: Replace for shutdown module handler.
static sec_mmio_shutdown_handler sec_mmio_shutdown_cb;
-// Value with good hamming weight used to mask the stored expected value.
-static const uint32_t kSecMmioMaskVal = 0x21692436u;
+enum {
+ // Value with good hamming weight used to mask the stored expected value.
+ kSecMmioMaskVal = 0x21692436u,
-// This must be set to a prime number greater than the number of items in
-// `sec_mmio_ctx.addrs`. Used to generate random read order permutations.
-static const uint32_t kSecMmioRndStep = 103u;
+ // This must be set to a prime number greater than the number of items in
+ // `sec_mmio_ctx.addrs`. Used to generate random read order permutations.
+ kSecMmioRndStep = 211u,
+};
+
+static_assert((uint32_t)kSecMmioRndStep > (uint32_t)kSecMmioRegFileSize,
+ "kSecMmioRndStep not large enough");
/**
* Updates or inserts the register entry pointed to by MMIO `addr` with the
diff --git a/sw/device/silicon_creator/lib/base/sec_mmio.h b/sw/device/silicon_creator/lib/base/sec_mmio.h
index 18d648d..e63f039 100644
--- a/sw/device/silicon_creator/lib/base/sec_mmio.h
+++ b/sw/device/silicon_creator/lib/base/sec_mmio.h
@@ -61,7 +61,7 @@
* This value must be less than the `kSecMmioRndStep` in sec_mmio.c.
*/
// TODO(#6609): Update size of expectations table.
- kSecMmioRegFileSize = 100,
+ kSecMmioRegFileSize = 200,
};
/**
@@ -109,12 +109,12 @@
* to both boot stages.
*/
OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, values, 0);
-OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, addrs, 400);
-OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, last_index, 800);
-OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, write_count, 804);
-OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, expected_write_count, 808);
-OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, check_count, 812);
-OT_ASSERT_SIZE(sec_mmio_ctx_t, 816); // Checked by linker script.
+OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, addrs, 800);
+OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, last_index, 1600);
+OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, write_count, 1604);
+OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, expected_write_count, 1608);
+OT_ASSERT_MEMBER_OFFSET(sec_mmio_ctx_t, check_count, 1612);
+OT_ASSERT_SIZE(sec_mmio_ctx_t, 1616); // Checked by linker script.
/**
* Shutdown module callback handler.
diff --git a/sw/device/silicon_creator/lib/base/static_critical.ld b/sw/device/silicon_creator/lib/base/static_critical.ld
index 03e7dc3..80b169d 100644
--- a/sw/device/silicon_creator/lib/base/static_critical.ld
+++ b/sw/device/silicon_creator/lib/base/static_critical.ld
@@ -11,4 +11,4 @@
ASSERT(. == ORIGIN(ram_main), "Error: .static_critical section not at the base address of main RAM.");
ASSERT(. - ADDR(.static_critical) == 0, "Error: .static_critical.sec_mmio_ctx section offset has changed.");
KEEP(*(.static_critical.sec_mmio_ctx))
-ASSERT(. - ADDR(.static_critical) == 816, "Error: .static_critical.sec_mmio_ctx section size has changed");
+ASSERT(. - ADDR(.static_critical) == 1616, "Error: .static_critical.sec_mmio_ctx section size has changed");
diff --git a/sw/device/silicon_creator/lib/meson.build b/sw/device/silicon_creator/lib/meson.build
index cec69ec..4a4449f 100644
--- a/sw/device/silicon_creator/lib/meson.build
+++ b/sw/device/silicon_creator/lib/meson.build
@@ -69,6 +69,7 @@
sources: [
hw_ip_alert_handler_reg_h,
hw_ip_otp_ctrl_reg_h,
+ hw_ip_lc_ctrl_reg_h,
hw_ip_keymgr_reg_h,
hw_ip_sram_ctrl_reg_h,
hw_ip_flash_ctrl_reg_h,
@@ -120,6 +121,7 @@
'sw_silicon_creator_lib_shutdown_unittest',
sources: [
hw_ip_alert_handler_reg_h,
+ hw_ip_lc_ctrl_reg_h,
hw_ip_otp_ctrl_reg_h,
hw_ip_keymgr_reg_h,
hw_ip_sram_ctrl_reg_h,
diff --git a/sw/device/silicon_creator/lib/shutdown.c b/sw/device/silicon_creator/lib/shutdown.c
index 170aec2..d4546bb 100644
--- a/sw/device/silicon_creator/lib/shutdown.c
+++ b/sw/device/silicon_creator/lib/shutdown.c
@@ -14,11 +14,13 @@
#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 "sw/device/silicon_creator/lib/drivers/otp.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 "lc_ctrl_regs.h"
#include "otp_ctrl_regs.h"
#include "sram_ctrl_regs.h"
@@ -46,16 +48,6 @@
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_CORE_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.
@@ -174,7 +166,13 @@
return error;
}
-uint32_t shutdown_redact(rom_error_t reason, shutdown_error_redact_t severity) {
+/**
+ * Implementation of `shutdown_redact` that is guaranteed to be inlined.
+ *
+ * This function must be inlined because it is called from `shutdown_finalize`.
+ */
+static ALWAYS_INLINE uint32_t
+shutdown_redact_inline(rom_error_t reason, shutdown_error_redact_t severity) {
uint32_t redacted = (uint32_t)reason;
if (reason == kErrorOk) {
return 0;
@@ -196,6 +194,70 @@
return redacted;
}
+uint32_t shutdown_redact(rom_error_t reason, shutdown_error_redact_t severity) {
+ return shutdown_redact_inline(reason, severity);
+}
+
+/**
+ * Implementation of `shutdown_redact_policy` that is guaranteed to be inlined.
+ *
+ * This function must be inlined because it is called from `shutdown_finalize`.
+ */
+static ALWAYS_INLINE shutdown_error_redact_t
+shutdown_redact_policy_inline(void) {
+ // Determine the error code redaction policy to apply according to the
+ // lifecycle state and OTP configuration.
+ //
+ // Note that we cannot use the lifecycle or OTP libraries since an error
+ // may trigger a call to `shutdown_finalize`.
+ lifecycle_state_t lc_state = (lifecycle_state_t)bitfield_field32_read(
+ abs_mmio_read32(TOP_EARLGREY_LC_CTRL_BASE_ADDR +
+ LC_CTRL_LC_STATE_REG_OFFSET),
+ LC_CTRL_LC_STATE_STATE_FIELD);
+ switch (lc_state) {
+ case kLcStateRaw:
+ case kLcStateTestUnlocked0:
+ case kLcStateTestUnlocked1:
+ case kLcStateTestUnlocked2:
+ case kLcStateTestUnlocked3:
+ case kLcStateTestUnlocked4:
+ case kLcStateTestUnlocked5:
+ case kLcStateTestUnlocked6:
+ case kLcStateTestUnlocked7:
+ case kLcStateRma:
+ // No error redaction in RAW, TEST_UNLOCKED and RMA states.
+ return kShutdownErrorRedactNone;
+ case kLcStateProd:
+ case kLcStateProdEnd:
+ case kLcStateDev:
+ // In production states use the redaction level specified in OTP.
+ return (shutdown_error_redact_t)abs_mmio_read32(
+ TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR +
+ OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
+ OTP_CTRL_PARAM_ROM_ERROR_REPORTING_OFFSET);
+ default:
+ // Redact everything if in an unexpected lifecycle state.
+ return kShutdownErrorRedactAll;
+ }
+}
+
+shutdown_error_redact_t shutdown_redact_policy(void) {
+ return shutdown_redact_policy_inline();
+}
+
+SHUTDOWN_FUNC(NO_MODIFIERS, shutdown_report_error(rom_error_t reason)) {
+ // Call the inline variant of `shutdown_redact_policy` because we want to
+ // guarantee that we won't jump to a different function.
+ shutdown_error_redact_t policy = shutdown_redact_policy_inline();
+
+ // Call the inline variant of `shutdown_redact` because we want to guarantee
+ // that we won't jump to a different function.
+ uint32_t redacted_error = shutdown_redact_inline(reason, policy);
+
+ // TODO(lowRISC/opentitan#8236): base_printf is in the .text section.
+ base_printf("boot_fault: 0x%08x\n", redacted_error);
+}
+
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.
@@ -265,12 +327,7 @@
__attribute__((section(".shutdown")))
#endif
void shutdown_finalize(rom_error_t reason) {
- uint32_t redacted_error = shutdown_redact(
- reason, otp_read32(OTP_CTRL_PARAM_ROM_ERROR_REPORTING_OFFSET));
-
- // TODO(lowRISC/opentitan#8236): base_printf is in the .text section.
- base_printf("boot_fault: 0x%08x\n", redacted_error);
-
+ shutdown_report_error(reason);
shutdown_software_escalate();
shutdown_keymgr_kill();
shutdown_flash_kill();
diff --git a/sw/device/silicon_creator/lib/shutdown.h b/sw/device/silicon_creator/lib/shutdown.h
index d79c309..044ff64 100644
--- a/sw/device/silicon_creator/lib/shutdown.h
+++ b/sw/device/silicon_creator/lib/shutdown.h
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <stdnoreturn.h>
+#include "sw/device/lib/base/macros.h"
#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
#include "sw/device/silicon_creator/lib/error.h"
#ifdef __cplusplus
@@ -19,6 +20,8 @@
* Reads the shutdown policy from OTP, and initializes the alert handler.
*
* @param lc_state: Lifecycle state of the chip.
+ * @param[out] redaction Redaction level initialized according to the lifecycle
+ * state and OTP configuration.
* @return: Any error encountered during initialization.
*/
rom_error_t shutdown_init(lifecycle_state_t lc_state);
@@ -42,6 +45,15 @@
} shutdown_error_redact_t;
/**
+ * Calculate the error redaction level required given the current lifecycle
+ * state and OTP configuration.
+ *
+ * @return Redaction level to apply to error codes.
+ */
+OT_WARN_UNUSED_RESULT
+shutdown_error_redact_t shutdown_redact_policy(void);
+
+/**
* Redact an error code.
*
* @param reason: The error code to be redacted.
@@ -53,7 +65,7 @@
/**
* Perform a shutdown in the Mask ROM in response to an exceptional condition.
*
- * @param reason: A reason for entering the shutdown state.
+ * @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
diff --git a/sw/device/silicon_creator/lib/shutdown_unittest.cc b/sw/device/silicon_creator/lib/shutdown_unittest.cc
index 5d94cb0..102bdf2 100644
--- a/sw/device/silicon_creator/lib/shutdown_unittest.cc
+++ b/sw/device/silicon_creator/lib/shutdown_unittest.cc
@@ -16,6 +16,7 @@
#include "alert_handler_regs.h"
#include "flash_ctrl_regs.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+#include "lc_ctrl_regs.h"
#include "otp_ctrl_regs.h"
// FIXME: I can't get ARRAYSIZE from `memory.h` because the definitions of
@@ -40,6 +41,7 @@
// Create a mock for shutdown functions.
class MockShutdown : public ::mask_rom_test::GlobalMock<MockShutdown> {
public:
+ MOCK_METHOD(void, shutdown_report_error, (rom_error_t));
MOCK_METHOD(void, shutdown_software_escalate, ());
MOCK_METHOD(void, shutdown_keymgr_kill, ());
MOCK_METHOD(void, shutdown_flash_kill, ());
@@ -52,7 +54,9 @@
} // namespace internal
using MockShutdown = testing::StrictMock<internal::MockShutdown>;
extern "C" {
-
+void shutdown_report_error(rom_error_t error) {
+ return MockShutdown::Instance().shutdown_report_error(error);
+}
void shutdown_software_escalate(void) {
return MockShutdown::Instance().shutdown_software_escalate();
}
@@ -488,6 +492,55 @@
EXPECT_EQ(shutdown_init(kLcStateRma), kErrorOk);
}
+TEST_F(ShutdownTest, RedactPolicyManufacturing) {
+ // Devices in manufacturing or RMA states should not redact errors regardless
+ // of the redaction level set by OTP.
+ constexpr auto kManufacturingStates = std::array<lifecycle_state_t, 10>{
+ kLcStateRaw, kLcStateTestUnlocked0,
+ kLcStateTestUnlocked1, kLcStateTestUnlocked2,
+ kLcStateTestUnlocked3, kLcStateTestUnlocked4,
+ kLcStateTestUnlocked5, kLcStateTestUnlocked6,
+ kLcStateTestUnlocked7, kLcStateRma};
+ for (const auto state : kManufacturingStates) {
+ EXPECT_ABS_READ32(
+ TOP_EARLGREY_LC_CTRL_BASE_ADDR + LC_CTRL_LC_STATE_REG_OFFSET,
+ static_cast<uint32_t>(state));
+ EXPECT_EQ(shutdown_redact_policy(), kShutdownErrorRedactNone);
+ }
+}
+
+TEST_F(ShutdownTest, RedactPolicyProduction) {
+ // Production states should read redaction level from OTP.
+ constexpr auto kProductionStates = std::array<lifecycle_state_t, 3>{
+ kLcStateProd, kLcStateProdEnd, kLcStateDev};
+ for (const auto state : kProductionStates) {
+ EXPECT_ABS_READ32(
+ TOP_EARLGREY_LC_CTRL_BASE_ADDR + LC_CTRL_LC_STATE_REG_OFFSET,
+ static_cast<uint32_t>(state));
+ EXPECT_ABS_READ32(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR +
+ OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
+ OTP_CTRL_PARAM_ROM_ERROR_REPORTING_OFFSET,
+ static_cast<uint32_t>(kShutdownErrorRedactModule));
+ EXPECT_EQ(shutdown_redact_policy(), kShutdownErrorRedactModule);
+ }
+}
+
+TEST_F(ShutdownTest, RedactPolicyInvalid) {
+ // Invalid states should result in the highest redaction level regardless of
+ // the redaction level set by OTP.
+ constexpr auto kInvalidStates = std::array<lifecycle_state_t, 11>{
+ kLcStateTestLocked0, kLcStateTestLocked1, kLcStateTestLocked2,
+ kLcStateTestLocked3, kLcStateTestLocked4, kLcStateTestLocked5,
+ kLcStateTestLocked6, kLcStateScrap, kLcStatePostTransition,
+ kLcStateEscalate, kLcStateInvalid};
+ for (const auto state : kInvalidStates) {
+ EXPECT_ABS_READ32(
+ TOP_EARLGREY_LC_CTRL_BASE_ADDR + LC_CTRL_LC_STATE_REG_OFFSET,
+ static_cast<uint32_t>(state));
+ EXPECT_EQ(shutdown_redact_policy(), kShutdownErrorRedactAll);
+ }
+}
+
TEST(ShutdownModule, RedactErrors) {
EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactNone), 0);
EXPECT_EQ(shutdown_redact(kErrorOk, kShutdownErrorRedactError), 0);
@@ -506,6 +559,7 @@
TEST_F(ShutdownTest, ShutdownFinalize) {
SetupOtpReads();
+ EXPECT_CALL(shutdown_, shutdown_report_error(kErrorUnknown));
EXPECT_CALL(shutdown_, shutdown_software_escalate());
EXPECT_CALL(shutdown_, shutdown_keymgr_kill());
EXPECT_CALL(shutdown_, shutdown_flash_kill());