blob: 362e264ab085764fad3dc898d7039221206d1b14 [file] [log] [blame]
Patrick Venture123b5c02019-03-05 14:01:00 -08001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "blob_handler.hpp"
18
19#include "blob_errors.hpp"
20#include "crc.hpp"
21#include "ipmi_errors.hpp"
Patrick Venture22fcc842019-05-13 09:06:24 -070022#include "ipmi_interface.hpp"
Patrick Venture123b5c02019-03-05 14:01:00 -080023
24#include <array>
Brandon Kimd3c06f92022-07-20 17:36:53 +000025#include <cinttypes>
Patrick Venture123b5c02019-03-05 14:01:00 -080026#include <cstring>
Brandon Kimee975082022-08-08 20:28:47 +000027#include <iterator>
Patrick Venture800f06d2019-05-14 14:43:58 -070028#include <limits>
Patrick Venture22fcc842019-05-13 09:06:24 -070029#include <memory>
Patrick Venture123b5c02019-03-05 14:01:00 -080030
Patrick Venture1470bec2019-03-06 07:33:12 -080031namespace ipmiblob
Patrick Venture123b5c02019-03-05 14:01:00 -080032{
33
34namespace
35{
36const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
37}
38
Patrick Venture22fcc842019-05-13 09:06:24 -070039std::unique_ptr<BlobInterface>
40 BlobHandler::CreateBlobHandler(std::unique_ptr<IpmiInterface> ipmi)
41{
42 return std::make_unique<BlobHandler>(std::move(ipmi));
43}
44
Patrick Williamsb80a0252024-08-16 15:21:31 -040045std::vector<std::uint8_t> BlobHandler::sendIpmiPayload(
46 BlobOEMCommands command, const std::vector<std::uint8_t>& payload)
Patrick Venture123b5c02019-03-05 14:01:00 -080047{
48 std::vector<std::uint8_t> request, reply, bytes;
49
50 std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
51 std::back_inserter(request));
Patrick Venture44474642019-05-20 19:11:04 -070052 request.push_back(static_cast<std::uint8_t>(command));
Patrick Venture123b5c02019-03-05 14:01:00 -080053
Patrick Venturebae76642020-11-16 14:02:39 -080054 if (!payload.empty())
Patrick Venture123b5c02019-03-05 14:01:00 -080055 {
56 /* Grow the vector to hold the bytes. */
57 request.reserve(request.size() + sizeof(std::uint16_t));
58
59 /* CRC required. */
60 std::uint16_t crc = generateCrc(payload);
61 auto src = reinterpret_cast<const std::uint8_t*>(&crc);
62
63 std::copy(src, src + sizeof(crc), std::back_inserter(request));
64
65 /* Copy the payload. */
66 std::copy(payload.begin(), payload.end(), std::back_inserter(request));
67 }
68
69 try
70 {
Patrick Venture958f1ce2019-05-31 17:07:25 -070071 reply = ipmi->sendPacket(ipmiOEMNetFn, ipmiOEMBlobCmd, request);
Patrick Venture123b5c02019-03-05 14:01:00 -080072 }
73 catch (const IpmiException& e)
74 {
75 throw BlobException(e.what());
76 }
77
78 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
79 * now.
80 */
Patrick Venturebae76642020-11-16 14:02:39 -080081 if (reply.empty())
Patrick Venture123b5c02019-03-05 14:01:00 -080082 {
83 return reply;
84 }
85
86 /* This cannot be a response because it's smaller than the smallest
87 * response.
88 */
89 if (reply.size() < ipmiPhosphorOen.size())
90 {
91 throw BlobException("Invalid response length");
92 }
93
94 /* Verify the OEN. */
95 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
96 ipmiPhosphorOen.size()) != 0)
97 {
98 throw BlobException("Invalid OEN received");
99 }
100
101 /* In this case there was no data, as there was no CRC. */
102 std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
103 if (reply.size() < headerSize)
104 {
105 return {};
106 }
107
108 /* Validate CRC. */
109 std::uint16_t crc;
110 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
111 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
112
Patrick Venture123b5c02019-03-05 14:01:00 -0800113 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
114
115 auto computed = generateCrc(bytes);
116 if (crc != computed)
117 {
118 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
119 crc, computed);
120 throw BlobException("Invalid CRC on received data.");
121 }
122
123 return bytes;
124}
125
126int BlobHandler::getBlobCount()
127{
128 std::uint32_t count;
129 try
130 {
131 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
132 if (resp.size() != sizeof(count))
133 {
134 return 0;
135 }
136
137 /* LE to LE (need to make this portable as some point. */
138 std::memcpy(&count, resp.data(), sizeof(count));
139 }
140 catch (const BlobException& b)
141 {
142 return 0;
143 }
144
Patrick Venture123b5c02019-03-05 14:01:00 -0800145 return count;
146}
147
148std::string BlobHandler::enumerateBlob(std::uint32_t index)
149{
150 std::vector<std::uint8_t> payload;
151 auto data = reinterpret_cast<const std::uint8_t*>(&index);
152
153 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
154
155 try
156 {
157 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
Patrick Venturebae76642020-11-16 14:02:39 -0800158 return (resp.empty()) ? ""
159 : std::string(&resp[0], &resp[resp.size() - 1]);
Patrick Venture123b5c02019-03-05 14:01:00 -0800160 }
161 catch (const BlobException& b)
162 {
163 return "";
164 }
165}
166
Patrick Venture8865e402019-05-14 13:29:10 -0700167void BlobHandler::commit(std::uint16_t session,
168 const std::vector<std::uint8_t>& bytes)
169{
170 std::vector<std::uint8_t> request;
171 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
172 std::copy(addrSession, addrSession + sizeof(session),
173 std::back_inserter(request));
174
175 /* You have one byte to describe the length. */
176 if (bytes.size() > std::numeric_limits<std::uint8_t>::max())
177 {
178 throw BlobException("Commit data length greater than 8-bit limit\n");
179 }
180
181 std::uint8_t length = static_cast<std::uint8_t>(bytes.size());
182 auto addrLength = reinterpret_cast<const std::uint8_t*>(&length);
183 std::copy(addrLength, addrLength + sizeof(length),
184 std::back_inserter(request));
185
186 std::copy(bytes.begin(), bytes.end(), std::back_inserter(request));
187
188 sendIpmiPayload(BlobOEMCommands::bmcBlobCommit, request);
189}
190
Patrick Venture123b5c02019-03-05 14:01:00 -0800191void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
192 std::uint32_t offset,
193 const std::vector<std::uint8_t>& bytes)
194{
195 std::vector<std::uint8_t> payload;
196
Patrick Williamsb80a0252024-08-16 15:21:31 -0400197 payload.reserve(
198 sizeof(std::uint16_t) + sizeof(std::uint32_t) + bytes.size());
Patrick Venture123b5c02019-03-05 14:01:00 -0800199
200 auto data = reinterpret_cast<const std::uint8_t*>(&session);
201 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
202
203 data = reinterpret_cast<const std::uint8_t*>(&offset);
204 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
205
206 std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
207
Brandon Kim0106f572019-10-18 10:13:55 -0700208 sendIpmiPayload(command, payload);
Patrick Venture123b5c02019-03-05 14:01:00 -0800209}
210
211void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
212 const std::vector<std::uint8_t>& bytes)
213{
Brandon Kim00f39b82019-10-18 11:00:00 -0700214 writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800215}
216
217void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
218 const std::vector<std::uint8_t>& bytes)
219{
Brandon Kim00f39b82019-10-18 11:00:00 -0700220 writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800221}
222
223std::vector<std::string> BlobHandler::getBlobList()
224{
225 std::vector<std::string> list;
226 int blobCount = getBlobCount();
227
228 for (int i = 0; i < blobCount; i++)
229 {
230 auto name = enumerateBlob(i);
231 /* Currently ignore failures. */
232 if (!name.empty())
233 {
234 list.push_back(name);
235 }
236 }
237
238 return list;
239}
240
Patrick Venture17186ae2019-05-06 10:30:55 -0700241StatResponse BlobHandler::statGeneric(BlobOEMCommands command,
242 const std::vector<std::uint8_t>& request)
Patrick Venture123b5c02019-03-05 14:01:00 -0800243{
244 StatResponse meta;
Brandon Kim714e3882020-06-22 12:36:32 -0700245 static constexpr std::size_t blobStateSize = sizeof(meta.blob_state);
246 static constexpr std::size_t metaSize = sizeof(meta.size);
247 static constexpr std::size_t metaOffset = blobStateSize + metaSize;
Patrick Williamsb80a0252024-08-16 15:21:31 -0400248 static constexpr std::size_t minRespSize =
249 metaOffset + sizeof(std::uint8_t);
Patrick Venture17186ae2019-05-06 10:30:55 -0700250 std::vector<std::uint8_t> resp;
Patrick Venture123b5c02019-03-05 14:01:00 -0800251
252 try
253 {
Patrick Venture17186ae2019-05-06 10:30:55 -0700254 resp = sendIpmiPayload(command, request);
Patrick Venture123b5c02019-03-05 14:01:00 -0800255 }
256 catch (const BlobException& b)
257 {
258 throw;
259 }
260
Brandon Kim714e3882020-06-22 12:36:32 -0700261 // Avoid out of bounds memcpy below
262 if (resp.size() < minRespSize)
263 {
264 std::fprintf(stderr,
Brandon Kimd3c06f92022-07-20 17:36:53 +0000265 "Invalid response length, Got %zu which is less than "
266 "minRespSize %zu\n",
Brandon Kim714e3882020-06-22 12:36:32 -0700267 resp.size(), minRespSize);
268 throw BlobException("Invalid response length");
269 }
270
271 std::memcpy(&meta.blob_state, &resp[0], blobStateSize);
272 std::memcpy(&meta.size, &resp[blobStateSize], metaSize);
273 std::uint8_t len = resp[metaOffset];
274
275 auto metaDataLength = resp.size() - minRespSize;
276 if (metaDataLength != len)
277 {
278 std::fprintf(stderr,
Brandon Kimd3c06f92022-07-20 17:36:53 +0000279 "Metadata length did not match actual length, Got %zu "
280 "which does not equal expected length %" PRIu8 "\n",
Brandon Kim714e3882020-06-22 12:36:32 -0700281 metaDataLength, len);
282 throw BlobException("Metadata length did not match actual length");
283 }
284
Patrick Venture123b5c02019-03-05 14:01:00 -0800285 if (len > 0)
286 {
Brandon Kim714e3882020-06-22 12:36:32 -0700287 meta.metadata.resize(len);
288 std::copy(resp.begin() + minRespSize, resp.end(),
289 meta.metadata.begin());
Patrick Venture123b5c02019-03-05 14:01:00 -0800290 }
291
292 return meta;
293}
294
Patrick Venture17186ae2019-05-06 10:30:55 -0700295StatResponse BlobHandler::getStat(const std::string& id)
296{
297 std::vector<std::uint8_t> name;
298 std::copy(id.begin(), id.end(), std::back_inserter(name));
299 name.push_back(0x00); /* need to add nul-terminator. */
300
301 return statGeneric(BlobOEMCommands::bmcBlobStat, name);
302}
303
Patrick Venture16a99a62019-05-03 17:21:30 -0700304StatResponse BlobHandler::getStat(std::uint16_t session)
305{
Patrick Venture16a99a62019-05-03 17:21:30 -0700306 std::vector<std::uint8_t> request;
307 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
308 std::copy(addrSession, addrSession + sizeof(session),
309 std::back_inserter(request));
310
Patrick Venture17186ae2019-05-06 10:30:55 -0700311 return statGeneric(BlobOEMCommands::bmcBlobSessionStat, request);
Patrick Venture16a99a62019-05-03 17:21:30 -0700312}
313
Patrick Venture123b5c02019-03-05 14:01:00 -0800314std::uint16_t BlobHandler::openBlob(const std::string& id,
315 std::uint16_t handlerFlags)
316{
317 std::uint16_t session;
318 std::vector<std::uint8_t> request, resp;
319 auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);
320
321 std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
322 std::back_inserter(request));
323 std::copy(id.begin(), id.end(), std::back_inserter(request));
324 request.push_back(0x00); /* need to add nul-terminator. */
325
326 try
327 {
328 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
329 }
330 catch (const BlobException& b)
331 {
332 throw;
333 }
334
335 if (resp.size() != sizeof(session))
336 {
337 throw BlobException("Did not receive session.");
338 }
339
340 std::memcpy(&session, resp.data(), sizeof(session));
341 return session;
342}
343
344void BlobHandler::closeBlob(std::uint16_t session)
345{
346 std::vector<std::uint8_t> request;
347 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
348 std::copy(addrSession, addrSession + sizeof(session),
349 std::back_inserter(request));
350
351 try
352 {
353 sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
354 }
355 catch (const BlobException& b)
356 {
357 std::fprintf(stderr, "Received failure on close: %s\n", b.what());
358 }
Patrick Venture123b5c02019-03-05 14:01:00 -0800359}
360
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700361bool BlobHandler::deleteBlob(const std::string& id)
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700362{
363 std::vector<std::uint8_t> name;
364 std::copy(id.begin(), id.end(), std::back_inserter(name));
365 name.push_back(0x00); /* need to add nul-terminator. */
366
367 try
368 {
369 sendIpmiPayload(BlobOEMCommands::bmcBlobDelete, name);
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700370 return true;
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700371 }
372 catch (const BlobException& b)
373 {
374 std::fprintf(stderr, "Received failure on delete: %s\n", b.what());
375 }
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700376 return false;
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700377}
378
Patrick Williamsb80a0252024-08-16 15:21:31 -0400379std::vector<std::uint8_t> BlobHandler::readBytes(
380 std::uint16_t session, std::uint32_t offset, std::uint32_t length)
Patrick Venture123b5c02019-03-05 14:01:00 -0800381{
382 std::vector<std::uint8_t> payload;
383
Patrick Williamsb80a0252024-08-16 15:21:31 -0400384 payload.reserve(
385 sizeof(std::uint16_t) + sizeof(std::uint32_t) + sizeof(std::uint32_t));
Patrick Venture123b5c02019-03-05 14:01:00 -0800386
387 auto data = reinterpret_cast<const std::uint8_t*>(&session);
388 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
389
390 data = reinterpret_cast<const std::uint8_t*>(&offset);
391 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
392
393 data = reinterpret_cast<const std::uint8_t*>(&length);
394 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
395
396 return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
397}
398
Patrick Venture1470bec2019-03-06 07:33:12 -0800399} // namespace ipmiblob