blob: 2ffb023c6cf0cf2ca0c0d310b064fb742493f197 [file] [log] [blame]
Patrick Ventureef3aead2018-09-12 08:53:29 -07001#include "crc.hpp"
2#include "crc_mock.hpp"
3#include "ipmi.hpp"
4#include "manager_mock.hpp"
5#include "process.hpp"
6
7#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.
60
61 EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen));
62}
63
64TEST(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
81TEST(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
98TEST(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
131TEST(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
166TEST(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
190TEST(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
211TEST(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
234TEST(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
257TEST(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