| // 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_lc_ctrl.h" |
| |
| #include <cstring> |
| #include <limits> |
| #include <utility> |
| |
| #include "gtest/gtest.h" |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/base/multibits.h" |
| #include "sw/device/lib/base/testing/mock_mmio.h" |
| |
| #include "lc_ctrl_regs.h" // Generated. |
| |
| namespace dif_lc_ctrl_unittest { |
| namespace { |
| using ::mock_mmio::LeInt; |
| using ::mock_mmio::MmioTest; |
| using ::mock_mmio::MockDevice; |
| using ::testing::Test; |
| |
| class LcCtrlTest : public Test, public MmioTest { |
| protected: |
| dif_lc_ctrl_t lc_ = {.base_addr = dev().region()}; |
| }; |
| |
| class StateTest : public LcCtrlTest {}; |
| |
| TEST_F(StateTest, GetState) { |
| std::vector<std::pair<uint32_t, dif_lc_ctrl_state_t>> states = { |
| {LC_CTRL_LC_STATE_STATE_VALUE_RAW, kDifLcCtrlStateRaw}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED0, |
| kDifLcCtrlStateTestUnlocked0}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED0, kDifLcCtrlStateTestLocked0}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED1, |
| kDifLcCtrlStateTestUnlocked1}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED1, kDifLcCtrlStateTestLocked1}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED2, |
| kDifLcCtrlStateTestUnlocked2}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED2, kDifLcCtrlStateTestLocked2}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED3, |
| kDifLcCtrlStateTestUnlocked3}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED3, kDifLcCtrlStateTestLocked3}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED4, |
| kDifLcCtrlStateTestUnlocked4}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED4, kDifLcCtrlStateTestLocked4}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED5, |
| kDifLcCtrlStateTestUnlocked5}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED5, kDifLcCtrlStateTestLocked5}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED6, |
| kDifLcCtrlStateTestUnlocked6}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_LOCKED6, kDifLcCtrlStateTestLocked6}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_TEST_UNLOCKED7, |
| kDifLcCtrlStateTestUnlocked7}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_DEV, kDifLcCtrlStateDev}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_PROD, kDifLcCtrlStateProd}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_PROD_END, kDifLcCtrlStateProdEnd}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_RMA, kDifLcCtrlStateRma}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_SCRAP, kDifLcCtrlStateScrap}, |
| |
| {LC_CTRL_LC_STATE_STATE_VALUE_POST_TRANSITION, |
| kDifLcCtrlStatePostTransition}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_ESCALATE, kDifLcCtrlStateEscalate}, |
| {LC_CTRL_LC_STATE_STATE_VALUE_INVALID, kDifLcCtrlStateInvalid}, |
| }; |
| |
| for (auto pair : states) { |
| dif_lc_ctrl_state_t state; |
| |
| EXPECT_READ32(LC_CTRL_LC_STATE_REG_OFFSET, |
| {{LC_CTRL_LC_STATE_STATE_OFFSET, pair.first}}); |
| EXPECT_EQ(dif_lc_ctrl_get_state(&lc_, &state), kDifOk); |
| EXPECT_EQ(state, pair.second); |
| } |
| } |
| |
| TEST_F(StateTest, GetAttempts) { |
| uint8_t attempts; |
| |
| EXPECT_READ32(LC_CTRL_LC_TRANSITION_CNT_REG_OFFSET, |
| {{LC_CTRL_LC_TRANSITION_CNT_CNT_OFFSET, 13}}); |
| EXPECT_EQ(dif_lc_ctrl_get_attempts(&lc_, &attempts), kDifOk); |
| EXPECT_EQ(attempts, 13); |
| |
| EXPECT_READ32(LC_CTRL_LC_TRANSITION_CNT_REG_OFFSET, |
| {{LC_CTRL_LC_TRANSITION_CNT_CNT_OFFSET, |
| LC_CTRL_LC_TRANSITION_CNT_CNT_MASK}}); |
| EXPECT_EQ(dif_lc_ctrl_get_attempts(&lc_, &attempts), kDifError); |
| } |
| |
| TEST_F(StateTest, GetStatus) { |
| dif_lc_ctrl_status_t status; |
| |
| EXPECT_READ32(LC_CTRL_STATUS_REG_OFFSET, { |
| {LC_CTRL_STATUS_READY_BIT, true}, |
| }); |
| EXPECT_EQ(dif_lc_ctrl_get_status(&lc_, &status), kDifOk); |
| EXPECT_EQ(status, bitfield_bit32_write(0, kDifLcCtrlStatusCodeReady, true)); |
| |
| EXPECT_READ32(LC_CTRL_STATUS_REG_OFFSET, |
| { |
| {LC_CTRL_STATUS_TRANSITION_ERROR_BIT, true}, |
| {LC_CTRL_STATUS_TOKEN_ERROR_BIT, true}, |
| {LC_CTRL_STATUS_OTP_ERROR_BIT, true}, |
| }); |
| EXPECT_EQ(dif_lc_ctrl_get_status(&lc_, &status), kDifOk); |
| EXPECT_TRUE( |
| bitfield_bit32_read(status, kDifLcCtrlStatusCodeInvalidTransition)); |
| EXPECT_TRUE(bitfield_bit32_read(status, kDifLcCtrlStatusCodeBadToken)); |
| EXPECT_TRUE(bitfield_bit32_read(status, kDifLcCtrlStatusCodeOtpError)); |
| } |
| |
| TEST_F(StateTest, GetIdState) { |
| std::vector<std::pair<uint32_t, dif_lc_ctrl_id_state_t>> states = { |
| {LC_CTRL_LC_ID_STATE_STATE_VALUE_BLANK, kDifLcCtrlIdStateBlank}, |
| {LC_CTRL_LC_ID_STATE_STATE_VALUE_PERSONALIZED, |
| kDifLcCtrlIdStatePersonalized}, |
| {LC_CTRL_LC_ID_STATE_STATE_VALUE_INVALID, kDifLcCtrlIdStateInvalid}, |
| }; |
| |
| for (auto pair : states) { |
| dif_lc_ctrl_id_state_t state; |
| |
| EXPECT_READ32(LC_CTRL_LC_ID_STATE_REG_OFFSET, |
| {{LC_CTRL_LC_ID_STATE_STATE_OFFSET, pair.first}}); |
| EXPECT_EQ(dif_lc_ctrl_get_id_state(&lc_, &state), kDifOk); |
| EXPECT_EQ(state, pair.second); |
| } |
| } |
| |
| TEST_F(StateTest, NullArgs) { |
| dif_lc_ctrl_state_t state; |
| EXPECT_EQ(dif_lc_ctrl_get_state(nullptr, &state), kDifBadArg); |
| EXPECT_EQ(dif_lc_ctrl_get_state(&lc_, nullptr), kDifBadArg); |
| |
| uint8_t attempts; |
| EXPECT_EQ(dif_lc_ctrl_get_attempts(nullptr, &attempts), kDifBadArg); |
| EXPECT_EQ(dif_lc_ctrl_get_attempts(&lc_, nullptr), kDifBadArg); |
| |
| dif_lc_ctrl_status_t status; |
| EXPECT_EQ(dif_lc_ctrl_get_status(nullptr, &status), kDifBadArg); |
| EXPECT_EQ(dif_lc_ctrl_get_status(&lc_, nullptr), kDifBadArg); |
| |
| dif_lc_ctrl_id_state_t id_state; |
| EXPECT_EQ(dif_lc_ctrl_get_id_state(nullptr, &id_state), kDifBadArg); |
| EXPECT_EQ(dif_lc_ctrl_get_id_state(&lc_, nullptr), kDifBadArg); |
| } |
| |
| class MutexTest : public LcCtrlTest {}; |
| |
| TEST_F(MutexTest, Acquire) { |
| EXPECT_WRITE32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8True); |
| EXPECT_READ32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8True); |
| EXPECT_EQ(dif_lc_ctrl_mutex_try_acquire(&lc_), kDifOk); |
| |
| EXPECT_WRITE32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8True); |
| EXPECT_READ32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8False); |
| EXPECT_EQ(dif_lc_ctrl_mutex_try_acquire(&lc_), kDifUnavailable); |
| } |
| |
| TEST_F(MutexTest, Release) { |
| EXPECT_READ32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8True); |
| EXPECT_WRITE32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8False); |
| EXPECT_EQ(dif_lc_ctrl_mutex_release(&lc_), kDifOk); |
| |
| EXPECT_READ32(LC_CTRL_CLAIM_TRANSITION_IF_REG_OFFSET, kMultiBitBool8False); |
| EXPECT_EQ(dif_lc_ctrl_mutex_release(&lc_), kDifError); |
| } |
| |
| TEST_F(MutexTest, NullArgs) { |
| EXPECT_EQ(dif_lc_ctrl_mutex_try_acquire(nullptr), kDifBadArg); |
| EXPECT_EQ(dif_lc_ctrl_mutex_release(nullptr), kDifBadArg); |
| } |
| |
| class TransitionTest : public LcCtrlTest {}; |
| |
| TEST_F(TransitionTest, NoToken) { |
| EXPECT_READ32(LC_CTRL_TRANSITION_REGWEN_REG_OFFSET, true); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TARGET_REG_OFFSET, |
| LC_CTRL_TRANSITION_TARGET_STATE_VALUE_PROD); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CTRL_REG_OFFSET, 0x0); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CMD_REG_OFFSET, true); |
| EXPECT_EQ(dif_lc_ctrl_transition(&lc_, kDifLcCtrlStateProd, nullptr, nullptr), |
| kDifOk); |
| } |
| |
| TEST_F(TransitionTest, WithToken) { |
| dif_lc_ctrl_token_t token = {"this is a token"}; |
| dif_lc_ctrl_settings_t settings = {kDifLcCtrlExternalClockEn}; |
| |
| EXPECT_READ32(LC_CTRL_TRANSITION_REGWEN_REG_OFFSET, true); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TARGET_REG_OFFSET, |
| LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED2); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CTRL_REG_OFFSET, 0x1); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_0_REG_OFFSET, LeInt("this")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_1_REG_OFFSET, LeInt(" is ")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_2_REG_OFFSET, LeInt("a to")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_3_REG_OFFSET, LeInt("ken\0")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CMD_REG_OFFSET, true); |
| EXPECT_EQ(dif_lc_ctrl_transition(&lc_, kDifLcCtrlStateTestUnlocked2, &token, |
| &settings), |
| kDifOk); |
| |
| EXPECT_READ32(LC_CTRL_TRANSITION_REGWEN_REG_OFFSET, true); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TARGET_REG_OFFSET, |
| LC_CTRL_TRANSITION_TARGET_STATE_VALUE_TEST_UNLOCKED6); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CTRL_REG_OFFSET, 0x1); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_0_REG_OFFSET, LeInt("this")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_1_REG_OFFSET, LeInt(" is ")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_2_REG_OFFSET, LeInt("a to")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_TOKEN_3_REG_OFFSET, LeInt("ken\0")); |
| EXPECT_WRITE32(LC_CTRL_TRANSITION_CMD_REG_OFFSET, true); |
| EXPECT_EQ(dif_lc_ctrl_transition(&lc_, kDifLcCtrlStateTestUnlocked6, &token, |
| &settings), |
| kDifOk); |
| } |
| |
| TEST_F(TransitionTest, Locked) { |
| EXPECT_READ32(LC_CTRL_TRANSITION_REGWEN_REG_OFFSET, false); |
| EXPECT_EQ(dif_lc_ctrl_transition(&lc_, kDifLcCtrlStateProd, nullptr, nullptr), |
| kDifUnavailable); |
| } |
| |
| TEST_F(TransitionTest, NullArgs) { |
| dif_lc_ctrl_token_t token = {"this is a token"}; |
| EXPECT_EQ( |
| dif_lc_ctrl_transition(nullptr, kDifLcCtrlStateProd, &token, nullptr), |
| kDifBadArg); |
| } |
| |
| class OtpVendorTestRegTest : public LcCtrlTest {}; |
| |
| TEST_F(OtpVendorTestRegTest, Read) { |
| uint32_t settings_read = 0; |
| EXPECT_READ32(LC_CTRL_OTP_VENDOR_TEST_CTRL_REG_OFFSET, 0x5A); |
| EXPECT_EQ(dif_lc_ctrl_get_otp_vendor_test_reg(&lc_, &settings_read), kDifOk); |
| EXPECT_EQ(settings_read, 0x5A); |
| } |
| |
| TEST_F(OtpVendorTestRegTest, Write) { |
| EXPECT_READ32(LC_CTRL_TRANSITION_REGWEN_REG_OFFSET, true); |
| EXPECT_WRITE32(LC_CTRL_OTP_VENDOR_TEST_CTRL_REG_OFFSET, 0xA5); |
| EXPECT_EQ(dif_lc_ctrl_set_otp_vendor_test_reg(&lc_, 0xA5), kDifOk); |
| } |
| |
| } // namespace |
| } // namespace dif_lc_ctrl_unittest |