#include "crc.hpp" #include "crc_mock.hpp" #include "ipmi.hpp" #include "manager_mock.hpp" #include "process.hpp" #include #include 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 (*const* rPtr)(ManagerInterface*, const uint8_t*, uint8_t*, size_t*) = rhs.target(); EXPECT_TRUE(lPtr); EXPECT_TRUE(rPtr); EXPECT_EQ(*lPtr, *rPtr); return; } } // namespace TEST(ValidateBlobCommandTest, InvalidCommandReturnsFailure) { // Verify we handle an invalid command. StrictMock 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. EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); } TEST(ValidateBlobCommandTest, ValidCommandWithoutPayload) { // Verify we handle a valid command that doesn't have a payload. StrictMock 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. IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 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 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. EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); } TEST(ValidateBlobCommandTest, WithPayloadAndInvalidCrc) { // Verify that the CRC is checked, and failure is reported. StrictMock crc; size_t dataLen; uint8_t request[MAX_IPMI_BUFFER] = {0}; uint8_t reply[MAX_IPMI_BUFFER] = {0}; auto req = reinterpret_cast(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)); EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); } TEST(ValidateBlobCommandTest, WithPayloadAndValidCrc) { // Verify the CRC is checked and if it matches, return the handler. StrictMock crc; size_t dataLen; uint8_t request[MAX_IPMI_BUFFER] = {0}; uint8_t reply[MAX_IPMI_BUFFER] = {0}; auto req = reinterpret_cast(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)); IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 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); IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 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 crc; StrictMock 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 crc; StrictMock 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 crc; StrictMock 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_INVALID, 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 crc; StrictMock 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)); 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