1 #include "helper.hpp"
2 #include "ipmi.hpp"
3 #include "manager_mock.hpp"
4 #include "process.hpp"
5 
6 #include <ipmiblob/test/crc_mock.hpp>
7 
8 #include <cstring>
9 #include <span>
10 
11 #include <gtest/gtest.h>
12 
13 // ipmid.hpp isn't installed where we can grab it and this value is per BMC
14 // SoC.
15 #define MAX_IPMI_BUFFER 64
16 
17 using ::testing::_;
18 using ::testing::ElementsAre;
19 using ::testing::Eq;
20 using ::testing::Return;
21 using ::testing::StrictMock;
22 
23 namespace ipmiblob
24 {
25 CrcInterface* crcIntf = nullptr;
26 
generateCrc(const std::vector<std::uint8_t> & data)27 std::uint16_t generateCrc(const std::vector<std::uint8_t>& data)
28 {
29     return (crcIntf) ? crcIntf->generateCrc(data) : 0x00;
30 }
31 } // namespace ipmiblob
32 
33 namespace blobs
34 {
35 namespace
36 {
37 
EqualFunctions(IpmiBlobHandler lhs,IpmiBlobHandler rhs)38 void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs)
39 {
40     EXPECT_FALSE(lhs == nullptr);
41     EXPECT_FALSE(rhs == nullptr);
42 
43     Resp (*const* lPtr)(ManagerInterface*, std::span<const uint8_t>) =
44         lhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>();
45 
46     Resp (*const* rPtr)(ManagerInterface*, std::span<const uint8_t>) =
47         rhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_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:
SetUp()59     void SetUp() override
60     {
61         ipmiblob::crcIntf = &crcMock;
62     }
63 
64     ipmiblob::CrcMock crcMock;
65 };
66 
TEST_F(ValidateBlobCommandTest,InvalidCommandReturnsFailure)67 TEST_F(ValidateBlobCommandTest, InvalidCommandReturnsFailure)
68 {
69     // Verify we handle an invalid command.
70     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
71     // There is no command 0xff.
72     IpmiBlobHandler handler = validateBlobCommand(0xff, request);
73     EXPECT_EQ(ipmi::responseInvalidFieldRequest(), handler(nullptr, {}));
74 }
75 
TEST_F(ValidateBlobCommandTest,ValidCommandWithoutPayload)76 TEST_F(ValidateBlobCommandTest, ValidCommandWithoutPayload)
77 {
78     // Verify we handle a valid command that doesn't have a payload.
79     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
80     IpmiBlobHandler handler = validateBlobCommand(
81         static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
82     EXPECT_FALSE(handler == nullptr);
83     EqualFunctions(getBlobCount, handler);
84 }
85 
TEST_F(ValidateBlobCommandTest,WithPayloadMinimumLengthIs3VerifyChecks)86 TEST_F(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     std::vector<uint8_t> request(sizeof(uint16_t));
92     // There is a payload, but there are insufficient bytes.
93 
94     IpmiBlobHandler handler = validateBlobCommand(
95         static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
96     EXPECT_EQ(ipmi::responseReqDataLenInvalid(), handler(nullptr, {}));
97 }
98 
TEST_F(ValidateBlobCommandTest,WithPayloadAndInvalidCrc)99 TEST_F(ValidateBlobCommandTest, WithPayloadAndInvalidCrc)
100 {
101     // Verify that the CRC is checked, and failure is reported.
102     std::vector<uint8_t> request;
103     BmcBlobWriteTx req;
104     req.crc = 0x34;
105     req.sessionId = 0x54;
106     req.offset = 0x100;
107 
108     std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
109     request.resize(sizeof(struct BmcBlobWriteTx));
110     std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
111     request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
112 
113     // skip over cmd and crc.
114     std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
115                                request.end());
116     EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x1234));
117 
118     IpmiBlobHandler handler = validateBlobCommand(
119         static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
120     EXPECT_EQ(ipmi::responseUnspecifiedError(), handler(nullptr, {}));
121 }
122 
TEST_F(ValidateBlobCommandTest,WithPayloadAndValidCrc)123 TEST_F(ValidateBlobCommandTest, WithPayloadAndValidCrc)
124 {
125     // Verify the CRC is checked and if it matches, return the handler.
126     std::vector<uint8_t> request;
127     BmcBlobWriteTx req;
128     req.crc = 0x3412;
129     req.sessionId = 0x54;
130     req.offset = 0x100;
131 
132     std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
133     request.resize(sizeof(struct BmcBlobWriteTx));
134     std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
135     request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
136 
137     // skip over cmd and crc.
138     std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
139                                request.end());
140     EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x3412));
141 
142     IpmiBlobHandler handler = validateBlobCommand(
143         static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
144     EXPECT_FALSE(handler == nullptr);
145     EqualFunctions(writeBlob, handler);
146 }
147 
148 class ProcessBlobCommandTest : public ::testing::Test
149 {
150   protected:
SetUp()151     void SetUp() override
152     {
153         ipmiblob::crcIntf = &crcMock;
154     }
155 
156     ipmiblob::CrcMock crcMock;
157 };
158 
TEST_F(ProcessBlobCommandTest,CommandReturnsNotOk)159 TEST_F(ProcessBlobCommandTest, CommandReturnsNotOk)
160 {
161     // Verify that if the IPMI command handler returns not OK that this is
162     // noticed and returned.
163 
164     StrictMock<ManagerMock> manager;
165     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
166 
167     IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
168         return ipmi::responseInvalidCommand();
169     };
170 
171     EXPECT_EQ(ipmi::responseInvalidCommand(),
172               processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
173 }
174 
TEST_F(ProcessBlobCommandTest,CommandReturnsOkWithNoPayload)175 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload)
176 {
177     // Verify that if the IPMI command handler returns OK but without a payload
178     // it doesn't try to compute a CRC.
179 
180     StrictMock<ManagerMock> manager;
181     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
182 
183     IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
184         return ipmi::responseSuccess(std::vector<uint8_t>());
185     };
186 
187     EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>()),
188               processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
189 }
190 
TEST_F(ProcessBlobCommandTest,CommandReturnsOkWithInvalidPayloadLength)191 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength)
192 {
193     // There is a minimum payload length of 2 bytes (the CRC only, no data, for
194     // read), this returns 1.
195 
196     StrictMock<ManagerMock> manager;
197     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
198 
199     IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
200         return ipmi::responseSuccess(std::vector<uint8_t>(1));
201     };
202 
203     EXPECT_EQ(ipmi::responseUnspecifiedError(),
204               processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
205 }
206 
TEST_F(ProcessBlobCommandTest,CommandReturnsOkWithValidPayloadLength)207 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength)
208 {
209     // There is a minimum payload length of 3 bytes, this command returns a
210     // payload of 3 bytes and the crc code is called to process the payload.
211 
212     StrictMock<ManagerMock> manager;
213     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
214     uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
215 
216     IpmiBlobHandler h =
217         [payloadLen](ManagerInterface*, std::span<const uint8_t>) {
218             std::vector<uint8_t> output(payloadLen, 0);
219             output[2] = 0x56;
220             return ipmi::responseSuccess(output);
221         };
222 
223     EXPECT_CALL(crcMock, generateCrc(_)).WillOnce(Return(0x3412));
224 
225     auto result = validateReply(
226         processBlobCommand(h, &manager, request, MAX_IPMI_BUFFER));
227 
228     EXPECT_EQ(result.size(), payloadLen);
229     EXPECT_THAT(result, ElementsAre(0x12, 0x34, 0x56));
230 }
231 
TEST_F(ProcessBlobCommandTest,CommandReturnsErrorWithReplyExceededMaxTransferSize)232 TEST_F(ProcessBlobCommandTest,
233        CommandReturnsErrorWithReplyExceededMaxTransferSize)
234 {
235     // There is a minimum payload length of 3 bytes, this command returns a
236     // payload of 3 bytes and the crc code is called to process the payload.
237 
238     StrictMock<ManagerMock> manager;
239     std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
240     uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
241 
242     IpmiBlobHandler h =
243         [payloadLen](ManagerInterface*, std::span<const uint8_t>) {
244             std::vector<uint8_t> output(payloadLen, 0);
245             output[2] = 0x56;
246             return ipmi::responseSuccess(output);
247         };
248 
249     EXPECT_EQ(ipmi::responseResponseError(),
250               processBlobCommand(h, &manager, request, 0));
251 }
252 } // namespace blobs
253