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