blob: 169e90ac221c00516929ca58ff28caf9f793461b [file] [log] [blame]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "google3/host_commands.h"
#include "firmware_spi_updater.hpp"
#include "host_command_mock.hpp"
#include "message_util.hpp"
#include <xyz/openbmc_project/Control/Hoth/error.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstring>
using google::hoth::internal::FirmwareSpiUpdater;
using google::hoth::internal::HostCommandMock;
using google::hoth::internal::ResetMode;
using sdbusplus::error::xyz::openbmc_project::control::hoth::FirmwareFailure;
using ::testing::_;
using ::testing::Return;
class FirmwareSpiUpdaterTest : public ::testing::Test
{
protected:
// Host Command interface handle
HostCommandMock hostCmd;
std::vector<uint8_t> hothGetRegionResponse = {
0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, // Header
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01 // Data
};
std::vector<uint8_t> hothSpiEmptyResponse = {
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Header
};
std::vector<uint8_t> hothSpiReadResponse = {
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RspHeader
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Padding
0x01, 0x01, 0x01, 0x01, 0x01 // Data
};
std::vector<uint8_t> hothSpiEnter4BResponse = {
0x03, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xd2, 0x4e, 0x00,
0xe8, 0xcf, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
std::vector<uint8_t> hothSpiExit4BResponse = {
0x03, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xd2, 0x4e, 0x00,
0xe8, 0xcf, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
std::vector<uint8_t> hothGetSpsPassthoughEnabledResponse = {
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // Header
0x01, // Enabled
0x00, 0x00, 0x00, // Padding
};
std::vector<uint8_t> hothGetSpsPassthoughDisabledResponse = {
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // Header
0x00, // Enabled
0x00, 0x00, 0x00, // Padding
};
std::vector<uint8_t> hothCommandFailedResponse = {
0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Header
};
};
TEST_F(FirmwareSpiUpdaterTest, SuccessEmptyData)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothGetRegionResponse))
.WillOnce(Return(hothSpiEnter4BResponse));
FirmwareSpiUpdater updater(&hostCmd);
ASSERT_NO_THROW(updater.update({}));
}
TEST_F(FirmwareSpiUpdaterTest, SuccessEmptyData3BytesAddress)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothGetRegionResponse))
.WillOnce(Return(hothSpiEnter4BResponse));
FirmwareSpiUpdater updater(&hostCmd, 3, ResetMode::never);
ASSERT_NO_THROW(updater.update({}));
}
TEST_F(FirmwareSpiUpdaterTest, SuccessNonEmptyData)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothGetRegionResponse))
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse));
FirmwareSpiUpdater updater(&hostCmd);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.update(firmwareData));
}
TEST_F(FirmwareSpiUpdaterTest, InvalidGetRegionResponse)
{
FirmwareSpiUpdater updater(&hostCmd);
// Invalid GetRegionResponse in Hoth response
hothGetRegionResponse.pop_back();
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothGetRegionResponse));
EXPECT_THROW(updater.update({}), std::runtime_error);
}
TEST_F(FirmwareSpiUpdaterTest, GetRegionResponseFlagOff)
{
FirmwareSpiUpdater updater(&hostCmd);
// Flag off
hothGetRegionResponse.back() = 0x00;
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothGetRegionResponse));
EXPECT_THROW(updater.update({}), FirmwareFailure);
}
TEST_F(FirmwareSpiUpdaterTest, SuccessSpiWriteNonEmptyData)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse));
FirmwareSpiUpdater updater(&hostCmd);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.spiWrite(0, firmwareData));
}
TEST_F(FirmwareSpiUpdaterTest, SpiWriteSpsPassthroughDisabled)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_GET_SPS_PASSTHROUGH_STATUS);
return hothGetSpsPassthoughDisabledResponse;
})
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse));
FirmwareSpiUpdater updater(&hostCmd, /* addressSize = */ 4, ResetMode::needed);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.spiWrite(0, firmwareData));
}
TEST_F(FirmwareSpiUpdaterTest, SpiWriteSpsPassthroughEnabled)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_GET_SPS_PASSTHROUGH_STATUS);
return hothGetSpsPassthoughEnabledResponse;
})
.WillOnce([this](uint16_t cmd, auto&&, const void* requestPtr, size_t requestSize) {
ec_request_reset_target request = {};
EXPECT_EQ(requestSize, sizeof(request));
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_RESET_TARGET);
std::memcpy(&request, requestPtr, std::min(requestSize, sizeof(request)));
EXPECT_EQ(request.reset_option, EC_TARGET_RESET_OPTION_SET);
return hothSpiEmptyResponse;
})
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse))
.WillOnce([this](uint16_t cmd, auto&&, const void* requestPtr, size_t requestSize) {
ec_request_reset_target request = {};
EXPECT_EQ(requestSize, sizeof(request));
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_RESET_TARGET);
std::memcpy(&request, requestPtr, std::min(requestSize, sizeof(request)));
EXPECT_EQ(request.reset_option, EC_TARGET_RESET_OPTION_RELEASE);
return hothSpiEmptyResponse;
});
FirmwareSpiUpdater updater(&hostCmd, /* addressSize = */ 4, ResetMode::needed);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.spiWrite(0, firmwareData));
}
TEST_F(FirmwareSpiUpdaterTest, SpiWriteActivelyDisablePassthrough)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_GET_SPS_PASSTHROUGH_STATUS);
return hothGetSpsPassthoughEnabledResponse;
})
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_SPS_PASSTHROUGH_DISABLE);
return hothSpiEmptyResponse;
})
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse))
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_SPS_PASSTHROUGH_ENABLE);
return hothSpiEmptyResponse;
});
FirmwareSpiUpdater updater(&hostCmd, /* addressSize = */ 4, ResetMode::needed_active);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.spiWrite(0, firmwareData));
}
TEST_F(FirmwareSpiUpdaterTest, SpiWriteDisablePassthroughFailed)
{
EXPECT_CALL(hostCmd, sendCommand(_, _, _, _))
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_GET_SPS_PASSTHROUGH_STATUS);
return hothGetSpsPassthoughEnabledResponse;
})
.WillOnce([this](uint16_t cmd, auto&&...){
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_SPS_PASSTHROUGH_DISABLE);
return hothCommandFailedResponse;
})
.WillOnce([this](uint16_t cmd, auto&&, const void* requestPtr, size_t requestSize) {
ec_request_reset_target request = {};
EXPECT_EQ(requestSize, sizeof(request));
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_RESET_TARGET);
std::memcpy(&request, requestPtr, std::min(requestSize, sizeof(request)));
EXPECT_EQ(request.reset_option, EC_TARGET_RESET_OPTION_SET);
return hothSpiEmptyResponse;
})
.WillOnce(Return(hothSpiEnter4BResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiEmptyResponse))
.WillOnce(Return(hothSpiReadResponse))
.WillOnce([this](uint16_t cmd, auto&&, const void* requestPtr, size_t requestSize) {
ec_request_reset_target request = {};
EXPECT_EQ(requestSize, sizeof(request));
EXPECT_EQ(cmd, EC_CMD_BOARD_SPECIFIC_BASE | EC_PRV_CMD_HOTH_RESET_TARGET);
std::memcpy(&request, requestPtr, std::min(requestSize, sizeof(request)));
EXPECT_EQ(request.reset_option, EC_TARGET_RESET_OPTION_RELEASE);
return hothSpiEmptyResponse;
});
FirmwareSpiUpdater updater(&hostCmd, /* addressSize = */ 4, ResetMode::needed_active);
std::vector<uint8_t> firmwareData(5, 0x01);
ASSERT_NO_THROW(updater.spiWrite(0, firmwareData));
}