blob: 53504e9df37fc5492eec26f1e143ced2c166af5b [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/silicon_creator/rom/bootstrap.h"
#include <cstring>
#include <limits>
#include "gtest/gtest.h"
#include "sw/device/lib/base/mock_abs_mmio.h"
#include "sw/device/silicon_creator/lib/base/chip.h"
#include "sw/device/silicon_creator/lib/drivers/mock_flash_ctrl.h"
#include "sw/device/silicon_creator/lib/drivers/mock_otp.h"
#include "sw/device/silicon_creator/lib/drivers/mock_rstmgr.h"
#include "sw/device/silicon_creator/lib/drivers/mock_spi_device.h"
#include "sw/device/silicon_creator/testing/rom_test.h"
#include "flash_ctrl_regs.h"
#include "gpio_regs.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otp_ctrl_regs.h"
bool operator==(flash_ctrl_perms_t lhs, flash_ctrl_perms_t rhs) {
return std::memcmp(&lhs, &rhs, sizeof(flash_ctrl_perms_t)) == 0;
}
namespace bootstrap_unittest {
namespace {
using ::testing::DoAll;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
MATCHER_P(HasBytes, bytes, "") {
return std::memcmp(arg, bytes.data(), bytes.size()) == 0;
}
class BootstrapTest : public rom_test::RomTest {
protected:
/**
* Sets an expectation for a bootstrap request check.
*
* @param requested Whether bootstrap is requested.
*/
void ExpectBootstrapRequestCheck(bool requested) {
EXPECT_CALL(otp_,
read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET))
.WillOnce(Return(kHardenedBoolTrue));
uint32_t pins = SW_STRAP_BOOTSTRAP;
if (!requested) {
pins = ~pins;
}
EXPECT_ABS_READ32(TOP_EARLGREY_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET,
pins);
}
/**
* Sets an expectation for a spi flash command.
*
* @param cmd Command struct to output.
*/
void ExpectSpiCmd(spi_device_cmd_t cmd) {
EXPECT_CALL(spi_device_, CmdGet(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(cmd), Return(kErrorOk)));
}
/**
* Sets an expectation for getting the SPI flash status register.
*
* @param wel Value of the WEL bit.
*/
void ExpectSpiFlashStatusGet(bool wel) {
EXPECT_CALL(spi_device_, FlashStatusGet())
.WillOnce(Return(wel << kSpiDeviceWelBit));
}
/**
* Sets an expectation for enabling write for the data partition.
*/
void ExpectFlashCtrlWriteEnable() {
EXPECT_CALL(flash_ctrl_, DataDefaultPermsSet((flash_ctrl_perms_t){
.read = kMultiBitBool4False,
.write = kMultiBitBool4True,
.erase = kMultiBitBool4False,
}));
}
/**
* Sets an expectation for disabling all permissions for the data partition.
*/
void ExpectFlashCtrlAllDisable() {
EXPECT_CALL(flash_ctrl_, DataDefaultPermsSet((flash_ctrl_perms_t){
.read = kMultiBitBool4False,
.write = kMultiBitBool4False,
.erase = kMultiBitBool4False,
}));
}
/**
* Sets expectations for a chip erase.
*
* @param err0 Result of erase for the first bank.
* @param err1 Result of erase for the second bank.
*/
void ExpectFlashCtrlChipErase(rom_error_t err0, rom_error_t err1) {
EXPECT_CALL(flash_ctrl_, BankErasePermsSet(kHardenedBoolTrue));
EXPECT_CALL(flash_ctrl_, DataErase(0, kFlashCtrlEraseTypeBank))
.WillOnce(Return(err0));
EXPECT_CALL(flash_ctrl_, DataErase(FLASH_CTRL_PARAM_BYTES_PER_BANK,
kFlashCtrlEraseTypeBank))
.WillOnce(Return(err1));
EXPECT_CALL(flash_ctrl_, BankErasePermsSet(kHardenedBoolFalse));
}
/**
* Sets expectations for a sector erase.
*
* @param err0 Result of erase for the first page.
* @param err1 Result of erase for the second page.
* @param addr Erase start address.
*/
void ExpectFlashCtrlSectorErase(rom_error_t err0, rom_error_t err1,
uint32_t addr) {
EXPECT_CALL(flash_ctrl_, DataDefaultPermsSet((flash_ctrl_perms_t){
.read = kMultiBitBool4False,
.write = kMultiBitBool4False,
.erase = kMultiBitBool4True,
}));
EXPECT_CALL(flash_ctrl_, DataErase(addr, kFlashCtrlEraseTypePage))
.WillOnce(Return(err0));
EXPECT_CALL(flash_ctrl_, DataErase(addr + FLASH_CTRL_PARAM_BYTES_PER_PAGE,
kFlashCtrlEraseTypePage))
.WillOnce(Return(err1));
ExpectFlashCtrlAllDisable();
}
/**
* Sets expectations for a chip erase verification.
*
* @param err0 Result of erase verification for the first bank.
* @param err1 Result of erase verification for the second bank.
*/
void ExpectFlashCtrlEraseVerify(rom_error_t err0, rom_error_t err1) {
EXPECT_CALL(flash_ctrl_, DataEraseVerify(0, kFlashCtrlEraseTypeBank))
.WillOnce(Return(err0));
EXPECT_CALL(flash_ctrl_, DataEraseVerify(FLASH_CTRL_PARAM_BYTES_PER_BANK,
kFlashCtrlEraseTypeBank))
.WillOnce(Return(err1));
}
rom_test::MockAbsMmio mmio_;
rom_test::MockFlashCtrl flash_ctrl_;
rom_test::MockOtp otp_;
rom_test::MockRstmgr rstmgr_;
rom_test::MockSpiDevice spi_device_;
};
TEST_F(BootstrapTest, RequestedDisabled) {
EXPECT_CALL(otp_, read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET))
.WillOnce(Return(kHardenedBoolFalse));
EXPECT_EQ(bootstrap_requested(), kHardenedBoolFalse);
}
TEST_F(BootstrapTest, RequestedEnabled) {
EXPECT_CALL(otp_, read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET))
.WillOnce(Return(kHardenedBoolTrue));
EXPECT_ABS_READ32(TOP_EARLGREY_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET,
SW_STRAP_BOOTSTRAP);
EXPECT_EQ(bootstrap_requested(), kHardenedBoolTrue);
EXPECT_CALL(otp_, read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET))
.WillOnce(Return(kHardenedBoolTrue));
EXPECT_ABS_READ32(TOP_EARLGREY_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET,
~SW_STRAP_BOOTSTRAP);
EXPECT_EQ(bootstrap_requested(), kHardenedBoolFalse);
}
/**
* Returns a struct that represents a CHIP_ERASE command.
*
* @return A `spi_device_cmd_t` that represents a CHIP_ERASE command.
*/
spi_device_cmd_t ChipEraseCmd() {
return {
.opcode = kSpiDeviceOpcodeChipErase,
.address = kSpiDeviceNoAddress,
.payload_byte_count = 0,
.payload = {},
};
}
/**
* Returns a struct that represents a SECTOR_ERASE command.
*
* @param address Address field of the command.
* @return A `spi_device_cmd_t` that represents a SECTOR_ERASE command.
*/
spi_device_cmd_t SectorEraseCmd(uint32_t address) {
return {
.opcode = kSpiDeviceOpcodeSectorErase,
.address = address,
.payload_byte_count = 0,
.payload = {},
};
}
/**
* Returns a struct that represents a PAGE_PROGRAM command.
*
* @param address Address field of the command.
* @param payload_byte_count Payload size in bytes.
* @return A `spi_device_cmd_t` that represents a PAGE_PROGRAM command.
*/
spi_device_cmd_t PageProgramCmd(uint32_t address, size_t payload_byte_count) {
spi_device_cmd_t cmd{
.opcode = kSpiDeviceOpcodePageProgram,
.address = address,
.payload_byte_count = payload_byte_count,
};
EXPECT_LE(payload_byte_count, kSpiDevicePayloadAreaNumBytes);
for (size_t i = 0; i < payload_byte_count; ++i) {
cmd.payload[i] = static_cast<uint8_t>(i);
}
return cmd;
}
spi_device_cmd_t ResetCmd() {
return {
.opcode = kSpiDeviceOpcodeReset,
.address = kSpiDeviceNoAddress,
.payload_byte_count = 0,
.payload = {},
};
}
TEST_F(BootstrapTest, PayloadOverflowErase) {
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
EXPECT_CALL(spi_device_, CmdGet(NotNull()))
.WillOnce(Return(kErrorSpiDevicePayloadOverflow));
EXPECT_EQ(bootstrap(), kErrorSpiDevicePayloadOverflow);
}
TEST_F(BootstrapTest, PayloadOverflowProgram) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
EXPECT_CALL(spi_device_, CmdGet(NotNull()))
.WillOnce(Return(kErrorSpiDevicePayloadOverflow));
EXPECT_EQ(bootstrap(), kErrorSpiDevicePayloadOverflow);
}
TEST_F(BootstrapTest, BootstrapSimple) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0, 16);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_, DataWrite(0, 4, HasBytes(flash_bytes)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BootstrapOddPayload) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0, 17);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
for (size_t i = 0; i < 7; ++i) {
flash_bytes.push_back(0xff);
}
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_, DataWrite(cmd.address, 6, HasBytes(flash_bytes)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BootstrapMisalignedAddrLongPayload) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0xfff0, 256);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes_0(cmd.payload, cmd.payload + 16);
std::vector<uint8_t> flash_bytes_1(cmd.payload + 16,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_, DataWrite(0xfff0, 4, HasBytes(flash_bytes_0)))
.WillOnce(Return(kErrorOk));
EXPECT_CALL(flash_ctrl_, DataWrite(0xff00, 60, HasBytes(flash_bytes_1)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BootstrapMisalignedAddrShortPayload) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(816, 8);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_, DataWrite(816, 2, HasBytes(flash_bytes)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BootstrapStartWithSectorErase) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(SectorEraseCmd(0));
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0, 16);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_,
DataWrite(cmd.address, cmd.payload_byte_count / sizeof(uint32_t),
HasBytes(flash_bytes)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BootstrapProgramWithErase) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0, 16);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_,
DataWrite(cmd.address, cmd.payload_byte_count / sizeof(uint32_t),
HasBytes(flash_bytes)))
.WillOnce(Return(kErrorOk));
ExpectFlashCtrlAllDisable();
EXPECT_CALL(spi_device_, FlashStatusClear());
// Chip erase
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Sector erase
ExpectSpiCmd(SectorEraseCmd(0));
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlSectorErase(kErrorOk, kErrorOk, 0);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, MisalignedEraseAddress) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Erase with misaligned and aligned addresses
ExpectSpiCmd(SectorEraseCmd(5));
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlSectorErase(kErrorOk, kErrorOk, 0);
EXPECT_CALL(spi_device_, FlashStatusClear());
ExpectSpiCmd(SectorEraseCmd(4096));
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlSectorErase(kErrorOk, kErrorOk, 4096);
EXPECT_CALL(spi_device_, FlashStatusClear());
ExpectSpiCmd(SectorEraseCmd(8195));
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlSectorErase(kErrorOk, kErrorOk, 8192);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, IgnoredCommands) {
// Phase 1: Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd()); // Ignored, missing WREN.
ExpectSpiFlashStatusGet(false);
ExpectSpiCmd(ResetCmd()); // Ignored, not supported.
ExpectSpiFlashStatusGet(true);
EXPECT_CALL(spi_device_, FlashStatusClear());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Phase 1: Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Phase 2: Erase/Program
ExpectSpiCmd(SectorEraseCmd(0));
ExpectSpiFlashStatusGet(false);
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(false);
ExpectSpiCmd(PageProgramCmd(0, 16));
ExpectSpiFlashStatusGet(false);
// Reset
ExpectSpiCmd(ResetCmd());
EXPECT_CALL(rstmgr_, Reset());
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, EraseBank0Error) {
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorUnknown, kErrorOk);
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, EraseBank1Error) {
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorUnknown);
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, EraseVerifyBank0Error) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorUnknown, kErrorOk);
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, EraseVerifyBank1Error) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorUnknown);
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, DataWriteError) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0, 16);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_,
DataWrite(cmd.address, cmd.payload_byte_count / sizeof(uint32_t),
HasBytes(flash_bytes)))
.WillOnce(Return(kErrorUnknown));
ExpectFlashCtrlAllDisable();
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, DataWriteErrorMisalignedAddr) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto cmd = PageProgramCmd(0xf0, 16);
ExpectSpiCmd(cmd);
ExpectSpiFlashStatusGet(true);
std::vector<uint8_t> flash_bytes(cmd.payload,
cmd.payload + cmd.payload_byte_count);
ExpectFlashCtrlWriteEnable();
EXPECT_CALL(flash_ctrl_, DataWrite(0xf0, 4, HasBytes(flash_bytes)))
.WillOnce(Return(kErrorUnknown));
ExpectFlashCtrlAllDisable();
EXPECT_EQ(bootstrap(), kErrorUnknown);
}
TEST_F(BootstrapTest, BadProgramAddress) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Program
auto page_program_cmd = PageProgramCmd(3, 16);
ExpectSpiCmd(page_program_cmd);
ExpectSpiFlashStatusGet(true);
EXPECT_EQ(bootstrap(), kErrorBootstrapProgramAddress);
}
TEST_F(BootstrapTest, BadEraseAddress) {
// Erase
ExpectBootstrapRequestCheck(true);
EXPECT_CALL(spi_device_, Init());
ExpectSpiCmd(ChipEraseCmd());
ExpectSpiFlashStatusGet(true);
ExpectFlashCtrlChipErase(kErrorOk, kErrorOk);
// Verify
ExpectFlashCtrlEraseVerify(kErrorOk, kErrorOk);
EXPECT_CALL(spi_device_, FlashStatusClear());
// Erase
ExpectSpiCmd(SectorEraseCmd(FLASH_CTRL_PARAM_BYTES_PER_BANK *
FLASH_CTRL_PARAM_REG_NUM_BANKS));
ExpectSpiFlashStatusGet(true);
EXPECT_EQ(bootstrap(), kErrorBootstrapEraseAddress);
}
TEST_F(BootstrapTest, NotRequested) {
ExpectBootstrapRequestCheck(false);
EXPECT_EQ(bootstrap(), kErrorBootstrapNotRequested);
}
} // namespace
} // namespace bootstrap_unittest