blob: c4273c491c4fc84ec52fbf5b24d5d8fc19f81f4e [file] [log] [blame]
#include "helper.hpp"
#include "ipmi.hpp"
#include "manager_mock.hpp"
#include "process.hpp"
#include <ipmiblob/test/crc_mock.hpp>
#include <cstring>
#include <span>
#include <gtest/gtest.h>
// ipmid.hpp isn't installed where we can grab it and this value is per BMC
// SoC.
#define MAX_IPMI_BUFFER 64
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Return;
using ::testing::StrictMock;
namespace ipmiblob
{
CrcInterface* crcIntf = nullptr;
std::uint16_t generateCrc(const std::vector<std::uint8_t>& data)
{
return (crcIntf) ? crcIntf->generateCrc(data) : 0x00;
}
} // namespace ipmiblob
namespace blobs
{
namespace
{
void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs)
{
EXPECT_FALSE(lhs == nullptr);
EXPECT_FALSE(rhs == nullptr);
Resp (*const* lPtr)(ManagerInterface*, std::span<const uint8_t>) =
lhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>();
Resp (*const* rPtr)(ManagerInterface*, std::span<const uint8_t>) =
rhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>();
EXPECT_TRUE(lPtr);
EXPECT_TRUE(rPtr);
EXPECT_EQ(*lPtr, *rPtr);
}
} // namespace
class ValidateBlobCommandTest : public ::testing::Test
{
protected:
void SetUp() override
{
ipmiblob::crcIntf = &crcMock;
}
ipmiblob::CrcMock crcMock;
};
TEST_F(ValidateBlobCommandTest, InvalidCommandReturnsFailure)
{
// Verify we handle an invalid command.
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
// There is no command 0xff.
IpmiBlobHandler handler = validateBlobCommand(0xff, request);
EXPECT_EQ(ipmi::responseInvalidFieldRequest(), handler(nullptr, {}));
}
TEST_F(ValidateBlobCommandTest, ValidCommandWithoutPayload)
{
// Verify we handle a valid command that doesn't have a payload.
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
IpmiBlobHandler handler = validateBlobCommand(
static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
EXPECT_FALSE(handler == nullptr);
EqualFunctions(getBlobCount, handler);
}
TEST_F(ValidateBlobCommandTest, WithPayloadMinimumLengthIs3VerifyChecks)
{
// Verify that if there's a payload, it's at least one command byte and
// two bytes for the crc16 and then one data byte.
std::vector<uint8_t> request(sizeof(uint16_t));
// There is a payload, but there are insufficient bytes.
IpmiBlobHandler handler = validateBlobCommand(
static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
EXPECT_EQ(ipmi::responseReqDataLenInvalid(), handler(nullptr, {}));
}
TEST_F(ValidateBlobCommandTest, WithPayloadAndInvalidCrc)
{
// Verify that the CRC is checked, and failure is reported.
std::vector<uint8_t> request;
BmcBlobWriteTx req;
req.crc = 0x34;
req.sessionId = 0x54;
req.offset = 0x100;
std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
request.resize(sizeof(struct BmcBlobWriteTx));
std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
// skip over cmd and crc.
std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
request.end());
EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x1234));
IpmiBlobHandler handler = validateBlobCommand(
static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
EXPECT_EQ(ipmi::responseUnspecifiedError(), handler(nullptr, {}));
}
TEST_F(ValidateBlobCommandTest, WithPayloadAndValidCrc)
{
// Verify the CRC is checked and if it matches, return the handler.
std::vector<uint8_t> request;
BmcBlobWriteTx req;
req.crc = 0x3412;
req.sessionId = 0x54;
req.offset = 0x100;
std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
request.resize(sizeof(struct BmcBlobWriteTx));
std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
// skip over cmd and crc.
std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
request.end());
EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x3412));
IpmiBlobHandler handler = validateBlobCommand(
static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
EXPECT_FALSE(handler == nullptr);
EqualFunctions(writeBlob, handler);
}
class ProcessBlobCommandTest : public ::testing::Test
{
protected:
void SetUp() override
{
ipmiblob::crcIntf = &crcMock;
}
ipmiblob::CrcMock crcMock;
};
TEST_F(ProcessBlobCommandTest, CommandReturnsNotOk)
{
// Verify that if the IPMI command handler returns not OK that this is
// noticed and returned.
StrictMock<ManagerMock> manager;
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseInvalidCommand();
};
EXPECT_EQ(ipmi::responseInvalidCommand(),
processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
}
TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload)
{
// Verify that if the IPMI command handler returns OK but without a payload
// it doesn't try to compute a CRC.
StrictMock<ManagerMock> manager;
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseSuccess(std::vector<uint8_t>());
};
EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>()),
processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
}
TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength)
{
// There is a minimum payload length of 2 bytes (the CRC only, no data, for
// read), this returns 1.
StrictMock<ManagerMock> manager;
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseSuccess(std::vector<uint8_t>(1));
};
EXPECT_EQ(ipmi::responseUnspecifiedError(),
processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
}
TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength)
{
// There is a minimum payload length of 3 bytes, this command returns a
// payload of 3 bytes and the crc code is called to process the payload.
StrictMock<ManagerMock> manager;
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
IpmiBlobHandler h = [payloadLen](ManagerInterface*,
std::span<const uint8_t>) {
std::vector<uint8_t> output(payloadLen, 0);
output[2] = 0x56;
return ipmi::responseSuccess(output);
};
EXPECT_CALL(crcMock, generateCrc(_)).WillOnce(Return(0x3412));
auto result = validateReply(
processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
EXPECT_EQ(result.size(), payloadLen);
EXPECT_THAT(result, ElementsAre(0x12, 0x34, 0x56));
}
TEST_F(ProcessBlobCommandTest,
CommandReturnsErrorWithReplyExceededMaxTransferSize)
{
// There is a minimum payload length of 3 bytes, this command returns a
// payload of 3 bytes and the crc code is called to process the payload.
StrictMock<ManagerMock> manager;
std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
IpmiBlobHandler h = [payloadLen](ManagerInterface*,
std::span<const uint8_t>) {
std::vector<uint8_t> output(payloadLen, 0);
output[2] = 0x56;
return ipmi::responseSuccess(output);
};
EXPECT_EQ(ipmi::responseResponseError(),
processBlobCommand(h, &manager, request, 0));
}
} // namespace blobs