blob: e9b82fdadd9fd110b6e700a6b84172c093ee0889 [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>
25#include <cstring>
Patrick Venture800f06d2019-05-14 14:43:58 -070026#include <limits>
Patrick Venture22fcc842019-05-13 09:06:24 -070027#include <memory>
Patrick Venture123b5c02019-03-05 14:01:00 -080028
Patrick Venture1470bec2019-03-06 07:33:12 -080029namespace ipmiblob
Patrick Venture123b5c02019-03-05 14:01:00 -080030{
31
32namespace
33{
34const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
35}
36
Patrick Venture22fcc842019-05-13 09:06:24 -070037std::unique_ptr<BlobInterface>
38 BlobHandler::CreateBlobHandler(std::unique_ptr<IpmiInterface> ipmi)
39{
40 return std::make_unique<BlobHandler>(std::move(ipmi));
41}
42
Patrick Venture123b5c02019-03-05 14:01:00 -080043std::vector<std::uint8_t>
44 BlobHandler::sendIpmiPayload(BlobOEMCommands command,
45 const std::vector<std::uint8_t>& payload)
46{
47 std::vector<std::uint8_t> request, reply, bytes;
48
49 std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
50 std::back_inserter(request));
Patrick Venture44474642019-05-20 19:11:04 -070051 request.push_back(static_cast<std::uint8_t>(command));
Patrick Venture123b5c02019-03-05 14:01:00 -080052
53 if (payload.size() > 0)
54 {
55 /* Grow the vector to hold the bytes. */
56 request.reserve(request.size() + sizeof(std::uint16_t));
57
58 /* CRC required. */
59 std::uint16_t crc = generateCrc(payload);
60 auto src = reinterpret_cast<const std::uint8_t*>(&crc);
61
62 std::copy(src, src + sizeof(crc), std::back_inserter(request));
63
64 /* Copy the payload. */
65 std::copy(payload.begin(), payload.end(), std::back_inserter(request));
66 }
67
68 try
69 {
Patrick Venture958f1ce2019-05-31 17:07:25 -070070 reply = ipmi->sendPacket(ipmiOEMNetFn, ipmiOEMBlobCmd, request);
Patrick Venture123b5c02019-03-05 14:01:00 -080071 }
72 catch (const IpmiException& e)
73 {
74 throw BlobException(e.what());
75 }
76
77 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
78 * now.
79 */
80 if (reply.size() == 0)
81 {
82 return reply;
83 }
84
85 /* This cannot be a response because it's smaller than the smallest
86 * response.
87 */
88 if (reply.size() < ipmiPhosphorOen.size())
89 {
90 throw BlobException("Invalid response length");
91 }
92
93 /* Verify the OEN. */
94 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
95 ipmiPhosphorOen.size()) != 0)
96 {
97 throw BlobException("Invalid OEN received");
98 }
99
100 /* In this case there was no data, as there was no CRC. */
101 std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
102 if (reply.size() < headerSize)
103 {
104 return {};
105 }
106
107 /* Validate CRC. */
108 std::uint16_t crc;
109 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
110 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
111
Patrick Venture123b5c02019-03-05 14:01:00 -0800112 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
113
114 auto computed = generateCrc(bytes);
115 if (crc != computed)
116 {
117 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
118 crc, computed);
119 throw BlobException("Invalid CRC on received data.");
120 }
121
122 return bytes;
123}
124
125int BlobHandler::getBlobCount()
126{
127 std::uint32_t count;
128 try
129 {
130 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
131 if (resp.size() != sizeof(count))
132 {
133 return 0;
134 }
135
136 /* LE to LE (need to make this portable as some point. */
137 std::memcpy(&count, resp.data(), sizeof(count));
138 }
139 catch (const BlobException& b)
140 {
141 return 0;
142 }
143
Patrick Venture123b5c02019-03-05 14:01:00 -0800144 return count;
145}
146
147std::string BlobHandler::enumerateBlob(std::uint32_t index)
148{
149 std::vector<std::uint8_t> payload;
150 auto data = reinterpret_cast<const std::uint8_t*>(&index);
151
152 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
153
154 try
155 {
156 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
157 return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
158 : "";
159 }
160 catch (const BlobException& b)
161 {
162 return "";
163 }
164}
165
Patrick Venture8865e402019-05-14 13:29:10 -0700166void BlobHandler::commit(std::uint16_t session,
167 const std::vector<std::uint8_t>& bytes)
168{
169 std::vector<std::uint8_t> request;
170 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
171 std::copy(addrSession, addrSession + sizeof(session),
172 std::back_inserter(request));
173
174 /* You have one byte to describe the length. */
175 if (bytes.size() > std::numeric_limits<std::uint8_t>::max())
176 {
177 throw BlobException("Commit data length greater than 8-bit limit\n");
178 }
179
180 std::uint8_t length = static_cast<std::uint8_t>(bytes.size());
181 auto addrLength = reinterpret_cast<const std::uint8_t*>(&length);
182 std::copy(addrLength, addrLength + sizeof(length),
183 std::back_inserter(request));
184
185 std::copy(bytes.begin(), bytes.end(), std::back_inserter(request));
186
187 sendIpmiPayload(BlobOEMCommands::bmcBlobCommit, request);
188}
189
Patrick Venture123b5c02019-03-05 14:01:00 -0800190void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
191 std::uint32_t offset,
192 const std::vector<std::uint8_t>& bytes)
193{
194 std::vector<std::uint8_t> payload;
195
196 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
197 bytes.size());
198
199 auto data = reinterpret_cast<const std::uint8_t*>(&session);
200 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
201
202 data = reinterpret_cast<const std::uint8_t*>(&offset);
203 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
204
205 std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
206
Brandon Kim0106f572019-10-18 10:13:55 -0700207 sendIpmiPayload(command, payload);
Patrick Venture123b5c02019-03-05 14:01:00 -0800208}
209
210void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
211 const std::vector<std::uint8_t>& bytes)
212{
Brandon Kim00f39b82019-10-18 11:00:00 -0700213 writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800214}
215
216void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
217 const std::vector<std::uint8_t>& bytes)
218{
Brandon Kim00f39b82019-10-18 11:00:00 -0700219 writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
Patrick Venture123b5c02019-03-05 14:01:00 -0800220}
221
222std::vector<std::string> BlobHandler::getBlobList()
223{
224 std::vector<std::string> list;
225 int blobCount = getBlobCount();
226
227 for (int i = 0; i < blobCount; i++)
228 {
229 auto name = enumerateBlob(i);
230 /* Currently ignore failures. */
231 if (!name.empty())
232 {
233 list.push_back(name);
234 }
235 }
236
237 return list;
238}
239
Patrick Venture17186ae2019-05-06 10:30:55 -0700240StatResponse BlobHandler::statGeneric(BlobOEMCommands command,
241 const std::vector<std::uint8_t>& request)
Patrick Venture123b5c02019-03-05 14:01:00 -0800242{
243 StatResponse meta;
Brandon Kim714e3882020-06-22 12:36:32 -0700244 static constexpr std::size_t blobStateSize = sizeof(meta.blob_state);
245 static constexpr std::size_t metaSize = sizeof(meta.size);
246 static constexpr std::size_t metaOffset = blobStateSize + metaSize;
247 static constexpr std::size_t minRespSize =
248 metaOffset + sizeof(std::uint8_t);
Patrick Venture17186ae2019-05-06 10:30:55 -0700249 std::vector<std::uint8_t> resp;
Patrick Venture123b5c02019-03-05 14:01:00 -0800250
251 try
252 {
Patrick Venture17186ae2019-05-06 10:30:55 -0700253 resp = sendIpmiPayload(command, request);
Patrick Venture123b5c02019-03-05 14:01:00 -0800254 }
255 catch (const BlobException& b)
256 {
257 throw;
258 }
259
Brandon Kim714e3882020-06-22 12:36:32 -0700260 // Avoid out of bounds memcpy below
261 if (resp.size() < minRespSize)
262 {
263 std::fprintf(stderr,
264 "Invalid response length, Got %li which is less than "
265 "minRespSize %li\n",
266 resp.size(), minRespSize);
267 throw BlobException("Invalid response length");
268 }
269
270 std::memcpy(&meta.blob_state, &resp[0], blobStateSize);
271 std::memcpy(&meta.size, &resp[blobStateSize], metaSize);
272 std::uint8_t len = resp[metaOffset];
273
274 auto metaDataLength = resp.size() - minRespSize;
275 if (metaDataLength != len)
276 {
277 std::fprintf(stderr,
278 "Metadata length did not match actual length, Got %li "
279 "which does not equal expected length %i\n",
280 metaDataLength, len);
281 throw BlobException("Metadata length did not match actual length");
282 }
283
Patrick Venture123b5c02019-03-05 14:01:00 -0800284 if (len > 0)
285 {
Brandon Kim714e3882020-06-22 12:36:32 -0700286 meta.metadata.resize(len);
287 std::copy(resp.begin() + minRespSize, resp.end(),
288 meta.metadata.begin());
Patrick Venture123b5c02019-03-05 14:01:00 -0800289 }
290
291 return meta;
292}
293
Patrick Venture17186ae2019-05-06 10:30:55 -0700294StatResponse BlobHandler::getStat(const std::string& id)
295{
296 std::vector<std::uint8_t> name;
297 std::copy(id.begin(), id.end(), std::back_inserter(name));
298 name.push_back(0x00); /* need to add nul-terminator. */
299
300 return statGeneric(BlobOEMCommands::bmcBlobStat, name);
301}
302
Patrick Venture16a99a62019-05-03 17:21:30 -0700303StatResponse BlobHandler::getStat(std::uint16_t session)
304{
Patrick Venture16a99a62019-05-03 17:21:30 -0700305 std::vector<std::uint8_t> request;
306 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
307 std::copy(addrSession, addrSession + sizeof(session),
308 std::back_inserter(request));
309
Patrick Venture17186ae2019-05-06 10:30:55 -0700310 return statGeneric(BlobOEMCommands::bmcBlobSessionStat, request);
Patrick Venture16a99a62019-05-03 17:21:30 -0700311}
312
Patrick Venture123b5c02019-03-05 14:01:00 -0800313std::uint16_t BlobHandler::openBlob(const std::string& id,
314 std::uint16_t handlerFlags)
315{
316 std::uint16_t session;
317 std::vector<std::uint8_t> request, resp;
318 auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);
319
320 std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
321 std::back_inserter(request));
322 std::copy(id.begin(), id.end(), std::back_inserter(request));
323 request.push_back(0x00); /* need to add nul-terminator. */
324
325 try
326 {
327 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
328 }
329 catch (const BlobException& b)
330 {
331 throw;
332 }
333
334 if (resp.size() != sizeof(session))
335 {
336 throw BlobException("Did not receive session.");
337 }
338
339 std::memcpy(&session, resp.data(), sizeof(session));
340 return session;
341}
342
343void BlobHandler::closeBlob(std::uint16_t session)
344{
345 std::vector<std::uint8_t> request;
346 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
347 std::copy(addrSession, addrSession + sizeof(session),
348 std::back_inserter(request));
349
350 try
351 {
352 sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
353 }
354 catch (const BlobException& b)
355 {
356 std::fprintf(stderr, "Received failure on close: %s\n", b.what());
357 }
Patrick Venture123b5c02019-03-05 14:01:00 -0800358}
359
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700360void BlobHandler::deleteBlob(const std::string& id)
361{
362 std::vector<std::uint8_t> name;
363 std::copy(id.begin(), id.end(), std::back_inserter(name));
364 name.push_back(0x00); /* need to add nul-terminator. */
365
366 try
367 {
368 sendIpmiPayload(BlobOEMCommands::bmcBlobDelete, name);
369 }
370 catch (const BlobException& b)
371 {
372 std::fprintf(stderr, "Received failure on delete: %s\n", b.what());
373 }
Brandon Kimcc4ef0c2019-10-18 10:08:37 -0700374}
375
Patrick Venture123b5c02019-03-05 14:01:00 -0800376std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
377 std::uint32_t offset,
378 std::uint32_t length)
379{
380 std::vector<std::uint8_t> payload;
381
382 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
383 sizeof(std::uint32_t));
384
385 auto data = reinterpret_cast<const std::uint8_t*>(&session);
386 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
387
388 data = reinterpret_cast<const std::uint8_t*>(&offset);
389 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
390
391 data = reinterpret_cast<const std::uint8_t*>(&length);
392 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
393
394 return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
395}
396
Patrick Venture1470bec2019-03-06 07:33:12 -0800397} // namespace ipmiblob