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