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