1 #include "ipmi.hpp" 2 #include "manager_mock.hpp" 3 #include "process.hpp" 4 5 #include <cstring> 6 #include <ipmiblob/test/crc_mock.hpp> 7 8 #include <gtest/gtest.h> 9 10 // ipmid.hpp isn't installed where we can grab it and this value is per BMC 11 // SoC. 12 #define MAX_IPMI_BUFFER 64 13 14 using ::testing::_; 15 using ::testing::Eq; 16 using ::testing::Invoke; 17 using ::testing::Return; 18 using ::testing::StrictMock; 19 20 namespace ipmiblob 21 { 22 CrcInterface* crcIntf = nullptr; 23 24 std::uint16_t generateCrc(const std::vector<std::uint8_t>& data) 25 { 26 return (crcIntf) ? crcIntf->generateCrc(data) : 0x00; 27 } 28 } // namespace ipmiblob 29 30 namespace blobs 31 { 32 namespace 33 { 34 35 void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs) 36 { 37 EXPECT_FALSE(lhs == nullptr); 38 EXPECT_FALSE(rhs == nullptr); 39 40 ipmi_ret_t (*const* lPtr)(ManagerInterface*, const uint8_t*, uint8_t*, 41 size_t*) = 42 lhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*, 43 size_t*)>(); 44 45 ipmi_ret_t (*const* rPtr)(ManagerInterface*, const uint8_t*, uint8_t*, 46 size_t*) = 47 rhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*, 48 size_t*)>(); 49 50 EXPECT_TRUE(lPtr); 51 EXPECT_TRUE(rPtr); 52 EXPECT_EQ(*lPtr, *rPtr); 53 return; 54 } 55 56 } // namespace 57 58 class ValidateBlobCommandTest : public ::testing::Test 59 { 60 protected: 61 void SetUp() override 62 { 63 ipmiblob::crcIntf = &crcMock; 64 } 65 66 ipmiblob::CrcMock crcMock; 67 }; 68 69 TEST_F(ValidateBlobCommandTest, InvalidCommandReturnsFailure) 70 { 71 // Verify we handle an invalid command. 72 73 size_t dataLen; 74 uint8_t request[MAX_IPMI_BUFFER] = {0}; 75 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 76 77 request[0] = 0xff; // There is no command 0xff. 78 dataLen = sizeof(uint8_t); // There is no payload for CRC. 79 ipmi_ret_t rc; 80 81 EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc)); 82 EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST, rc); 83 } 84 85 TEST_F(ValidateBlobCommandTest, ValidCommandWithoutPayload) 86 { 87 // Verify we handle a valid command that doesn't have a payload. 88 89 size_t dataLen; 90 uint8_t request[MAX_IPMI_BUFFER] = {0}; 91 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 92 93 request[0] = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount); 94 dataLen = sizeof(uint8_t); // There is no payload for CRC. 95 ipmi_ret_t rc; 96 97 IpmiBlobHandler res = validateBlobCommand(request, reply, &dataLen, &rc); 98 EXPECT_FALSE(res == nullptr); 99 EqualFunctions(getBlobCount, res); 100 } 101 102 TEST_F(ValidateBlobCommandTest, WithPayloadMinimumLengthIs3VerifyChecks) 103 { 104 // Verify that if there's a payload, it's at least one command byte and 105 // two bytes for the crc16 and then one data byte. 106 107 size_t dataLen; 108 uint8_t request[MAX_IPMI_BUFFER] = {0}; 109 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 110 111 request[0] = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount); 112 dataLen = sizeof(uint8_t) + sizeof(uint16_t); 113 // There is a payload, but there are insufficient bytes. 114 ipmi_ret_t rc; 115 116 EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc)); 117 EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID, rc); 118 } 119 120 TEST_F(ValidateBlobCommandTest, WithPayloadAndInvalidCrc) 121 { 122 // Verify that the CRC is checked, and failure is reported. 123 124 size_t dataLen; 125 uint8_t request[MAX_IPMI_BUFFER] = {0}; 126 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 127 128 auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); 129 req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite); 130 req->crc = 0x34; 131 req->sessionId = 0x54; 132 req->offset = 0x100; 133 134 uint8_t expectedBytes[2] = {0x66, 0x67}; 135 std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); 136 137 dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); 138 139 // skip over cmd and crc. 140 std::vector<std::uint8_t> bytes(&request[3], request + dataLen); 141 EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x1234)); 142 143 ipmi_ret_t rc; 144 145 EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc)); 146 EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR, rc); 147 } 148 149 TEST_F(ValidateBlobCommandTest, WithPayloadAndValidCrc) 150 { 151 // Verify the CRC is checked and if it matches, return the handler. 152 153 size_t dataLen; 154 uint8_t request[MAX_IPMI_BUFFER] = {0}; 155 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 156 157 auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); 158 req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite); 159 req->crc = 0x3412; 160 req->sessionId = 0x54; 161 req->offset = 0x100; 162 163 uint8_t expectedBytes[2] = {0x66, 0x67}; 164 std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); 165 166 dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); 167 168 // skip over cmd and crc. 169 std::vector<std::uint8_t> bytes(&request[3], request + dataLen); 170 EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x3412)); 171 172 ipmi_ret_t rc; 173 174 IpmiBlobHandler res = validateBlobCommand(request, reply, &dataLen, &rc); 175 EXPECT_FALSE(res == nullptr); 176 EqualFunctions(writeBlob, res); 177 } 178 179 class ProcessBlobCommandTest : public ::testing::Test 180 { 181 protected: 182 void SetUp() override 183 { 184 ipmiblob::crcIntf = &crcMock; 185 } 186 187 ipmiblob::CrcMock crcMock; 188 }; 189 190 TEST_F(ProcessBlobCommandTest, CommandReturnsNotOk) 191 { 192 // Verify that if the IPMI command handler returns not OK that this is 193 // noticed and returned. 194 195 StrictMock<ManagerMock> manager; 196 size_t dataLen; 197 uint8_t request[MAX_IPMI_BUFFER] = {0}; 198 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 199 200 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 201 uint8_t* replyCmdBuf, 202 size_t* dataLen) { return IPMI_CC_INVALID; }; 203 204 dataLen = sizeof(request); 205 206 EXPECT_EQ(IPMI_CC_INVALID, 207 processBlobCommand(h, &manager, request, reply, &dataLen)); 208 } 209 210 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload) 211 { 212 // Verify that if the IPMI command handler returns OK but without a payload 213 // it doesn't try to compute a CRC. 214 215 StrictMock<ManagerMock> manager; 216 size_t dataLen; 217 uint8_t request[MAX_IPMI_BUFFER] = {0}; 218 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 219 220 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 221 uint8_t* replyCmdBuf, size_t* dataLen) { 222 (*dataLen) = 0; 223 return IPMI_CC_OK; 224 }; 225 226 dataLen = sizeof(request); 227 228 EXPECT_EQ(IPMI_CC_OK, 229 processBlobCommand(h, &manager, request, reply, &dataLen)); 230 } 231 232 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength) 233 { 234 // There is a minimum payload length of 2 bytes (the CRC only, no data, for 235 // read), this returns 1. 236 237 StrictMock<ManagerMock> manager; 238 size_t dataLen; 239 uint8_t request[MAX_IPMI_BUFFER] = {0}; 240 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 241 242 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 243 uint8_t* replyCmdBuf, size_t* dataLen) { 244 (*dataLen) = sizeof(uint8_t); 245 return IPMI_CC_OK; 246 }; 247 248 dataLen = sizeof(request); 249 250 EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR, 251 processBlobCommand(h, &manager, request, reply, &dataLen)); 252 } 253 254 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength) 255 { 256 // There is a minimum payload length of 3 bytes, this command returns a 257 // payload of 3 bytes and the crc code is called to process the payload. 258 259 StrictMock<ManagerMock> manager; 260 size_t dataLen; 261 uint8_t request[MAX_IPMI_BUFFER] = {0}; 262 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 263 uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t); 264 265 IpmiBlobHandler h = [payloadLen](ManagerInterface* mgr, 266 const uint8_t* reqBuf, 267 uint8_t* replyCmdBuf, size_t* dataLen) { 268 (*dataLen) = payloadLen; 269 replyCmdBuf[2] = 0x56; 270 return IPMI_CC_OK; 271 }; 272 273 dataLen = sizeof(request); 274 275 EXPECT_CALL(crcMock, generateCrc(_)).WillOnce(Return(0x3412)); 276 277 EXPECT_EQ(IPMI_CC_OK, 278 processBlobCommand(h, &manager, request, reply, &dataLen)); 279 EXPECT_EQ(dataLen, payloadLen); 280 281 uint8_t expectedBytes[3] = {0x12, 0x34, 0x56}; 282 EXPECT_EQ(0, std::memcmp(expectedBytes, reply, sizeof(expectedBytes))); 283 } 284 } // namespace blobs 285