blob: 62bbb4d373f1acefec97e3659de75b9194600753 [file] [log] [blame]
Patrick Venturec79faa12018-12-12 13:12:21 -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
Patrick Venture00887592018-12-11 10:57:06 -080017#include "blob_handler.hpp"
18
Patrick Venture0533d0b2018-12-13 08:48:24 -080019#include "blob_errors.hpp"
Patrick Venturec79faa12018-12-12 13:12:21 -080020#include "crc.hpp"
21#include "ipmi_errors.hpp"
22
23#include <array>
24#include <cstring>
25
26const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
27
28std::vector<std::uint8_t>
29 BlobHandler::sendIpmiPayload(BlobOEMCommands command,
30 const std::vector<std::uint8_t>& payload)
31{
32 std::vector<std::uint8_t> request, reply;
33
34 std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
35 std::back_inserter(request));
36 request.push_back(command);
37
38 if (payload.size() > 0)
39 {
40 /* Grow the vector to hold the bytes. */
41 request.reserve(request.size() + sizeof(std::uint16_t));
42
43 /* CRC required. */
44 std::uint16_t crc = generateCrc(payload);
45 std::uint8_t* src = reinterpret_cast<std::uint8_t*>(&crc);
46
47 std::copy(src, src + sizeof(crc), std::back_inserter(request));
48
49 /* Copy the payload. */
50 std::copy(payload.begin(), payload.end(), std::back_inserter(request));
51 }
52
53 try
54 {
55 reply = ipmi->sendPacket(request);
56 }
57 catch (const IpmiException& e)
58 {
Patrick Venture339dece2018-12-14 18:32:04 -080059 throw BlobException(e.what());
Patrick Venturec79faa12018-12-12 13:12:21 -080060 }
61
62 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
63 * now.
64 */
65 if (reply.size() == 0)
66 {
67 return reply;
68 }
69
70 size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
71
72 /* This cannot be a response because it's smaller than the smallest
73 * response.
74 */
75 if (reply.size() < headerSize)
76 {
Patrick Venture339dece2018-12-14 18:32:04 -080077 throw BlobException("Invalid response length");
Patrick Venturec79faa12018-12-12 13:12:21 -080078 }
79
80 /* Verify the OEN. */
81 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
82 ipmiPhosphorOen.size()) != 0)
83 {
Patrick Venture339dece2018-12-14 18:32:04 -080084 throw BlobException("Invalid OEN received");
Patrick Venturec79faa12018-12-12 13:12:21 -080085 }
86
87 /* Validate CRC. */
88 std::uint16_t crc;
89 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
90 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
91
Patrick Venturebcec9c62018-12-14 13:58:41 -080092 for (const auto& byte : reply)
93 {
94 std::fprintf(stderr, "0x%02x ", byte);
95 }
96 std::fprintf(stderr, "\n");
97
Patrick Venturec79faa12018-12-12 13:12:21 -080098 std::vector<std::uint8_t> bytes;
Patrick Venturebcec9c62018-12-14 13:58:41 -080099 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
Patrick Venturec79faa12018-12-12 13:12:21 -0800100
101 auto computed = generateCrc(bytes);
102 if (crc != computed)
103 {
104 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
105 crc, computed);
Patrick Venture339dece2018-12-14 18:32:04 -0800106 throw BlobException("Invalid CRC on received data.");
Patrick Venturec79faa12018-12-12 13:12:21 -0800107 }
108
109 return bytes;
110}
111
112int BlobHandler::getBlobCount()
113{
114 std::uint32_t count;
Patrick Venture339dece2018-12-14 18:32:04 -0800115 try
116 {
117 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
118 if (resp.size() != sizeof(count))
119 {
120 return 0;
121 }
122
123 /* LE to LE (need to make this portable as some point. */
124 std::memcpy(&count, resp.data(), sizeof(count));
125 }
126 catch (const BlobException& b)
Patrick Venturec79faa12018-12-12 13:12:21 -0800127 {
128 return 0;
129 }
130
Patrick Venture339dece2018-12-14 18:32:04 -0800131 std::fprintf(stderr, "BLOB Count: %d\n", count);
Patrick Venturec79faa12018-12-12 13:12:21 -0800132 return count;
133}
134
135std::string BlobHandler::enumerateBlob(std::uint32_t index)
136{
137 std::vector<std::uint8_t> payload;
138 std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&index);
Patrick Venture339dece2018-12-14 18:32:04 -0800139
Patrick Venturec79faa12018-12-12 13:12:21 -0800140 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
141
Patrick Venture339dece2018-12-14 18:32:04 -0800142 try
143 {
144 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
145 return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
146 : "";
147 }
148 catch (const BlobException& b)
149 {
150 return "";
151 }
Patrick Venturec79faa12018-12-12 13:12:21 -0800152}
153
Patrick Venture00887592018-12-11 10:57:06 -0800154std::vector<std::string> BlobHandler::getBlobList()
155{
Patrick Venturec79faa12018-12-12 13:12:21 -0800156 std::vector<std::string> list;
157 int blobCount = getBlobCount();
158
159 for (int i = 0; i < blobCount; i++)
160 {
161 auto name = enumerateBlob(i);
162 /* Currently ignore failures. */
163 if (!name.empty())
164 {
165 list.push_back(name);
166 }
167 }
168
169 return list;
Patrick Venture00887592018-12-11 10:57:06 -0800170}
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800171
172StatResponse BlobHandler::getStat(const std::string& id)
173{
174 StatResponse meta;
Patrick Venture339dece2018-12-14 18:32:04 -0800175 std::vector<std::uint8_t> name, resp;
176
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800177 std::copy(id.begin(), id.end(), std::back_inserter(name));
Patrick Venture0d88a122018-12-17 07:52:04 -0800178 name.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800179
Patrick Venture339dece2018-12-14 18:32:04 -0800180 try
181 {
182 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
183 }
184 catch (const BlobException& b)
185 {
186 throw;
187 }
188
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800189 std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
190 std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
191 int offset = sizeof(meta.blob_state) + sizeof(meta.size);
192 std::uint8_t len = resp[offset];
193 if (len > 0)
194 {
195 std::copy(&resp[offset + 1], &resp[resp.size()],
196 std::back_inserter(meta.metadata));
197 }
198
199 return meta;
200}
Patrick Venture0533d0b2018-12-13 08:48:24 -0800201
202std::uint16_t
203 BlobHandler::openBlob(const std::string& id,
204 blobs::FirmwareBlobHandler::UpdateFlags handlerFlags)
205{
206 std::uint16_t session;
Patrick Venture339dece2018-12-14 18:32:04 -0800207 std::vector<std::uint8_t> request, resp;
Patrick Venture0533d0b2018-12-13 08:48:24 -0800208 std::uint16_t flags =
209 blobs::FirmwareBlobHandler::UpdateFlags::openWrite | handlerFlags;
210 auto addrFlags = reinterpret_cast<std::uint8_t*>(&flags);
Patrick Venture339dece2018-12-14 18:32:04 -0800211
Patrick Venture0533d0b2018-12-13 08:48:24 -0800212 std::copy(addrFlags, addrFlags + sizeof(flags),
213 std::back_inserter(request));
214 std::copy(id.begin(), id.end(), std::back_inserter(request));
Patrick Venture0d88a122018-12-17 07:52:04 -0800215 request.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0533d0b2018-12-13 08:48:24 -0800216
Patrick Venture339dece2018-12-14 18:32:04 -0800217 try
218 {
219 resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
220 }
221 catch (const BlobException& b)
222 {
223 throw;
224 }
225
Patrick Venture0533d0b2018-12-13 08:48:24 -0800226 if (resp.size() != sizeof(session))
227 {
228 throw BlobException("Did not receive session.");
229 }
230
231 std::memcpy(&session, resp.data(), sizeof(session));
232 return session;
233}