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 61 EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); 62 } 63 64 TEST(ValidateBlobCommandTest, ValidCommandWithoutPayload) 65 { 66 // Verify we handle a valid command that doesn't have a payload. 67 68 StrictMock<CrcMock> crc; 69 size_t dataLen; 70 uint8_t request[MAX_IPMI_BUFFER] = {0}; 71 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 72 73 request[0] = BlobOEMCommands::bmcBlobGetCount; 74 dataLen = sizeof(uint8_t); // There is no payload for CRC. 75 76 IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 77 EXPECT_FALSE(res == nullptr); 78 EqualFunctions(getBlobCount, res); 79 } 80 81 TEST(ValidateBlobCommandTest, WithPayloadMinimumLengthIs3VerifyChecks) 82 { 83 // Verify that if there's a payload, it's at least one command byte and 84 // two bytes for the crc16 and then one data byte. 85 86 StrictMock<CrcMock> crc; 87 size_t dataLen; 88 uint8_t request[MAX_IPMI_BUFFER] = {0}; 89 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 90 91 request[0] = BlobOEMCommands::bmcBlobGetCount; 92 dataLen = sizeof(uint8_t) + sizeof(uint16_t); 93 // There is a payload, but there are insufficient bytes. 94 95 EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); 96 } 97 98 TEST(ValidateBlobCommandTest, WithPayloadAndInvalidCrc) 99 { 100 // Verify that the CRC is checked, and failure is reported. 101 102 StrictMock<CrcMock> crc; 103 size_t dataLen; 104 uint8_t request[MAX_IPMI_BUFFER] = {0}; 105 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 106 107 auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); 108 req->cmd = BlobOEMCommands::bmcBlobWrite; 109 req->crc = 0x34; 110 req->sessionId = 0x54; 111 req->offset = 0x100; 112 113 uint8_t expectedBytes[2] = {0x66, 0x67}; 114 std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); 115 116 dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); 117 118 // skip over cmd and crc. 119 size_t expectedLen = dataLen - 3; 120 121 EXPECT_CALL(crc, clear()); 122 EXPECT_CALL(crc, compute(_, expectedLen)) 123 .WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) { 124 EXPECT_EQ(0, std::memcmp(&request[3], bytes, length)); 125 })); 126 EXPECT_CALL(crc, get()).WillOnce(Return(0x1234)); 127 128 EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); 129 } 130 131 TEST(ValidateBlobCommandTest, WithPayloadAndValidCrc) 132 { 133 // Verify the CRC is checked and if it matches, return the handler. 134 135 StrictMock<CrcMock> crc; 136 size_t dataLen; 137 uint8_t request[MAX_IPMI_BUFFER] = {0}; 138 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 139 140 auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); 141 req->cmd = BlobOEMCommands::bmcBlobWrite; 142 req->crc = 0x3412; 143 req->sessionId = 0x54; 144 req->offset = 0x100; 145 146 uint8_t expectedBytes[2] = {0x66, 0x67}; 147 std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); 148 149 dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); 150 151 // skip over cmd and crc. 152 size_t expectedLen = dataLen - 3; 153 154 EXPECT_CALL(crc, clear()); 155 EXPECT_CALL(crc, compute(_, expectedLen)) 156 .WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) { 157 EXPECT_EQ(0, std::memcmp(&request[3], bytes, length)); 158 })); 159 EXPECT_CALL(crc, get()).WillOnce(Return(0x3412)); 160 161 IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 162 EXPECT_FALSE(res == nullptr); 163 EqualFunctions(writeBlob, res); 164 } 165 166 TEST(ValidateBlobCommandTest, InputIntegrationTest) 167 { 168 // Given a request buffer generated by the host-side utility, verify it is 169 // properly routed. 170 171 Crc16 crc; 172 size_t dataLen; 173 uint8_t request[] = {0x02, 0x88, 0x21, 0x03, 0x00, 0x2f, 0x64, 0x65, 0x76, 174 0x2f, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 175 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, 176 0x73, 0x74, 0x68, 0x72, 0x75, 0x00}; 177 178 // The above request to open a file for reading & writing named: 179 // "/dev/haven/command_passthru" 180 181 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 182 183 dataLen = sizeof(request); 184 185 IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); 186 EXPECT_FALSE(res == nullptr); 187 EqualFunctions(openBlob, res); 188 } 189 190 TEST(ProcessBlobCommandTest, CommandReturnsNotOk) 191 { 192 // Verify that if the IPMI command handler returns not OK that this is 193 // noticed and returned. 194 195 StrictMock<CrcMock> crc; 196 StrictMock<ManagerMock> manager; 197 size_t dataLen; 198 uint8_t request[MAX_IPMI_BUFFER] = {0}; 199 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 200 201 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 202 uint8_t* replyCmdBuf, 203 size_t* dataLen) { return IPMI_CC_INVALID; }; 204 205 dataLen = sizeof(request); 206 207 EXPECT_EQ(IPMI_CC_INVALID, 208 processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); 209 } 210 211 TEST(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload) 212 { 213 // Verify that if the IPMI command handler returns OK but without a payload 214 // it doesn't try to compute a CRC. 215 216 StrictMock<CrcMock> crc; 217 StrictMock<ManagerMock> manager; 218 size_t dataLen; 219 uint8_t request[MAX_IPMI_BUFFER] = {0}; 220 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 221 222 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 223 uint8_t* replyCmdBuf, size_t* dataLen) { 224 (*dataLen) = 0; 225 return IPMI_CC_OK; 226 }; 227 228 dataLen = sizeof(request); 229 230 EXPECT_EQ(IPMI_CC_OK, 231 processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); 232 } 233 234 TEST(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength) 235 { 236 // There is a minimum payload length of 3 bytes, this command returns a 237 // payload of 2 bytes. 238 239 StrictMock<CrcMock> crc; 240 StrictMock<ManagerMock> manager; 241 size_t dataLen; 242 uint8_t request[MAX_IPMI_BUFFER] = {0}; 243 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 244 245 IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, 246 uint8_t* replyCmdBuf, size_t* dataLen) { 247 (*dataLen) = sizeof(uint16_t); 248 return IPMI_CC_OK; 249 }; 250 251 dataLen = sizeof(request); 252 253 EXPECT_EQ(IPMI_CC_INVALID, 254 processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); 255 } 256 257 TEST(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength) 258 { 259 // There is a minimum payload length of 3 bytes, this command returns a 260 // payload of 3 bytes and the crc code is called to process the payload. 261 262 StrictMock<CrcMock> crc; 263 StrictMock<ManagerMock> manager; 264 size_t dataLen; 265 uint8_t request[MAX_IPMI_BUFFER] = {0}; 266 uint8_t reply[MAX_IPMI_BUFFER] = {0}; 267 uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t); 268 269 IpmiBlobHandler h = [payloadLen](ManagerInterface* mgr, 270 const uint8_t* reqBuf, 271 uint8_t* replyCmdBuf, size_t* dataLen) { 272 (*dataLen) = payloadLen; 273 replyCmdBuf[2] = 0x56; 274 return IPMI_CC_OK; 275 }; 276 277 dataLen = sizeof(request); 278 279 EXPECT_CALL(crc, clear()); 280 EXPECT_CALL(crc, compute(_, payloadLen)); 281 EXPECT_CALL(crc, get()).WillOnce(Return(0x3412)); 282 283 EXPECT_EQ(IPMI_CC_OK, 284 processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); 285 EXPECT_EQ(dataLen, payloadLen); 286 287 uint8_t expectedBytes[3] = {0x12, 0x34, 0x56}; 288 EXPECT_EQ(0, std::memcmp(expectedBytes, reply, sizeof(expectedBytes))); 289 } 290 } // namespace blobs 291