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