blob: 4fec0e938ea12c5719fe7b0e8b036ecfd4cb2e50 [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/arch/device.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_flash_ctrl.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/print.h"
#include "sw/device/lib/testing/flash_ctrl_testutils.h"
#include "sw/device/lib/testing/pinmux_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/status.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
static dif_uart_t uart0;
static dif_flash_ctrl_state_t flash_state;
enum {
kFlashInfoPageIdCreatorSecret = 1,
kFlashInfoPageIdOwnerSecret = 2,
kFlashInfoPageIdIsoPart = 3,
kFlashInfoBank = 0,
kFlashDataRegionZero = 0,
kFlashDataRegionOne = 1,
kRegionBaseBank0Page0Index = 0,
kRegionBaseBank1Page0Index = 256,
kPartitionId = 0,
kRegionSize = 1,
kPageSize = 2048,
kNumTestWords = 16,
kNumTestBytes = kNumTestWords * sizeof(uint32_t),
};
enum {
kTestPhaseCheckUnscrambledInit0 = 0,
kTestPhaseCheckUnscrambledInit1 = 1,
kTestPhaseCheckUnscrambledInit2 = 2,
kTestPhaseCheckScrambledInit0 = 3,
kTestPhaseCheckScrambledInit1 = 4,
kTestPhaseCheckBackdoor0 = 5,
kTestPhaseCheckBackdoor1 = 6,
kTestPhaseKeymgrPrep = 7,
kTestPhaseKeymgrTest0 = 8,
kTestPhaseKeymgrTest1 = 9,
};
enum {
kNumRegions = 5,
kAddressBank0Page0Data = 0,
kAddressBank1Page0Data = 1,
kAddressCreatorSecret = 2,
kAddressOwnerSecret = 3,
kAddressIsoPart = 4,
};
enum {
kCreatorSecretDataRetSramAddress = 0,
kOwnerSecretDataRetSramAddress =
kCreatorSecretDataRetSramAddress + (kNumTestWords * sizeof(uint32_t)),
kIsoPartDataRetSramAddress =
kOwnerSecretDataRetSramAddress + (kNumTestWords * sizeof(uint32_t)),
kBank0Page0DataRetSramAddress =
kIsoPartDataRetSramAddress + (kNumTestWords * sizeof(uint32_t)),
kBank1Page0DataRetSramAddress =
kBank0Page0DataRetSramAddress + (kNumTestWords * sizeof(uint32_t)),
};
// The test phase is updated by the testbench with a symbol backdoor overwrite.
static volatile const uint8_t kTestPhase = 0;
// The test data is updated by the testbench by filling the retention SRAM
// which can then be copied and used locally.
static uint32_t kCreatorSecretData[kNumTestWords];
static uint32_t kOwnerSecretData[kNumTestWords];
static uint32_t kIsoPartData[kNumTestWords];
static uint32_t kBank0Page0Data[kNumTestWords];
static uint32_t kBank1Page0Data[kNumTestWords];
static uint32_t region_addresses[kNumRegions];
static void setup_unscrambled_regions(void) {
region_addresses[kAddressBank0Page0Data] =
flash_ctrl_testutils_data_region_setup(&flash_state,
kRegionBaseBank0Page0Index,
kFlashDataRegionZero, kRegionSize);
region_addresses[kAddressBank1Page0Data] =
flash_ctrl_testutils_data_region_setup(&flash_state,
kRegionBaseBank1Page0Index,
kFlashDataRegionOne, kRegionSize);
region_addresses[kAddressCreatorSecret] =
flash_ctrl_testutils_info_region_setup(&flash_state,
kFlashInfoPageIdCreatorSecret,
kFlashInfoBank, kPartitionId);
region_addresses[kAddressOwnerSecret] =
flash_ctrl_testutils_info_region_setup(&flash_state,
kFlashInfoPageIdOwnerSecret,
kFlashInfoBank, kPartitionId);
region_addresses[kAddressIsoPart] = flash_ctrl_testutils_info_region_setup(
&flash_state, kFlashInfoPageIdIsoPart, kFlashInfoBank, kPartitionId);
}
static void setup_scrambled_regions(void) {
region_addresses[kAddressBank0Page0Data] =
flash_ctrl_testutils_data_region_scrambled_setup(
&flash_state, kRegionBaseBank0Page0Index, kFlashDataRegionZero,
kRegionSize);
region_addresses[kAddressBank1Page0Data] =
flash_ctrl_testutils_data_region_scrambled_setup(
&flash_state, kRegionBaseBank1Page0Index, kFlashDataRegionOne,
kRegionSize);
region_addresses[kAddressCreatorSecret] =
flash_ctrl_testutils_info_region_scrambled_setup(
&flash_state, kFlashInfoPageIdCreatorSecret, kFlashInfoBank,
kPartitionId);
region_addresses[kAddressOwnerSecret] =
flash_ctrl_testutils_info_region_scrambled_setup(
&flash_state, kFlashInfoPageIdOwnerSecret, kFlashInfoBank,
kPartitionId);
region_addresses[kAddressIsoPart] =
flash_ctrl_testutils_info_region_scrambled_setup(
&flash_state, kFlashInfoPageIdIsoPart, kFlashInfoBank, kPartitionId);
}
static void erase_and_write_regions(void) {
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash_state, region_addresses[kAddressBank0Page0Data], kPartitionId,
kBank0Page0Data, kDifFlashCtrlPartitionTypeData, kNumTestWords));
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash_state, region_addresses[kAddressBank1Page0Data], kPartitionId,
kBank1Page0Data, kDifFlashCtrlPartitionTypeData, kNumTestWords));
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash_state, region_addresses[kAddressCreatorSecret], kPartitionId,
kCreatorSecretData, kDifFlashCtrlPartitionTypeInfo, kNumTestWords));
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash_state, region_addresses[kAddressOwnerSecret], kPartitionId,
kOwnerSecretData, kDifFlashCtrlPartitionTypeInfo, kNumTestWords));
CHECK(flash_ctrl_testutils_erase_and_write_page(
&flash_state, region_addresses[kAddressIsoPart], kPartitionId,
kIsoPartData, kDifFlashCtrlPartitionTypeInfo, kNumTestWords));
}
static void read_and_check_host_if(uint32_t addr, const uint32_t *check_data) {
uint32_t host_data[kNumTestWords];
mmio_region_memcpy_from_mmio32(
mmio_region_from_addr(TOP_EARLGREY_EFLASH_BASE_ADDR), addr, &host_data,
kNumTestBytes);
CHECK_ARRAYS_EQ(host_data, check_data, kNumTestWords);
}
static void check_readback_data_match(void) {
uint32_t readback_data[kNumTestWords];
CHECK(flash_ctrl_testutils_read(
&flash_state, region_addresses[kAddressBank0Page0Data], kPartitionId,
readback_data, kDifFlashCtrlPartitionTypeData, kNumTestWords, 0));
CHECK_ARRAYS_EQ(readback_data, kBank0Page0Data, kNumTestWords);
CHECK(flash_ctrl_testutils_read(
&flash_state, region_addresses[kAddressBank1Page0Data], kPartitionId,
readback_data, kDifFlashCtrlPartitionTypeData, kNumTestWords, 0));
CHECK_ARRAYS_EQ(readback_data, kBank1Page0Data, kNumTestWords);
CHECK(flash_ctrl_testutils_read(
&flash_state, region_addresses[kAddressCreatorSecret], kPartitionId,
readback_data, kDifFlashCtrlPartitionTypeInfo, kNumTestWords, 0));
CHECK_ARRAYS_EQ(readback_data, kCreatorSecretData, kNumTestWords);
CHECK(flash_ctrl_testutils_read(
&flash_state, region_addresses[kAddressOwnerSecret], kPartitionId,
readback_data, kDifFlashCtrlPartitionTypeInfo, kNumTestWords, 0));
CHECK_ARRAYS_EQ(readback_data, kOwnerSecretData, kNumTestWords);
CHECK(flash_ctrl_testutils_read(
&flash_state, region_addresses[kAddressIsoPart], kPartitionId,
readback_data, kDifFlashCtrlPartitionTypeInfo, kNumTestWords, 0));
CHECK_ARRAYS_EQ(readback_data, kIsoPartData, kNumTestWords);
read_and_check_host_if(kRegionBaseBank0Page0Index, kBank0Page0Data);
read_and_check_host_if(kPageSize * kRegionBaseBank1Page0Index,
kBank1Page0Data);
}
static bool transaction_end_check_read_error(void) {
dif_flash_ctrl_output_t output;
while (true) {
dif_result_t dif_result = dif_flash_ctrl_end(&flash_state, &output);
CHECK(dif_result != kDifBadArg);
CHECK(dif_result != kDifError);
if (dif_result == kDifOk) {
break;
}
}
bool is_read_error = !output.error_code.codes.memory_properties_error &
output.error_code.codes.read_error &
!output.error_code.codes.prog_window_error &
!output.error_code.codes.prog_type_error &
!output.error_code.codes.shadow_register_error;
CHECK_DIF_OK(
dif_flash_ctrl_clear_error_codes(&flash_state, output.error_code.codes));
return is_read_error;
}
static void check_readback_fail(void) {
uint32_t readback_data[kNumTestWords];
dif_flash_ctrl_transaction_t transaction = {
.byte_address = region_addresses[kAddressBank0Page0Data],
.op = kDifFlashCtrlOpRead,
.partition_type = kDifFlashCtrlPartitionTypeData,
.partition_id = kPartitionId,
.word_count = kNumTestWords};
CHECK_DIF_OK(dif_flash_ctrl_start(&flash_state, transaction));
CHECK_DIF_OK(
dif_flash_ctrl_read_fifo_pop(&flash_state, kNumTestWords, readback_data));
CHECK(transaction_end_check_read_error());
transaction.byte_address = region_addresses[kAddressBank1Page0Data];
CHECK_DIF_OK(dif_flash_ctrl_start(&flash_state, transaction));
CHECK_DIF_OK(
dif_flash_ctrl_read_fifo_pop(&flash_state, kNumTestWords, readback_data));
CHECK(transaction_end_check_read_error());
transaction.partition_type = kDifFlashCtrlPartitionTypeInfo;
transaction.byte_address = region_addresses[kAddressCreatorSecret];
CHECK_DIF_OK(dif_flash_ctrl_start(&flash_state, transaction));
CHECK_DIF_OK(
dif_flash_ctrl_read_fifo_pop(&flash_state, kNumTestWords, readback_data));
CHECK(transaction_end_check_read_error());
transaction.byte_address = region_addresses[kAddressOwnerSecret];
CHECK_DIF_OK(dif_flash_ctrl_start(&flash_state, transaction));
CHECK_DIF_OK(
dif_flash_ctrl_read_fifo_pop(&flash_state, kNumTestWords, readback_data));
CHECK(transaction_end_check_read_error());
transaction.byte_address = region_addresses[kAddressIsoPart];
CHECK_DIF_OK(dif_flash_ctrl_start(&flash_state, transaction));
CHECK_DIF_OK(
dif_flash_ctrl_read_fifo_pop(&flash_state, kNumTestWords, readback_data));
CHECK(transaction_end_check_read_error());
}
static void flash_init(void) {
CHECK_DIF_OK(dif_flash_ctrl_start_controller_init(&flash_state));
dif_flash_ctrl_status_t flash_ctrl_status;
while (true) {
CHECK_DIF_OK(dif_flash_ctrl_get_status(&flash_state, &flash_ctrl_status));
if (flash_ctrl_status.controller_init_wip == false) {
break;
}
}
}
static void check_unscrambled_init(void) {
setup_unscrambled_regions();
erase_and_write_regions();
flash_init();
check_readback_data_match();
test_status_set(kTestStatusInWfi);
wait_for_interrupt();
}
static void check_scrambled_init(void) {
setup_scrambled_regions();
erase_and_write_regions();
check_readback_data_match();
flash_init();
check_readback_fail();
test_status_set(kTestStatusInWfi);
wait_for_interrupt();
}
static void check_scrambled_backdoor_data(void) {
setup_scrambled_regions();
flash_init();
check_readback_data_match();
test_status_set(kTestStatusInWfi);
wait_for_interrupt();
}
bool rom_test_main(void) {
// We need to set the test status as "in test" to indicate to the test code
// has been reached, even though this test is also in the "boot ROM".
test_status_set(kTestStatusInTest);
dif_pinmux_t pinmux;
CHECK_DIF_OK(dif_pinmux_init(
mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
pinmux_testutils_init(&pinmux);
// We need to initialize the UART regardless if we LOG any messages, since
// Verilator and FPGA platforms use the UART to communicate the test results.
if (kDeviceType != kDeviceSimDV) {
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
CHECK_DIF_OK(
dif_uart_configure(&uart0, (dif_uart_config_t){
.baudrate = kUartBaudrate,
.clk_freq_hz = kClockFreqPeripheralHz,
.parity_enable = kDifToggleDisabled,
.parity = kDifUartParityEven,
.tx_enable = kDifToggleEnabled,
.rx_enable = kDifToggleEnabled,
}));
base_uart_stdout(&uart0);
}
// Test code.
mmio_region_t sram_region_ret_base_addr =
mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR);
mmio_region_memcpy_from_mmio32(sram_region_ret_base_addr,
kCreatorSecretDataRetSramAddress,
&kCreatorSecretData, kNumTestBytes);
mmio_region_memcpy_from_mmio32(sram_region_ret_base_addr,
kOwnerSecretDataRetSramAddress,
&kOwnerSecretData, kNumTestBytes);
mmio_region_memcpy_from_mmio32(sram_region_ret_base_addr,
kIsoPartDataRetSramAddress, &kIsoPartData,
kNumTestBytes);
mmio_region_memcpy_from_mmio32(sram_region_ret_base_addr,
kBank0Page0DataRetSramAddress,
&kBank0Page0Data, kNumTestBytes);
mmio_region_memcpy_from_mmio32(sram_region_ret_base_addr,
kBank1Page0DataRetSramAddress,
&kBank1Page0Data, kNumTestBytes);
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash_state,
mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR)));
switch (kTestPhase) {
case kTestPhaseCheckUnscrambledInit0:
case kTestPhaseCheckUnscrambledInit1:
case kTestPhaseCheckUnscrambledInit2:
check_unscrambled_init();
break;
case kTestPhaseCheckScrambledInit0:
case kTestPhaseCheckScrambledInit1:
check_scrambled_init();
break;
case kTestPhaseCheckBackdoor0:
case kTestPhaseCheckBackdoor1:
check_scrambled_backdoor_data();
break;
case kTestPhaseKeymgrPrep:
case kTestPhaseKeymgrTest0:
case kTestPhaseKeymgrTest1:
flash_init();
test_status_set(kTestStatusInWfi);
wait_for_interrupt();
break;
default:
break;
}
return true;
}