blob: 6f114e584eff2d2523a34d2aaeaf361c8886e949 [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 Venture123b5c02019-03-05 14:01:00 -080045std::vector<std::uint8_t>
46 BlobHandler::sendIpmiPayload(BlobOEMCommands command,
47 const std::vector<std::uint8_t>& payload)
48{
49 std::vector<std::uint8_t> request, reply, bytes;
50
51 std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
52 std::back_inserter(request));
Patrick Venture44474642019-05-20 19:11:04 -070053 request.push_back(static_cast<std::uint8_t>(command));
Patrick Venture123b5c02019-03-05 14:01:00 -080054
Patrick Venturebae76642020-11-16 14:02:39 -080055 if (!payload.empty())
Patrick Venture123b5c02019-03-05 14:01:00 -080056 {
57 /* Grow the vector to hold the bytes. */
58 request.reserve(request.size() + sizeof(std::uint16_t));
59
60 /* CRC required. */
61 std::uint16_t crc = generateCrc(payload);
62 auto src = reinterpret_cast<const std::uint8_t*>(&crc);
63
64 std::copy(src, src + sizeof(crc), std::back_inserter(request));
65
66 /* Copy the payload. */
67 std::copy(payload.begin(), payload.end(), std::back_inserter(request));
68 }
69
70 try
71 {
Patrick Venture958f1ce2019-05-31 17:07:25 -070072 reply = ipmi->sendPacket(ipmiOEMNetFn, ipmiOEMBlobCmd, request);
Patrick Venture123b5c02019-03-05 14:01:00 -080073 }
74 catch (const IpmiException& e)
75 {
76 throw BlobException(e.what());
77 }
78
79 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
80 * now.
81 */
Patrick Venturebae76642020-11-16 14:02:39 -080082 if (reply.empty())
Patrick Venture123b5c02019-03-05 14:01:00 -080083 {
84 return reply;
85 }
86
87 /* This cannot be a response because it's smaller than the smallest
88 * response.
89 */
90 if (reply.size() < ipmiPhosphorOen.size())
91 {
92 throw BlobException("Invalid response length");
93 }
94
95 /* Verify the OEN. */
96 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
97 ipmiPhosphorOen.size()) != 0)
98 {
99 throw BlobException("Invalid OEN received");
100 }
101
102 /* In this case there was no data, as there was no CRC. */
103 std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
104 if (reply.size() < headerSize)
105 {
106 return {};
107 }
108
109 /* Validate CRC. */
110 std::uint16_t crc;
111 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
112 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
113
Patrick Venture123b5c02019-03-05 14:01:00 -0800114 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
115
116 auto computed = generateCrc(bytes);
117 if (crc != computed)
118 {
119 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
120 crc, computed);
121 throw BlobException("Invalid CRC on received data.");
122 }
123
124 return bytes;
125}
126
127int BlobHandler::getBlobCount()
128{
129 std::uint32_t count;
130 try
131 {
132 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
133 if (resp.size() != sizeof(count))
134 {
135 return 0;
136 }
137
138 /* LE to LE (need to make this portable as some point. */
139 std::memcpy(&count, resp.data(), sizeof(count));
140 }
141 catch (const BlobException& b)
142 {
143 return 0;
144 }
145
Patrick Venture123b5c02019-03-05 14:01:00 -0800146 return count;
147}
148
149std::string BlobHandler::enumerateBlob(std::uint32_t index)
150{
151 std::vector<std::uint8_t> payload;
152 auto data = reinterpret_cast<const std::uint8_t*>(&index);
153
154 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
155
156 try
157 {
158 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
Patrick Venturebae76642020-11-16 14:02:39 -0800159 return (resp.empty()) ? ""
160 : std::string(&resp[0], &resp[resp.size() - 1]);
Patrick Venture123b5c02019-03-05 14:01:00 -0800161 }
162 catch (const BlobException& b)
163 {
164 return "";
165 }
166}
167
Patrick Venture8865e402019-05-14 13:29:10 -0700168void BlobHandler::commit(std::uint16_t session,
169 const std::vector<std::uint8_t>& bytes)
170{
171 std::vector<std::uint8_t> request;
172 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
173 std::copy(addrSession, addrSession + sizeof(session),
174 std::back_inserter(request));
175
176 /* You have one byte to describe the length. */
177 if (bytes.size() > std::numeric_limits<std::uint8_t>::max())
178 {
179 throw BlobException("Commit data length greater than 8-bit limit\n");
180 }
181
182 std::uint8_t length = static_cast<std::uint8_t>(bytes.size());
183 auto addrLength = reinterpret_cast<const std::uint8_t*>(&length);
184 std::copy(addrLength, addrLength + sizeof(length),
185 std::back_inserter(request));
186
187 std::copy(bytes.begin(), bytes.end(), std::back_inserter(request));
188
189 sendIpmiPayload(BlobOEMCommands::bmcBlobCommit, request);
190}
191
Patrick Venture123b5c02019-03-05 14:01:00 -0800192void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
193 std::uint32_t offset,
194 const std::vector<std::uint8_t>& bytes)
195{
196 std::vector<std::uint8_t> payload;
197
198 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
199 bytes.size());
200
201 auto data = reinterpret_cast<const std::uint8_t*>(&session);
202 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
203
204 data = reinterpret_cast<const std::uint8_t*>(&offset);
205 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
206
207 std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
208
Brandon Kim0106f572019-10-18 10:13:55 -0700209 sendIpmiPayload(command, payload);
Patrick Venture123b5c02019-03-05 14:01:00 -0800210}
211
212void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
213 const std::vector<std::uint8_t>& bytes)
214{
Brandon Kim00f39b82019-10-18 11:00:00 -0700215 writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800216}
217
218void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
219 const std::vector<std::uint8_t>& bytes)
220{
Brandon Kim00f39b82019-10-18 11:00:00 -0700221 writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800222}
223
224std::vector<std::string> BlobHandler::getBlobList()
225{
226 std::vector<std::string> list;
227 int blobCount = getBlobCount();
228
229 for (int i = 0; i < blobCount; i++)
230 {
231 auto name = enumerateBlob(i);
232 /* Currently ignore failures. */
233 if (!name.empty())
234 {
235 list.push_back(name);
236 }
237 }
238
239 return list;
240}
241
Patrick Venture17186ae2019-05-06 10:30:55 -0700242StatResponse BlobHandler::statGeneric(BlobOEMCommands command,
243 const std::vector<std::uint8_t>& request)
Patrick Venture123b5c02019-03-05 14:01:00 -0800244{
245 StatResponse meta;
Brandon Kim714e3882020-06-22 12:36:32 -0700246 static constexpr std::size_t blobStateSize = sizeof(meta.blob_state);
247 static constexpr std::size_t metaSize = sizeof(meta.size);
248 static constexpr std::size_t metaOffset = blobStateSize + metaSize;
249 static constexpr std::size_t minRespSize =
250 metaOffset + sizeof(std::uint8_t);
Patrick Venture17186ae2019-05-06 10:30:55 -0700251 std::vector<std::uint8_t> resp;
Patrick Venture123b5c02019-03-05 14:01:00 -0800252
253 try
254 {
Patrick Venture17186ae2019-05-06 10:30:55 -0700255 resp = sendIpmiPayload(command, request);
Patrick Venture123b5c02019-03-05 14:01:00 -0800256 }
257 catch (const BlobException& b)
258 {
259 throw;
260 }
261
Brandon Kim714e3882020-06-22 12:36:32 -0700262 // Avoid out of bounds memcpy below
263 if (resp.size() < minRespSize)
264 {
265 std::fprintf(stderr,
Brandon Kimd3c06f92022-07-20 17:36:53 +0000266 "Invalid response length, Got %zu which is less than "
267 "minRespSize %zu\n",
Brandon Kim714e3882020-06-22 12:36:32 -0700268 resp.size(), minRespSize);
269 throw BlobException("Invalid response length");
270 }
271
272 std::memcpy(&meta.blob_state, &resp[0], blobStateSize);
273 std::memcpy(&meta.size, &resp[blobStateSize], metaSize);
274 std::uint8_t len = resp[metaOffset];
275
276 auto metaDataLength = resp.size() - minRespSize;
277 if (metaDataLength != len)
278 {
279 std::fprintf(stderr,
Brandon Kimd3c06f92022-07-20 17:36:53 +0000280 "Metadata length did not match actual length, Got %zu "
281 "which does not equal expected length %" PRIu8 "\n",
Brandon Kim714e3882020-06-22 12:36:32 -0700282 metaDataLength, len);
283 throw BlobException("Metadata length did not match actual length");
284 }
285
Patrick Venture123b5c02019-03-05 14:01:00 -0800286 if (len > 0)
287 {
Brandon Kim714e3882020-06-22 12:36:32 -0700288 meta.metadata.resize(len);
289 std::copy(resp.begin() + minRespSize, resp.end(),
290 meta.metadata.begin());
Patrick Venture123b5c02019-03-05 14:01:00 -0800291 }
292
293 return meta;
294}
295
Patrick Venture17186ae2019-05-06 10:30:55 -0700296StatResponse BlobHandler::getStat(const std::string& id)
297{
298 std::vector<std::uint8_t> name;
299 std::copy(id.begin(), id.end(), std::back_inserter(name));
300 name.push_back(0x00); /* need to add nul-terminator. */
301
302 return statGeneric(BlobOEMCommands::bmcBlobStat, name);
303}
304
Patrick Venture16a99a62019-05-03 17:21:30 -0700305StatResponse BlobHandler::getStat(std::uint16_t session)
306{
Patrick Venture16a99a62019-05-03 17:21:30 -0700307 std::vector<std::uint8_t> request;
308 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
309 std::copy(addrSession, addrSession + sizeof(session),
310 std::back_inserter(request));
311
Patrick Venture17186ae2019-05-06 10:30:55 -0700312 return statGeneric(BlobOEMCommands::bmcBlobSessionStat, request);
Patrick Venture16a99a62019-05-03 17:21:30 -0700313}
314
Patrick Venture123b5c02019-03-05 14:01:00 -0800315std::uint16_t BlobHandler::openBlob(const std::string& id,
316 std::uint16_t handlerFlags)
317{
318 std::uint16_t session;
319 std::vector<std::uint8_t> request, resp;
320 auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);
321
322 std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
323 std::back_inserter(request));
324 std::copy(id.begin(), id.end(), std::back_inserter(request));
325 request.push_back(0x00); /* need to add nul-terminator. */
326
327 try
328 {
329 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
330 }
331 catch (const BlobException& b)
332 {
333 throw;
334 }
335
336 if (resp.size() != sizeof(session))
337 {
338 throw BlobException("Did not receive session.");
339 }
340
341 std::memcpy(&session, resp.data(), sizeof(session));
342 return session;
343}
344
345void BlobHandler::closeBlob(std::uint16_t session)
346{
347 std::vector<std::uint8_t> request;
348 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
349 std::copy(addrSession, addrSession + sizeof(session),
350 std::back_inserter(request));
351
352 try
353 {
354 sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
355 }
356 catch (const BlobException& b)
357 {
358 std::fprintf(stderr, "Received failure on close: %s\n", b.what());
359 }
Patrick Venture123b5c02019-03-05 14:01:00 -0800360}
361
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700362bool BlobHandler::deleteBlob(const std::string& id)
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700363{
364 std::vector<std::uint8_t> name;
365 std::copy(id.begin(), id.end(), std::back_inserter(name));
366 name.push_back(0x00); /* need to add nul-terminator. */
367
368 try
369 {
370 sendIpmiPayload(BlobOEMCommands::bmcBlobDelete, name);
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700371 return true;
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700372 }
373 catch (const BlobException& b)
374 {
375 std::fprintf(stderr, "Received failure on delete: %s\n", b.what());
376 }
William A. Kennington IIId46530f2021-11-06 17:12:07 -0700377 return false;
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700378}
379
Patrick Venture123b5c02019-03-05 14:01:00 -0800380std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
381 std::uint32_t offset,
382 std::uint32_t length)
383{
384 std::vector<std::uint8_t> payload;
385
386 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
387 sizeof(std::uint32_t));
388
389 auto data = reinterpret_cast<const std::uint8_t*>(&session);
390 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
391
392 data = reinterpret_cast<const std::uint8_t*>(&offset);
393 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
394
395 data = reinterpret_cast<const std::uint8_t*>(&length);
396 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
397
398 return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
399}
400
Patrick Venture1470bec2019-03-06 07:33:12 -0800401} // namespace ipmiblob