blob: a908ee6be850a7bcf772386c26664f8bf611aa79 [file] [log] [blame]
#include "crc.hpp"
#include "crc_mock.hpp"
#include "ipmi.hpp"
#include "process.hpp"
#include <blobs-ipmid/test/manager_mock.hpp>
#include <cstring>
#include <gtest/gtest.h>
namespace blobs
{
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::StrictMock;
// ipmid.hpp isn't installed where we can grab it and this value is per BMC
// SoC.
#define MAX_IPMI_BUFFER 64
namespace
{
void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs)
{
EXPECT_FALSE(lhs == nullptr);
EXPECT_FALSE(rhs == nullptr);
ipmi_ret_t (*const* lPtr)(ManagerInterface*, const uint8_t*, uint8_t*,
size_t*) =
lhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*,
size_t*)>();
ipmi_ret_t (*const* rPtr)(ManagerInterface*, const uint8_t*, uint8_t*,
size_t*) =
rhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*,
size_t*)>();
EXPECT_TRUE(lPtr);
EXPECT_TRUE(rPtr);
EXPECT_EQ(*lPtr, *rPtr);
return;
}
} // namespace
TEST(ValidateBlobCommandTest, InvalidCommandReturnsFailure)
{
// Verify we handle an invalid command.
StrictMock<CrcMock> crc;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
request[0] = 0xff; // There is no command 0xff.
dataLen = sizeof(uint8_t); // There is no payload for CRC.
ipmi_ret_t rc;
EXPECT_EQ(nullptr,
validateBlobCommand(&crc, request, reply, &dataLen, &rc));
EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST, rc);
}
TEST(ValidateBlobCommandTest, ValidCommandWithoutPayload)
{
// Verify we handle a valid command that doesn't have a payload.
StrictMock<CrcMock> crc;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
request[0] = BlobOEMCommands::bmcBlobGetCount;
dataLen = sizeof(uint8_t); // There is no payload for CRC.
ipmi_ret_t rc;
IpmiBlobHandler res =
validateBlobCommand(&crc, request, reply, &dataLen, &rc);
EXPECT_FALSE(res == nullptr);
EqualFunctions(getBlobCount, res);
}
TEST(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.
StrictMock<CrcMock> crc;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
request[0] = BlobOEMCommands::bmcBlobGetCount;
dataLen = sizeof(uint8_t) + sizeof(uint16_t);
// There is a payload, but there are insufficient bytes.
ipmi_ret_t rc;
EXPECT_EQ(nullptr,
validateBlobCommand(&crc, request, reply, &dataLen, &rc));
EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID, rc);
}
TEST(ValidateBlobCommandTest, WithPayloadAndInvalidCrc)
{
// Verify that the CRC is checked, and failure is reported.
StrictMock<CrcMock> crc;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
req->cmd = BlobOEMCommands::bmcBlobWrite;
req->crc = 0x34;
req->sessionId = 0x54;
req->offset = 0x100;
uint8_t expectedBytes[2] = {0x66, 0x67};
std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
// skip over cmd and crc.
size_t expectedLen = dataLen - 3;
EXPECT_CALL(crc, clear());
EXPECT_CALL(crc, compute(_, expectedLen))
.WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) {
EXPECT_EQ(0, std::memcmp(&request[3], bytes, length));
}));
EXPECT_CALL(crc, get()).WillOnce(Return(0x1234));
ipmi_ret_t rc;
EXPECT_EQ(nullptr,
validateBlobCommand(&crc, request, reply, &dataLen, &rc));
EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR, rc);
}
TEST(ValidateBlobCommandTest, WithPayloadAndValidCrc)
{
// Verify the CRC is checked and if it matches, return the handler.
StrictMock<CrcMock> crc;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
req->cmd = BlobOEMCommands::bmcBlobWrite;
req->crc = 0x3412;
req->sessionId = 0x54;
req->offset = 0x100;
uint8_t expectedBytes[2] = {0x66, 0x67};
std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
// skip over cmd and crc.
size_t expectedLen = dataLen - 3;
EXPECT_CALL(crc, clear());
EXPECT_CALL(crc, compute(_, expectedLen))
.WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) {
EXPECT_EQ(0, std::memcmp(&request[3], bytes, length));
}));
EXPECT_CALL(crc, get()).WillOnce(Return(0x3412));
ipmi_ret_t rc;
IpmiBlobHandler res =
validateBlobCommand(&crc, request, reply, &dataLen, &rc);
EXPECT_FALSE(res == nullptr);
EqualFunctions(writeBlob, res);
}
TEST(ValidateBlobCommandTest, InputIntegrationTest)
{
// Given a request buffer generated by the host-side utility, verify it is
// properly routed.
Crc16 crc;
size_t dataLen;
uint8_t request[] = {0x02, 0x88, 0x21, 0x03, 0x00, 0x2f, 0x64, 0x65, 0x76,
0x2f, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2f, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73,
0x73, 0x74, 0x68, 0x72, 0x75, 0x00};
// The above request to open a file for reading & writing named:
// "/dev/haven/command_passthru"
uint8_t reply[MAX_IPMI_BUFFER] = {0};
dataLen = sizeof(request);
ipmi_ret_t rc;
IpmiBlobHandler res =
validateBlobCommand(&crc, request, reply, &dataLen, &rc);
EXPECT_FALSE(res == nullptr);
EqualFunctions(openBlob, res);
}
TEST(ProcessBlobCommandTest, CommandReturnsNotOk)
{
// Verify that if the IPMI command handler returns not OK that this is
// noticed and returned.
StrictMock<CrcMock> crc;
StrictMock<ManagerMock> manager;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf,
size_t* dataLen) { return IPMI_CC_INVALID; };
dataLen = sizeof(request);
EXPECT_EQ(IPMI_CC_INVALID,
processBlobCommand(h, &manager, &crc, request, reply, &dataLen));
}
TEST(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload)
{
// Verify that if the IPMI command handler returns OK but without a payload
// it doesn't try to compute a CRC.
StrictMock<CrcMock> crc;
StrictMock<ManagerMock> manager;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen) {
(*dataLen) = 0;
return IPMI_CC_OK;
};
dataLen = sizeof(request);
EXPECT_EQ(IPMI_CC_OK,
processBlobCommand(h, &manager, &crc, request, reply, &dataLen));
}
TEST(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength)
{
// There is a minimum payload length of 3 bytes, this command returns a
// payload of 2 bytes.
StrictMock<CrcMock> crc;
StrictMock<ManagerMock> manager;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen) {
(*dataLen) = sizeof(uint16_t);
return IPMI_CC_OK;
};
dataLen = sizeof(request);
EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
processBlobCommand(h, &manager, &crc, request, reply, &dataLen));
}
TEST(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<CrcMock> crc;
StrictMock<ManagerMock> manager;
size_t dataLen;
uint8_t request[MAX_IPMI_BUFFER] = {0};
uint8_t reply[MAX_IPMI_BUFFER] = {0};
uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
IpmiBlobHandler h = [payloadLen](ManagerInterface* mgr,
const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen) {
(*dataLen) = payloadLen;
replyCmdBuf[2] = 0x56;
return IPMI_CC_OK;
};
dataLen = sizeof(request);
EXPECT_CALL(crc, clear());
EXPECT_CALL(crc, compute(_, payloadLen - sizeof(uint16_t)));
EXPECT_CALL(crc, get()).WillOnce(Return(0x3412));
EXPECT_EQ(IPMI_CC_OK,
processBlobCommand(h, &manager, &crc, request, reply, &dataLen));
EXPECT_EQ(dataLen, payloadLen);
uint8_t expectedBytes[3] = {0x12, 0x34, 0x56};
EXPECT_EQ(0, std::memcmp(expectedBytes, reply, sizeof(expectedBytes)));
}
} // namespace blobs