1 #include "helper.hpp" 2 #include "ipmi.hpp" 3 #include "manager_mock.hpp" 4 #include "process.hpp" 5 6 #include <ipmiblob/test/crc_mock.hpp> 7 8 #include <cstring> 9 #include <span> 10 11 #include <gtest/gtest.h> 12 13 // ipmid.hpp isn't installed where we can grab it and this value is per BMC 14 // SoC. 15 #define MAX_IPMI_BUFFER 64 16 17 using ::testing::_; 18 using ::testing::ElementsAre; 19 using ::testing::Eq; 20 using ::testing::Return; 21 using ::testing::StrictMock; 22 23 namespace ipmiblob 24 { 25 CrcInterface* crcIntf = nullptr; 26 27 std::uint16_t generateCrc(const std::vector<std::uint8_t>& data) 28 { 29 return (crcIntf) ? crcIntf->generateCrc(data) : 0x00; 30 } 31 } // namespace ipmiblob 32 33 namespace blobs 34 { 35 namespace 36 { 37 38 void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs) 39 { 40 EXPECT_FALSE(lhs == nullptr); 41 EXPECT_FALSE(rhs == nullptr); 42 43 Resp (*const* lPtr)(ManagerInterface*, std::span<const uint8_t>) = 44 lhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>(); 45 46 Resp (*const* rPtr)(ManagerInterface*, std::span<const uint8_t>) = 47 rhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_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 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 71 // There is no command 0xff. 72 IpmiBlobHandler handler = validateBlobCommand(0xff, request); 73 EXPECT_EQ(ipmi::responseInvalidFieldRequest(), handler(nullptr, {})); 74 } 75 76 TEST_F(ValidateBlobCommandTest, ValidCommandWithoutPayload) 77 { 78 // Verify we handle a valid command that doesn't have a payload. 79 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 80 IpmiBlobHandler handler = validateBlobCommand( 81 static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request); 82 EXPECT_FALSE(handler == nullptr); 83 EqualFunctions(getBlobCount, handler); 84 } 85 86 TEST_F(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 std::vector<uint8_t> request(sizeof(uint16_t)); 92 // There is a payload, but there are insufficient bytes. 93 94 IpmiBlobHandler handler = validateBlobCommand( 95 static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request); 96 EXPECT_EQ(ipmi::responseReqDataLenInvalid(), handler(nullptr, {})); 97 } 98 99 TEST_F(ValidateBlobCommandTest, WithPayloadAndInvalidCrc) 100 { 101 // Verify that the CRC is checked, and failure is reported. 102 std::vector<uint8_t> request; 103 BmcBlobWriteTx req; 104 req.crc = 0x34; 105 req.sessionId = 0x54; 106 req.offset = 0x100; 107 108 std::array<uint8_t, 2> expectedBytes = {0x66, 0x67}; 109 request.resize(sizeof(struct BmcBlobWriteTx)); 110 std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx)); 111 request.insert(request.end(), expectedBytes.begin(), expectedBytes.end()); 112 113 // skip over cmd and crc. 114 std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc), 115 request.end()); 116 EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x1234)); 117 118 IpmiBlobHandler handler = validateBlobCommand( 119 static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request); 120 EXPECT_EQ(ipmi::responseUnspecifiedError(), handler(nullptr, {})); 121 } 122 123 TEST_F(ValidateBlobCommandTest, WithPayloadAndValidCrc) 124 { 125 // Verify the CRC is checked and if it matches, return the handler. 126 std::vector<uint8_t> request; 127 BmcBlobWriteTx req; 128 req.crc = 0x3412; 129 req.sessionId = 0x54; 130 req.offset = 0x100; 131 132 std::array<uint8_t, 2> expectedBytes = {0x66, 0x67}; 133 request.resize(sizeof(struct BmcBlobWriteTx)); 134 std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx)); 135 request.insert(request.end(), expectedBytes.begin(), expectedBytes.end()); 136 137 // skip over cmd and crc. 138 std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc), 139 request.end()); 140 EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x3412)); 141 142 IpmiBlobHandler handler = validateBlobCommand( 143 static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request); 144 EXPECT_FALSE(handler == nullptr); 145 EqualFunctions(writeBlob, handler); 146 } 147 148 class ProcessBlobCommandTest : public ::testing::Test 149 { 150 protected: 151 void SetUp() override 152 { 153 ipmiblob::crcIntf = &crcMock; 154 } 155 156 ipmiblob::CrcMock crcMock; 157 }; 158 159 TEST_F(ProcessBlobCommandTest, CommandReturnsNotOk) 160 { 161 // Verify that if the IPMI command handler returns not OK that this is 162 // noticed and returned. 163 164 StrictMock<ManagerMock> manager; 165 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 166 167 IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) { 168 return ipmi::responseInvalidCommand(); 169 }; 170 171 EXPECT_EQ(ipmi::responseInvalidCommand(), 172 processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER)); 173 } 174 175 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload) 176 { 177 // Verify that if the IPMI command handler returns OK but without a payload 178 // it doesn't try to compute a CRC. 179 180 StrictMock<ManagerMock> manager; 181 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 182 183 IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) { 184 return ipmi::responseSuccess(std::vector<uint8_t>()); 185 }; 186 187 EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>()), 188 processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER)); 189 } 190 191 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength) 192 { 193 // There is a minimum payload length of 2 bytes (the CRC only, no data, for 194 // read), this returns 1. 195 196 StrictMock<ManagerMock> manager; 197 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 198 199 IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) { 200 return ipmi::responseSuccess(std::vector<uint8_t>(1)); 201 }; 202 203 EXPECT_EQ(ipmi::responseUnspecifiedError(), 204 processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER)); 205 } 206 207 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength) 208 { 209 // There is a minimum payload length of 3 bytes, this command returns a 210 // payload of 3 bytes and the crc code is called to process the payload. 211 212 StrictMock<ManagerMock> manager; 213 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 214 uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t); 215 216 IpmiBlobHandler h = 217 [payloadLen](ManagerInterface*, std::span<const uint8_t>) { 218 std::vector<uint8_t> output(payloadLen, 0); 219 output[2] = 0x56; 220 return ipmi::responseSuccess(output); 221 }; 222 223 EXPECT_CALL(crcMock, generateCrc(_)).WillOnce(Return(0x3412)); 224 225 auto result = validateReply( 226 processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER)); 227 228 EXPECT_EQ(result.size(), payloadLen); 229 EXPECT_THAT(result, ElementsAre(0x12, 0x34, 0x56)); 230 } 231 232 TEST_F(ProcessBlobCommandTest, 233 CommandReturnsErrorWithReplyExceededMaxTransferSize) 234 { 235 // There is a minimum payload length of 3 bytes, this command returns a 236 // payload of 3 bytes and the crc code is called to process the payload. 237 238 StrictMock<ManagerMock> manager; 239 std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1); 240 uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t); 241 242 IpmiBlobHandler h = 243 [payloadLen](ManagerInterface*, std::span<const uint8_t>) { 244 std::vector<uint8_t> output(payloadLen, 0); 245 output[2] = 0x56; 246 return ipmi::responseSuccess(output); 247 }; 248 249 EXPECT_EQ(ipmi::responseResponseError(), 250 processBlobCommand(h, &manager, request, 0)); 251 } 252 } // namespace blobs 253