blob: 7a688bf3688eb9854bd4e8cbafc73f699fdab913 [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"
22
23#include <array>
24#include <cstring>
25
Patrick Venture1470bec2019-03-06 07:33:12 -080026namespace ipmiblob
Patrick Venture123b5c02019-03-05 14:01:00 -080027{
28
29namespace
30{
31const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
32}
33
34std::vector<std::uint8_t>
35 BlobHandler::sendIpmiPayload(BlobOEMCommands command,
36 const std::vector<std::uint8_t>& payload)
37{
38 std::vector<std::uint8_t> request, reply, bytes;
39
40 std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
41 std::back_inserter(request));
42 request.push_back(command);
43
44 if (payload.size() > 0)
45 {
46 /* Grow the vector to hold the bytes. */
47 request.reserve(request.size() + sizeof(std::uint16_t));
48
49 /* CRC required. */
50 std::uint16_t crc = generateCrc(payload);
51 auto src = reinterpret_cast<const std::uint8_t*>(&crc);
52
53 std::copy(src, src + sizeof(crc), std::back_inserter(request));
54
55 /* Copy the payload. */
56 std::copy(payload.begin(), payload.end(), std::back_inserter(request));
57 }
58
59 try
60 {
61 reply = ipmi->sendPacket(request);
62 }
63 catch (const IpmiException& e)
64 {
65 throw BlobException(e.what());
66 }
67
68 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
69 * now.
70 */
71 if (reply.size() == 0)
72 {
73 return reply;
74 }
75
76 /* This cannot be a response because it's smaller than the smallest
77 * response.
78 */
79 if (reply.size() < ipmiPhosphorOen.size())
80 {
81 throw BlobException("Invalid response length");
82 }
83
84 /* Verify the OEN. */
85 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
86 ipmiPhosphorOen.size()) != 0)
87 {
88 throw BlobException("Invalid OEN received");
89 }
90
91 /* In this case there was no data, as there was no CRC. */
92 std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
93 if (reply.size() < headerSize)
94 {
95 return {};
96 }
97
98 /* Validate CRC. */
99 std::uint16_t crc;
100 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
101 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
102
Patrick Venture123b5c02019-03-05 14:01:00 -0800103 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
104
105 auto computed = generateCrc(bytes);
106 if (crc != computed)
107 {
108 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
109 crc, computed);
110 throw BlobException("Invalid CRC on received data.");
111 }
112
113 return bytes;
114}
115
116int BlobHandler::getBlobCount()
117{
118 std::uint32_t count;
119 try
120 {
121 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
122 if (resp.size() != sizeof(count))
123 {
124 return 0;
125 }
126
127 /* LE to LE (need to make this portable as some point. */
128 std::memcpy(&count, resp.data(), sizeof(count));
129 }
130 catch (const BlobException& b)
131 {
132 return 0;
133 }
134
Patrick Venture123b5c02019-03-05 14:01:00 -0800135 return count;
136}
137
138std::string BlobHandler::enumerateBlob(std::uint32_t index)
139{
140 std::vector<std::uint8_t> payload;
141 auto data = reinterpret_cast<const std::uint8_t*>(&index);
142
143 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
144
145 try
146 {
147 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
148 return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
149 : "";
150 }
151 catch (const BlobException& b)
152 {
153 return "";
154 }
155}
156
157void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
158 std::uint32_t offset,
159 const std::vector<std::uint8_t>& bytes)
160{
161 std::vector<std::uint8_t> payload;
162
163 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
164 bytes.size());
165
166 auto data = reinterpret_cast<const std::uint8_t*>(&session);
167 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
168
169 data = reinterpret_cast<const std::uint8_t*>(&offset);
170 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
171
172 std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
173
174 auto resp = sendIpmiPayload(command, payload);
175}
176
177void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
178 const std::vector<std::uint8_t>& bytes)
179{
180 return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset,
181 bytes);
182}
183
184void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
185 const std::vector<std::uint8_t>& bytes)
186{
187 return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
188}
189
190std::vector<std::string> BlobHandler::getBlobList()
191{
192 std::vector<std::string> list;
193 int blobCount = getBlobCount();
194
195 for (int i = 0; i < blobCount; i++)
196 {
197 auto name = enumerateBlob(i);
198 /* Currently ignore failures. */
199 if (!name.empty())
200 {
201 list.push_back(name);
202 }
203 }
204
205 return list;
206}
207
Patrick Venture17186ae2019-05-06 10:30:55 -0700208StatResponse BlobHandler::statGeneric(BlobOEMCommands command,
209 const std::vector<std::uint8_t>& request)
Patrick Venture123b5c02019-03-05 14:01:00 -0800210{
211 StatResponse meta;
Patrick Venture17186ae2019-05-06 10:30:55 -0700212 std::vector<std::uint8_t> resp;
Patrick Venture123b5c02019-03-05 14:01:00 -0800213
214 try
215 {
Patrick Venture17186ae2019-05-06 10:30:55 -0700216 resp = sendIpmiPayload(command, request);
Patrick Venture123b5c02019-03-05 14:01:00 -0800217 }
218 catch (const BlobException& b)
219 {
220 throw;
221 }
222
223 std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
224 std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
225 int offset = sizeof(meta.blob_state) + sizeof(meta.size);
226 std::uint8_t len = resp[offset];
227 if (len > 0)
228 {
Patrick Ventured39b6f82019-05-07 11:04:35 -0700229 std::copy(resp.begin() + offset + sizeof(len), resp.end(),
Patrick Venture123b5c02019-03-05 14:01:00 -0800230 std::back_inserter(meta.metadata));
231 }
232
233 return meta;
234}
235
Patrick Venture17186ae2019-05-06 10:30:55 -0700236StatResponse BlobHandler::getStat(const std::string& id)
237{
238 std::vector<std::uint8_t> name;
239 std::copy(id.begin(), id.end(), std::back_inserter(name));
240 name.push_back(0x00); /* need to add nul-terminator. */
241
242 return statGeneric(BlobOEMCommands::bmcBlobStat, name);
243}
244
Patrick Venture16a99a62019-05-03 17:21:30 -0700245StatResponse BlobHandler::getStat(std::uint16_t session)
246{
Patrick Venture16a99a62019-05-03 17:21:30 -0700247 std::vector<std::uint8_t> request;
248 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
249 std::copy(addrSession, addrSession + sizeof(session),
250 std::back_inserter(request));
251
Patrick Venture17186ae2019-05-06 10:30:55 -0700252 return statGeneric(BlobOEMCommands::bmcBlobSessionStat, request);
Patrick Venture16a99a62019-05-03 17:21:30 -0700253}
254
Patrick Venture123b5c02019-03-05 14:01:00 -0800255std::uint16_t BlobHandler::openBlob(const std::string& id,
256 std::uint16_t handlerFlags)
257{
258 std::uint16_t session;
259 std::vector<std::uint8_t> request, resp;
260 auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);
261
262 std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
263 std::back_inserter(request));
264 std::copy(id.begin(), id.end(), std::back_inserter(request));
265 request.push_back(0x00); /* need to add nul-terminator. */
266
267 try
268 {
269 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
270 }
271 catch (const BlobException& b)
272 {
273 throw;
274 }
275
276 if (resp.size() != sizeof(session))
277 {
278 throw BlobException("Did not receive session.");
279 }
280
281 std::memcpy(&session, resp.data(), sizeof(session));
282 return session;
283}
284
285void BlobHandler::closeBlob(std::uint16_t session)
286{
287 std::vector<std::uint8_t> request;
288 auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
289 std::copy(addrSession, addrSession + sizeof(session),
290 std::back_inserter(request));
291
292 try
293 {
294 sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
295 }
296 catch (const BlobException& b)
297 {
298 std::fprintf(stderr, "Received failure on close: %s\n", b.what());
299 }
300
301 return;
302}
303
304std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
305 std::uint32_t offset,
306 std::uint32_t length)
307{
308 std::vector<std::uint8_t> payload;
309
310 payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
311 sizeof(std::uint32_t));
312
313 auto data = reinterpret_cast<const std::uint8_t*>(&session);
314 std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
315
316 data = reinterpret_cast<const std::uint8_t*>(&offset);
317 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
318
319 data = reinterpret_cast<const std::uint8_t*>(&length);
320 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
321
322 return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
323}
324
Patrick Venture1470bec2019-03-06 07:33:12 -0800325} // namespace ipmiblob