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