| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/lib/dif/dif_otp_ctrl.h" |
| |
| #include <cstring> |
| #include <limits> |
| #include <ostream> |
| |
| #include "gtest/gtest.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/base/mock_mmio.h" |
| #include "sw/device/lib/dif/dif_test_base.h" |
| |
| #include "otp_ctrl_regs.h" // Generated. |
| |
| namespace dif_otp_ctrl_unittest { |
| namespace { |
| using ::mock_mmio::LeInt; |
| using ::mock_mmio::MmioTest; |
| using ::mock_mmio::MockDevice; |
| using ::testing::Each; |
| using ::testing::ElementsAre; |
| |
| class OtpTest : public testing::Test, public MmioTest { |
| protected: |
| dif_otp_ctrl_t otp_ = {.base_addr = dev().region()}; |
| }; |
| |
| class ConfigTest : public OtpTest {}; |
| |
| TEST_F(ConfigTest, Basic) { |
| dif_otp_ctrl_config_t config = { |
| .check_timeout = 100'000, |
| .integrity_period_mask = 0x3'ffff, |
| .consistency_period_mask = 0x3ff'ffff, |
| }; |
| |
| EXPECT_READ32(OTP_CTRL_CHECK_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, true}}); |
| |
| EXPECT_WRITE32(OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET, config.check_timeout); |
| EXPECT_WRITE32(OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET, |
| config.integrity_period_mask); |
| EXPECT_WRITE32(OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET, |
| config.consistency_period_mask); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_configure(&otp_, config)); |
| } |
| |
| TEST_F(ConfigTest, Locked) { |
| EXPECT_READ32(OTP_CTRL_CHECK_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, false}}); |
| |
| EXPECT_EQ(dif_otp_ctrl_configure(&otp_, {}), kDifLocked); |
| } |
| |
| TEST_F(ConfigTest, IsConfigLocked) { |
| bool flag; |
| |
| EXPECT_READ32(OTP_CTRL_CHECK_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, true}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_config_is_locked(&otp_, &flag)); |
| EXPECT_FALSE(flag); |
| |
| EXPECT_READ32(OTP_CTRL_CHECK_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, false}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_config_is_locked(&otp_, &flag)); |
| EXPECT_TRUE(flag); |
| } |
| |
| TEST_F(ConfigTest, LockConfig) { |
| EXPECT_WRITE32(OTP_CTRL_CHECK_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, false}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_lock_config(&otp_)); |
| } |
| |
| TEST_F(ConfigTest, NullArgs) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_configure(nullptr, {})); |
| |
| bool flag; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_config_is_locked(nullptr, &flag)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_config_is_locked(&otp_, nullptr)); |
| |
| EXPECT_DIF_BADARG(dif_otp_ctrl_lock_config(nullptr)); |
| } |
| |
| class CheckTest : public OtpTest {}; |
| |
| TEST_F(CheckTest, Integrity) { |
| EXPECT_READ32( |
| OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_INTEGRITY_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_check_integrity(&otp_)); |
| } |
| |
| TEST_F(CheckTest, Consistency) { |
| EXPECT_READ32( |
| OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_CONSISTENCY_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_check_consistency(&otp_)); |
| } |
| |
| TEST_F(CheckTest, LockTrigger) { |
| EXPECT_WRITE32( |
| OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, false}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_lock_check_trigger(&otp_)); |
| } |
| |
| TEST_F(CheckTest, Locked) { |
| EXPECT_READ32( |
| OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, false}}); |
| EXPECT_EQ(dif_otp_ctrl_check_integrity(&otp_), kDifLocked); |
| |
| EXPECT_READ32( |
| OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, false}}); |
| EXPECT_EQ(dif_otp_ctrl_check_consistency(&otp_), kDifLocked); |
| } |
| |
| TEST_F(CheckTest, NullArgs) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_check_integrity(nullptr)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_check_consistency(nullptr)); |
| } |
| |
| class ReadLockTest : public OtpTest {}; |
| |
| TEST_F(ReadLockTest, IsLocked) { |
| bool flag; |
| |
| EXPECT_READ32( |
| OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT, true}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionCreatorSwCfg, &flag)); |
| EXPECT_FALSE(flag); |
| |
| EXPECT_READ32( |
| OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT, |
| false}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionCreatorSwCfg, &flag)); |
| EXPECT_TRUE(flag); |
| |
| EXPECT_READ32( |
| OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT, true}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionOwnerSwCfg, &flag)); |
| EXPECT_FALSE(flag); |
| |
| EXPECT_READ32( |
| OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT, |
| false}}); |
| EXPECT_DIF_OK(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionOwnerSwCfg, &flag)); |
| EXPECT_TRUE(flag); |
| } |
| |
| TEST_F(ReadLockTest, Lock) { |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, 1); |
| EXPECT_WRITE32( |
| OTP_CTRL_VENDOR_TEST_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_VENDOR_TEST_READ_LOCK_VENDOR_TEST_READ_LOCK_BIT, false}}); |
| EXPECT_DIF_OK( |
| dif_otp_ctrl_lock_reading(&otp_, kDifOtpCtrlPartitionVendorTest)); |
| |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, 1); |
| EXPECT_WRITE32( |
| OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT, |
| false}}); |
| EXPECT_DIF_OK( |
| dif_otp_ctrl_lock_reading(&otp_, kDifOtpCtrlPartitionCreatorSwCfg)); |
| |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, 1); |
| EXPECT_WRITE32( |
| OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET, |
| {{OTP_CTRL_OWNER_SW_CFG_READ_LOCK_OWNER_SW_CFG_READ_LOCK_BIT, false}}); |
| EXPECT_DIF_OK( |
| dif_otp_ctrl_lock_reading(&otp_, kDifOtpCtrlPartitionOwnerSwCfg)); |
| } |
| |
| TEST_F(ReadLockTest, HwPartition) { |
| bool flag; |
| EXPECT_DIF_BADARG( |
| dif_otp_ctrl_lock_reading(&otp_, kDifOtpCtrlPartitionHwCfg)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionSecret0, &flag)); |
| } |
| |
| TEST_F(ReadLockTest, NullArgs) { |
| bool flag; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_reading_is_locked( |
| nullptr, kDifOtpCtrlPartitionOwnerSwCfg, &flag)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_reading_is_locked( |
| &otp_, kDifOtpCtrlPartitionOwnerSwCfg, nullptr)); |
| |
| EXPECT_DIF_BADARG( |
| dif_otp_ctrl_lock_reading(nullptr, kDifOtpCtrlPartitionOwnerSwCfg)); |
| } |
| |
| class StatusTest : public OtpTest {}; |
| |
| TEST_F(StatusTest, Idle) { |
| dif_otp_ctrl_status_t status; |
| |
| EXPECT_READ32(OTP_CTRL_STATUS_REG_OFFSET, |
| {{OTP_CTRL_STATUS_DAI_IDLE_BIT, true}}); |
| EXPECT_READ32(OTP_CTRL_ERR_CODE_REG_OFFSET, 0); |
| EXPECT_DIF_OK(dif_otp_ctrl_get_status(&otp_, &status)); |
| |
| EXPECT_EQ(status.codes, 1 << kDifOtpCtrlStatusCodeDaiIdle); |
| EXPECT_THAT(status.causes, Each(kDifOtpCtrlErrorOk)); |
| } |
| |
| TEST_F(StatusTest, Errors) { |
| dif_otp_ctrl_status_t status; |
| |
| EXPECT_READ32(OTP_CTRL_STATUS_REG_OFFSET, |
| { |
| {OTP_CTRL_STATUS_DAI_IDLE_BIT, true}, |
| {OTP_CTRL_STATUS_HW_CFG_ERROR_BIT, true}, |
| {OTP_CTRL_STATUS_LCI_ERROR_BIT, true}, |
| }); |
| |
| EXPECT_READ32(OTP_CTRL_ERR_CODE_REG_OFFSET, |
| {{OTP_CTRL_ERR_CODE_ERR_CODE_3_OFFSET, |
| OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ECC_CORR_ERROR}, |
| {OTP_CTRL_ERR_CODE_ERR_CODE_9_OFFSET, |
| OTP_CTRL_ERR_CODE_ERR_CODE_0_VALUE_MACRO_ERROR}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_get_status(&otp_, &status)); |
| EXPECT_EQ(status.codes, (1 << kDifOtpCtrlStatusCodeDaiIdle) | |
| (1 << kDifOtpCtrlStatusCodeHwCfgError) | |
| (1 << kDifOtpCtrlStatusCodeLciError)); |
| EXPECT_EQ(status.causes[kDifOtpCtrlStatusCodeHwCfgError], |
| kDifOtpCtrlErrorMacroRecoverableRead); |
| EXPECT_EQ(status.causes[kDifOtpCtrlStatusCodeLciError], |
| kDifOtpCtrlErrorMacroUnspecified); |
| } |
| |
| TEST_F(StatusTest, NullArgs) { |
| dif_otp_ctrl_status_t status; |
| |
| EXPECT_DIF_BADARG(dif_otp_ctrl_get_status(nullptr, &status)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_get_status(&otp_, nullptr)); |
| } |
| |
| class DaiReadTest : public OtpTest {}; |
| |
| TEST_F(DaiReadTest, Read32) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_MANUF_STATE_OFFSET); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x20)); |
| |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET, 0x12345678); |
| |
| uint32_t val; |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_read32_end(&otp_, &val)); |
| EXPECT_EQ(val, 0x12345678); |
| } |
| |
| TEST_F(DaiReadTest, Read64) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_SECRET2_OFFSET + 0x8); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionSecret2, |
| /*address=*/0x8)); |
| |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET, 0x12345678); |
| EXPECT_READ32(OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET, 0x90abcdef); |
| |
| uint64_t val; |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_read64_end(&otp_, &val)); |
| EXPECT_EQ(val, 0x1234567890abcdef); |
| } |
| |
| TEST_F(DaiReadTest, Unaligned) { |
| EXPECT_EQ(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0b01), |
| kDifUnaligned); |
| EXPECT_EQ(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionSecret2, |
| /*address=*/0b100), |
| kDifUnaligned); |
| } |
| |
| TEST_F(DaiReadTest, OutOfRange) { |
| EXPECT_EQ(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x100), |
| kDifOutOfRange); |
| } |
| |
| TEST_F(DaiReadTest, Busy) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| EXPECT_EQ(dif_otp_ctrl_dai_read_start(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x0), |
| kDifUnavailable); |
| |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| uint32_t val32; |
| EXPECT_EQ(dif_otp_ctrl_dai_read32_end(&otp_, &val32), kDifUnavailable); |
| |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| uint64_t val64; |
| EXPECT_EQ(dif_otp_ctrl_dai_read64_end(&otp_, &val64), kDifUnavailable); |
| } |
| |
| TEST_F(DaiReadTest, NullArgs) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_read_start(nullptr, |
| kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x0)); |
| |
| uint32_t val32; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_read32_end(nullptr, &val32)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_read32_end(&otp_, nullptr)); |
| |
| uint64_t val64; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_read64_end(nullptr, &val64)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_read64_end(&otp_, nullptr)); |
| } |
| |
| class DaiProgramTest : public OtpTest {}; |
| |
| TEST_F(DaiProgramTest, Program32) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_MANUF_STATE_OFFSET); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, 0x12345678); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_program32(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x20, |
| /*value=*/0x12345678)); |
| } |
| |
| TEST_F(DaiProgramTest, Program64) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_SECRET2_OFFSET + 0x8); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, 0x90abcdef); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET, 0x12345678); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_program64(&otp_, kDifOtpCtrlPartitionSecret2, |
| /*address=*/0x8, |
| /*value=*/0x1234567890abcdef)); |
| } |
| |
| TEST_F(DaiProgramTest, BadPartition) { |
| EXPECT_EQ(dif_otp_ctrl_dai_program32(&otp_, kDifOtpCtrlPartitionSecret1, |
| /*address=*/0x0, /*value=*/42), |
| kDifError); |
| EXPECT_EQ(dif_otp_ctrl_dai_program64(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x0, /*value=*/42), |
| kDifError); |
| |
| // LC is never writeable. |
| EXPECT_EQ(dif_otp_ctrl_dai_program32(&otp_, kDifOtpCtrlPartitionLifeCycle, |
| /*address=*/0x0, /*value=*/42), |
| kDifError); |
| } |
| |
| TEST_F(DaiProgramTest, Unaligned) { |
| EXPECT_EQ(dif_otp_ctrl_dai_program32(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0b01, /*value=*/42), |
| kDifUnaligned); |
| EXPECT_EQ(dif_otp_ctrl_dai_program64(&otp_, kDifOtpCtrlPartitionSecret2, |
| /*address=*/0b100, /*value=*/42), |
| kDifUnaligned); |
| } |
| |
| TEST_F(DaiProgramTest, OutOfRange) { |
| // Check that we can't write a digest directly. |
| EXPECT_EQ(dif_otp_ctrl_dai_program32( |
| &otp_, kDifOtpCtrlPartitionCreatorSwCfg, |
| /*address=*/OTP_CTRL_PARAM_CREATOR_SW_CFG_DIGEST_OFFSET, |
| /*value=*/42), |
| kDifOutOfRange); |
| |
| // Same digest check for 64-bit. |
| EXPECT_EQ(dif_otp_ctrl_dai_program64( |
| &otp_, kDifOtpCtrlPartitionSecret2, |
| /*address=*/OTP_CTRL_PARAM_SECRET2_DIGEST_OFFSET, /*value=*/42), |
| kDifOutOfRange); |
| } |
| |
| TEST_F(DaiProgramTest, Busy) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| EXPECT_EQ(dif_otp_ctrl_dai_program32(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x0, /*value=*/42), |
| kDifUnavailable); |
| |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| EXPECT_EQ(dif_otp_ctrl_dai_program64(&otp_, kDifOtpCtrlPartitionSecret0, |
| /*address=*/0x0, /*value=*/42), |
| kDifUnavailable); |
| } |
| |
| TEST_F(DaiProgramTest, NullArgs) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_program32(nullptr, |
| kDifOtpCtrlPartitionHwCfg, |
| /*address=*/0x0, /*value=*/42)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_program64(nullptr, |
| kDifOtpCtrlPartitionSecret0, |
| /*address=*/0x0, /*value=*/42)); |
| } |
| |
| class DaiDigestTest : public OtpTest {}; |
| |
| TEST_F(DaiDigestTest, DigestSw) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_CREATOR_SW_CFG_DIGEST_OFFSET); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET, 0x00abcdef); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET, 0xabcdef00); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true}}); |
| |
| EXPECT_DIF_OK(dif_otp_ctrl_dai_digest(&otp_, kDifOtpCtrlPartitionCreatorSwCfg, |
| /*digest=*/0xabcdef0000abcdef)); |
| } |
| |
| TEST_F(DaiDigestTest, DigestHw) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, true}}); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET, |
| OTP_CTRL_PARAM_DEVICE_ID_OFFSET); |
| EXPECT_WRITE32(OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST_BIT, true}}); |
| |
| EXPECT_DIF_OK( |
| dif_otp_ctrl_dai_digest(&otp_, kDifOtpCtrlPartitionHwCfg, /*digest=*/0)); |
| } |
| |
| TEST_F(DaiDigestTest, BadPartition) { |
| EXPECT_EQ(dif_otp_ctrl_dai_digest(&otp_, kDifOtpCtrlPartitionLifeCycle, |
| /*digest=*/0), |
| kDifError); |
| } |
| |
| TEST_F(DaiDigestTest, Busy) { |
| EXPECT_READ32( |
| OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET, |
| {{OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false}}); |
| |
| EXPECT_EQ( |
| dif_otp_ctrl_dai_digest(&otp_, kDifOtpCtrlPartitionHwCfg, /*digest=*/0), |
| kDifUnavailable); |
| } |
| |
| TEST_F(DaiDigestTest, BadDigest) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_digest(&otp_, kDifOtpCtrlPartitionHwCfg, |
| /*digest=*/0xabcdef0000abcdef)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_digest(&otp_, |
| kDifOtpCtrlPartitionCreatorSwCfg, |
| /*digest=*/0)); |
| } |
| |
| TEST_F(DaiDigestTest, NullArgs) { |
| EXPECT_DIF_BADARG(dif_otp_ctrl_dai_digest(nullptr, |
| kDifOtpCtrlPartitionCreatorSwCfg, |
| /*digest=*/0xabcdef0000abcdef)); |
| } |
| |
| class IsDigestComputed : public OtpTest {}; |
| |
| TEST_F(IsDigestComputed, NullArgs) { |
| bool is_computed; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_is_digest_computed( |
| nullptr, kDifOtpCtrlPartitionSecret2, &is_computed)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_is_digest_computed( |
| &otp_, kDifOtpCtrlPartitionSecret2, nullptr)); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_is_digest_computed( |
| nullptr, kDifOtpCtrlPartitionSecret2, nullptr)); |
| } |
| |
| TEST_F(IsDigestComputed, BadPartition) { |
| bool is_computed; |
| EXPECT_DIF_BADARG(dif_otp_ctrl_is_digest_computed( |
| &otp_, kDifOtpCtrlPartitionLifeCycle, &is_computed)); |
| } |
| |
| TEST_F(IsDigestComputed, Success) { |
| bool is_computed; |
| |
| EXPECT_READ32(OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET, 0x98abcdef); |
| EXPECT_READ32(OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET, 0xabcdef01); |
| EXPECT_DIF_OK(dif_otp_ctrl_is_digest_computed( |
| &otp_, kDifOtpCtrlPartitionSecret2, &is_computed)); |
| EXPECT_TRUE(is_computed); |
| |
| EXPECT_READ32(OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET, 0); |
| EXPECT_READ32(OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET, 0); |
| EXPECT_DIF_OK(dif_otp_ctrl_is_digest_computed( |
| &otp_, kDifOtpCtrlPartitionSecret2, &is_computed)); |
| EXPECT_FALSE(is_computed); |
| } |
| |
| struct DigestParams { |
| dif_otp_ctrl_partition_t partition; |
| ptrdiff_t reg0, reg1; |
| }; |
| |
| class GetDigest : public OtpTest, |
| public testing::WithParamInterface<DigestParams> {}; |
| |
| TEST_P(GetDigest, GetDigest) { |
| if (GetParam().partition == kDifOtpCtrlPartitionLifeCycle) { |
| uint64_t digest; |
| EXPECT_DIF_BADARG( |
| dif_otp_ctrl_get_digest(&otp_, GetParam().partition, &digest)); |
| return; |
| } |
| |
| EXPECT_READ32(GetParam().reg1, 0xabcdef99); |
| EXPECT_READ32(GetParam().reg0, 0x99abcdef); |
| |
| uint64_t digest; |
| EXPECT_DIF_OK(dif_otp_ctrl_get_digest(&otp_, GetParam().partition, &digest)); |
| EXPECT_EQ(digest, 0xabcdef9999abcdef); |
| } |
| |
| TEST_P(GetDigest, BadDigest) { |
| if (GetParam().partition == kDifOtpCtrlPartitionLifeCycle) { |
| return; |
| } |
| |
| EXPECT_READ32(GetParam().reg1, 0x0); |
| EXPECT_READ32(GetParam().reg0, 0x0); |
| |
| uint64_t digest; |
| EXPECT_EQ(dif_otp_ctrl_get_digest(&otp_, GetParam().partition, &digest), |
| kDifError); |
| } |
| |
| TEST_P(GetDigest, NullArgs) { |
| uint64_t digest; |
| EXPECT_DIF_BADARG( |
| dif_otp_ctrl_get_digest(nullptr, GetParam().partition, &digest)); |
| EXPECT_DIF_BADARG( |
| dif_otp_ctrl_get_digest(&otp_, GetParam().partition, nullptr)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllDigests, GetDigest, |
| testing::Values( |
| DigestParams{ |
| kDifOtpCtrlPartitionCreatorSwCfg, |
| OTP_CTRL_CREATOR_SW_CFG_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_CREATOR_SW_CFG_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionOwnerSwCfg, |
| OTP_CTRL_OWNER_SW_CFG_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_OWNER_SW_CFG_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionHwCfg, |
| OTP_CTRL_HW_CFG_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_HW_CFG_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionSecret0, |
| OTP_CTRL_SECRET0_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_SECRET0_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionSecret1, |
| OTP_CTRL_SECRET1_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_SECRET1_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionSecret2, |
| OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET, |
| OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET, |
| }, |
| DigestParams{ |
| kDifOtpCtrlPartitionLifeCycle, |
| 0, |
| 0, |
| })); |
| |
| class BlockingIoTest : public OtpTest { |
| protected: |
| static constexpr size_t kWords = 4; |
| }; |
| |
| TEST_F(BlockingIoTest, Read) { |
| for (size_t i = 0; i < kWords; ++i) { |
| auto offset = |
| OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET + 0x10 + i * sizeof(uint32_t); |
| EXPECT_READ32(OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + offset, i + 1); |
| } |
| |
| std::vector<uint32_t> buf(kWords); |
| EXPECT_DIF_OK(dif_otp_ctrl_read_blocking( |
| &otp_, kDifOtpCtrlPartitionOwnerSwCfg, 0x10, buf.data(), buf.size())); |
| EXPECT_THAT(buf, ElementsAre(1, 2, 3, 4)); |
| } |
| |
| TEST_F(BlockingIoTest, BadPartition) { |
| std::vector<uint32_t> buf(kWords); |
| EXPECT_EQ(dif_otp_ctrl_read_blocking(&otp_, kDifOtpCtrlPartitionHwCfg, 0x10, |
| buf.data(), buf.size()), |
| kDifError); |
| } |
| |
| TEST_F(BlockingIoTest, Unaligned) { |
| std::vector<uint32_t> buf(kWords); |
| EXPECT_EQ(dif_otp_ctrl_read_blocking(&otp_, kDifOtpCtrlPartitionOwnerSwCfg, |
| 0x11, buf.data(), buf.size()), |
| kDifUnaligned); |
| } |
| |
| TEST_F(BlockingIoTest, OutOfRange) { |
| std::vector<uint32_t> buf(0x2f0); |
| EXPECT_EQ(dif_otp_ctrl_read_blocking(&otp_, kDifOtpCtrlPartitionOwnerSwCfg, |
| 0x300, buf.data(), buf.size()), |
| kDifOutOfRange); |
| EXPECT_EQ(dif_otp_ctrl_read_blocking(&otp_, kDifOtpCtrlPartitionOwnerSwCfg, |
| 0x10, buf.data(), 0x330), |
| kDifOutOfRange); |
| } |
| |
| TEST_F(BlockingIoTest, NullArgs) { |
| std::vector<uint32_t> buf(kWords); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_read_blocking( |
| nullptr, kDifOtpCtrlPartitionOwnerSwCfg, 0x10, buf.data(), buf.size())); |
| EXPECT_DIF_BADARG(dif_otp_ctrl_read_blocking( |
| &otp_, kDifOtpCtrlPartitionOwnerSwCfg, 0x10, nullptr, buf.size())); |
| } |
| |
| } // namespace |
| } // namespace dif_otp_ctrl_unittest |