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