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