blob: bd1f88610204f27c4037c6821f29e3efe5e06df2 [file] [log] [blame]
Patrick Ventureef3aead2018-09-12 08:53:29 -07001#include "crc.hpp"
2#include "crc_mock.hpp"
3#include "ipmi.hpp"
Patrick Venturecd8dab42019-01-15 19:57:38 -08004#include "manager_mock.hpp"
Patrick Ventureef3aead2018-09-12 08:53:29 -07005#include "process.hpp"
6
Patrick Ventureef3aead2018-09-12 08:53:29 -07007#include <cstring>
8
9#include <gtest/gtest.h>
10
11namespace blobs
12{
13
14using ::testing::_;
15using ::testing::Invoke;
16using ::testing::Return;
17using ::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
23namespace
24{
25
26void 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
49TEST(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.
Patrick Venture41258802018-11-12 10:46:30 -080060 ipmi_ret_t rc;
Patrick Ventureef3aead2018-09-12 08:53:29 -070061
Patrick Venture41258802018-11-12 10:46:30 -080062 EXPECT_EQ(nullptr,
63 validateBlobCommand(&crc, request, reply, &dataLen, &rc));
64 EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST, rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -070065}
66
67TEST(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.
Patrick Venture41258802018-11-12 10:46:30 -080078 ipmi_ret_t rc;
Patrick Ventureef3aead2018-09-12 08:53:29 -070079
Patrick Venture41258802018-11-12 10:46:30 -080080 IpmiBlobHandler res =
81 validateBlobCommand(&crc, request, reply, &dataLen, &rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -070082 EXPECT_FALSE(res == nullptr);
83 EqualFunctions(getBlobCount, res);
84}
85
86TEST(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.
Patrick Venture41258802018-11-12 10:46:30 -080099 ipmi_ret_t rc;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700100
Patrick Venture41258802018-11-12 10:46:30 -0800101 EXPECT_EQ(nullptr,
102 validateBlobCommand(&crc, request, reply, &dataLen, &rc));
103 EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID, rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700104}
105
106TEST(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
Patrick Venture41258802018-11-12 10:46:30 -0800136 ipmi_ret_t rc;
137
138 EXPECT_EQ(nullptr,
139 validateBlobCommand(&crc, request, reply, &dataLen, &rc));
140 EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR, rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700141}
142
143TEST(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
Patrick Venture41258802018-11-12 10:46:30 -0800173 ipmi_ret_t rc;
174
175 IpmiBlobHandler res =
176 validateBlobCommand(&crc, request, reply, &dataLen, &rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700177 EXPECT_FALSE(res == nullptr);
178 EqualFunctions(writeBlob, res);
179}
180
181TEST(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);
Patrick Venture41258802018-11-12 10:46:30 -0800199 ipmi_ret_t rc;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700200
Patrick Venture41258802018-11-12 10:46:30 -0800201 IpmiBlobHandler res =
202 validateBlobCommand(&crc, request, reply, &dataLen, &rc);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700203 EXPECT_FALSE(res == nullptr);
204 EqualFunctions(openBlob, res);
205}
206
207TEST(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
228TEST(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
251TEST(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength)
252{
Patrick Ventured1c3e862019-01-10 13:12:20 -0800253 // There is a minimum payload length of 2 bytes (the CRC only, no data, for
254 // read), this returns 1.
Patrick Ventureef3aead2018-09-12 08:53:29 -0700255
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) {
Patrick Ventured1c3e862019-01-10 13:12:20 -0800264 (*dataLen) = sizeof(uint8_t);
Patrick Ventureef3aead2018-09-12 08:53:29 -0700265 return IPMI_CC_OK;
266 };
267
268 dataLen = sizeof(request);
269
Patrick Venture41258802018-11-12 10:46:30 -0800270 EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
Patrick Ventureef3aead2018-09-12 08:53:29 -0700271 processBlobCommand(h, &manager, &crc, request, reply, &dataLen));
272}
273
274TEST(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());
Patrick Ventureebf7b9b2018-11-13 12:52:30 -0800297 EXPECT_CALL(crc, compute(_, payloadLen - sizeof(uint16_t)));
Patrick Ventureef3aead2018-09-12 08:53:29 -0700298 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