blob: 0d46730bae56e628c1f97a2bd582c622e9717811 [file] [log] [blame]
// 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/base/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_lc_ctrl.h"
#include "sw/device/lib/dif/dif_otp_ctrl.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/lc_ctrl_testutils.h"
#include "sw/device/lib/testing/otp_ctrl_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#define LC_TOKEN_SIZE 16
OTTF_DEFINE_TEST_CONFIG();
static dif_lc_ctrl_t lc;
static dif_otp_ctrl_t otp;
static dif_rstmgr_t rstmgr;
/**
* Track LC state transition tokens and destination state.
*
* These variables will be further randomized and overridden by the testbench.
*/
static volatile const uint8_t kDestState;
// LC exit token value for LC state transition.
static volatile const uint8_t kLcExitToken[LC_TOKEN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
// LC exit token value in OTP secret0 partition.
static volatile const uint8_t kOtpExitToken[LC_TOKEN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
// LC unlock token value in OTP secret0 partition.
static volatile const uint8_t kOtpUnlockToken[LC_TOKEN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
// LC rma token value for LC state transition.
static volatile const uint8_t kLcRmaToken[LC_TOKEN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
// LC rma token value in OTP secret2 partition.
static volatile const uint8_t kOtpRmaToken[LC_TOKEN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
static void lock_otp_secret0_partition(void) {
uint64_t otp_unlock_token_0 = 0;
uint64_t otp_unlock_token_1 = 0;
for (int i = 0; i < LC_TOKEN_SIZE; i++) {
if (i < LC_TOKEN_SIZE / 2) {
otp_unlock_token_0 |= (uint64_t)kOtpUnlockToken[i] << (i * 8);
} else {
otp_unlock_token_1 |= (uint64_t)kOtpUnlockToken[i]
<< ((i - LC_TOKEN_SIZE / 2) * 8);
}
}
uint64_t otp_exit_token_0 = 0;
uint64_t otp_exit_token_1 = 0;
for (int i = 0; i < LC_TOKEN_SIZE; i++) {
if (i < LC_TOKEN_SIZE / 2) {
otp_exit_token_0 |= (uint64_t)kOtpExitToken[i] << (i * 8);
} else {
otp_exit_token_1 |= (uint64_t)kOtpExitToken[i]
<< ((i - LC_TOKEN_SIZE / 2) * 8);
}
}
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
/*address=*/0x0,
/*value=*/otp_unlock_token_0));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
/*address=*/0x8,
/*value=*/otp_unlock_token_1));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
/*address=*/0x10,
/*value=*/otp_exit_token_0));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret0,
/*address=*/0x18,
/*value=*/otp_exit_token_1));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_digest(&otp, kDifOtpCtrlPartitionSecret0,
/*digest=*/0));
otp_ctrl_testutils_wait_for_dai(&otp);
}
static void lock_otp_secret2_partition() {
// Write LC RMA tokens to OTP secret2 partition.
uint64_t otp_rma_token_0 = 0;
uint64_t otp_rma_token_1 = 0;
for (int i = 0; i < LC_TOKEN_SIZE; i++) {
if (i < LC_TOKEN_SIZE / 2) {
otp_rma_token_0 |= (uint64_t)kOtpRmaToken[i] << (i * 8);
} else {
otp_rma_token_1 |= (uint64_t)kOtpRmaToken[i]
<< ((i - LC_TOKEN_SIZE / 2) * 8);
}
}
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret2,
/*address=*/0x0,
/*value=*/otp_rma_token_0));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_program64(&otp, kDifOtpCtrlPartitionSecret2,
/*address=*/0x8,
/*value=*/otp_rma_token_1));
otp_ctrl_testutils_wait_for_dai(&otp);
CHECK_DIF_OK(dif_otp_ctrl_dai_digest(&otp, kDifOtpCtrlPartitionSecret2,
/*digest=*/0));
otp_ctrl_testutils_wait_for_dai(&otp);
}
/**
* Walkthrough LC state: RAW -> TestUnlock0 -> Destination -> RMA (if
* applicable).
*
* 1). Preload OTP RAW image file.
* 2). DV sequence drives JTAG interface to write RawUnlockToken and
* transfers LC state to TestUnlock0 state.
* 3). In TestUnlock0 state, SW programs OTP secret0 partition to write
* ExitToken and TestUnlockToken.
* Also programs OTP secret2 partition to write RmaToken.
* 4). DV sequence issues reset to lock OTP secret0 partition.
* 5). SW requests a LC state transfer to ProdEnd state with the correct
* TestLockToken.
* 6). DV sequence issues reset and SW check if LC transfers to the destination
* state.
* 7). If the destination state can be transferred to RMA state, will issue
* another LC state transfer to RMA state.
*
* Note that the destination state is a runtime input from the testbench.
*/
bool test_main(void) {
LOG_INFO("Start LC walkthrough %0d test.", kDestState);
mmio_region_t lc_reg = mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_BASE_ADDR);
CHECK_DIF_OK(dif_lc_ctrl_init(lc_reg, &lc));
mmio_region_t otp_reg =
mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR);
CHECK_DIF_OK(dif_otp_ctrl_init(otp_reg, &otp));
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
LOG_INFO("Read and check LC state and count.");
dif_lc_ctrl_state_t curr_state;
CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
if (curr_state == kDifLcCtrlStateTestUnlocked0) {
lc_ctrl_testutils_check_transition_count(&lc, 1);
bool secret0_locked;
CHECK_DIF_OK(dif_otp_ctrl_is_digest_computed(
&otp, kDifOtpCtrlPartitionSecret0, &secret0_locked));
if (!secret0_locked) {
LOG_INFO("In TestUnlocked0 state. Write and lock OTP secret0 partition.");
lock_otp_secret0_partition();
LOG_INFO("Written and locked OTP secret0 partition!");
CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
wait_for_interrupt();
// Print out LcRmaToken to avoid SW compile error saying kLcRmaToken is
// unused in certain state trasition tests.
LOG_INFO("LC RMA token start with %0h", kLcRmaToken[0]);
// Unreachable
return false;
} else {
LOG_INFO(
"In TestUnlocked0 state. Issue LC state transfer to destination "
"state.");
dif_lc_ctrl_token_t token;
for (int i = 0; i < LC_TOKEN_SIZE; i++) {
if (kDestState != kDifLcCtrlStateRma) {
token.data[i] = kLcExitToken[i];
} else {
// Transition from TestUnlock to Rma state is unconditional and
// requires to write all zero default tokens.
token.data[i] = 0;
}
}
CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
// TODO(lowRISC/opentitan#12775): randomize using external or internal
// clock.
bool use_ext_clock = false;
CHECK_DIF_OK(
dif_lc_ctrl_configure(&lc, kDestState, use_ext_clock, &token),
"LC transition configuration failed!");
CHECK_DIF_OK(dif_lc_ctrl_transition(&lc), "LC transition failed!");
LOG_INFO("Waiting for LC transition done and reboot.");
wait_for_interrupt();
// Unreachable.
return false;
}
} else if (curr_state == kDestState) {
bool secret2_locked;
CHECK_DIF_OK(dif_otp_ctrl_is_digest_computed(
&otp, kDifOtpCtrlPartitionSecret2, &secret2_locked));
if (!secret2_locked) {
LOG_INFO(
"In destination state. Check LC count and lock secret2 partition.");
CHECK(curr_state == kDestState);
lc_ctrl_testutils_check_transition_count(&lc, 2);
lock_otp_secret2_partition();
LOG_INFO("Written and locked OTP secret2 partition in next power cycle!");
CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
wait_for_interrupt();
return false;
} else {
if (kDestState == kDifLcCtrlStateRma ||
kDestState == kDifLcCtrlStateProdEnd) {
lc_ctrl_testutils_check_transition_count(&lc, 2);
LOG_INFO("LC transition done!");
return true;
} else {
// Check LC counts and transit to RMA state.
LOG_INFO(
"In destination state. Check LC count and transfer to RMA "
"state.");
lc_ctrl_testutils_check_transition_count(&lc, 2);
// Issue a LC state transfer to destination state.
dif_lc_ctrl_token_t token;
for (int i = 0; i < LC_TOKEN_SIZE; i++) {
token.data[i] = kLcRmaToken[i];
}
CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
// TODO: randomize using external or internal clock.
bool use_ext_clock = true;
CHECK_DIF_OK(dif_lc_ctrl_configure(&lc, kDifLcCtrlStateRma,
use_ext_clock, &token),
"LC transition configuration failed!");
CHECK_DIF_OK(dif_lc_ctrl_transition(&lc), "LC transition failed!");
LOG_INFO("Waiting for LC RMA transition done and reboot.");
wait_for_interrupt();
// Unreachable.
return false;
}
}
} else {
LOG_INFO("In RMA state. Check LC count and exit.");
CHECK(curr_state == kDifLcCtrlStateRma);
lc_ctrl_testutils_check_transition_count(&lc, 3);
return true;
}
}