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