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