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