blob: 43fc891d91f15c57bcb02de1e7b849a13bab4476 [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 {
59 std::fprintf(stderr, "Received exception: %s\n", e.what());
60 return {};
61 }
62
63 /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
64 * now.
65 */
66 if (reply.size() == 0)
67 {
68 return reply;
69 }
70
71 size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
72
73 /* This cannot be a response because it's smaller than the smallest
74 * response.
75 */
76 if (reply.size() < headerSize)
77 {
78 std::fprintf(stderr, "Invalid response length\n");
79 return {};
80 }
81
82 /* Verify the OEN. */
83 if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
84 ipmiPhosphorOen.size()) != 0)
85 {
86 std::fprintf(stderr, "Invalid OEN received\n");
87 return {};
88 }
89
90 /* Validate CRC. */
91 std::uint16_t crc;
92 auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
93 std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
94
Patrick Venturebcec9c62018-12-14 13:58:41 -080095 for (const auto& byte : reply)
96 {
97 std::fprintf(stderr, "0x%02x ", byte);
98 }
99 std::fprintf(stderr, "\n");
100
Patrick Venturec79faa12018-12-12 13:12:21 -0800101 std::vector<std::uint8_t> bytes;
Patrick Venturebcec9c62018-12-14 13:58:41 -0800102 bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
Patrick Venturec79faa12018-12-12 13:12:21 -0800103
104 auto computed = generateCrc(bytes);
105 if (crc != computed)
106 {
107 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
108 crc, computed);
109 return {};
110 }
111
112 return bytes;
113}
114
115int BlobHandler::getBlobCount()
116{
117 std::uint32_t count;
118 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
119 if (resp.size() != sizeof(count))
120 {
121 return 0;
122 }
123
124 /* LE to LE (need to make this portable as some point. */
125 std::memcpy(&count, resp.data(), sizeof(count));
126 return count;
127}
128
129std::string BlobHandler::enumerateBlob(std::uint32_t index)
130{
131 std::vector<std::uint8_t> payload;
132 std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&index);
133 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
134
135 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
Patrick Venture86ef75d2018-12-19 15:59:03 -0800136 return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
137 : "";
Patrick Venturec79faa12018-12-12 13:12:21 -0800138}
139
Patrick Venture00887592018-12-11 10:57:06 -0800140std::vector<std::string> BlobHandler::getBlobList()
141{
Patrick Venturec79faa12018-12-12 13:12:21 -0800142 std::vector<std::string> list;
143 int blobCount = getBlobCount();
144
145 for (int i = 0; i < blobCount; i++)
146 {
147 auto name = enumerateBlob(i);
148 /* Currently ignore failures. */
149 if (!name.empty())
150 {
151 list.push_back(name);
152 }
153 }
154
155 return list;
Patrick Venture00887592018-12-11 10:57:06 -0800156}
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800157
158StatResponse BlobHandler::getStat(const std::string& id)
159{
160 StatResponse meta;
161 std::vector<std::uint8_t> name;
162 std::copy(id.begin(), id.end(), std::back_inserter(name));
Patrick Venture0d88a122018-12-17 07:52:04 -0800163 name.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800164
165 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
166 std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
167 std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
168 int offset = sizeof(meta.blob_state) + sizeof(meta.size);
169 std::uint8_t len = resp[offset];
170 if (len > 0)
171 {
172 std::copy(&resp[offset + 1], &resp[resp.size()],
173 std::back_inserter(meta.metadata));
174 }
175
176 return meta;
177}
Patrick Venture0533d0b2018-12-13 08:48:24 -0800178
179std::uint16_t
180 BlobHandler::openBlob(const std::string& id,
181 blobs::FirmwareBlobHandler::UpdateFlags handlerFlags)
182{
183 std::uint16_t session;
184 std::vector<std::uint8_t> request;
185 std::uint16_t flags =
186 blobs::FirmwareBlobHandler::UpdateFlags::openWrite | handlerFlags;
187 auto addrFlags = reinterpret_cast<std::uint8_t*>(&flags);
188 std::copy(addrFlags, addrFlags + sizeof(flags),
189 std::back_inserter(request));
190 std::copy(id.begin(), id.end(), std::back_inserter(request));
Patrick Venture0d88a122018-12-17 07:52:04 -0800191 request.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0533d0b2018-12-13 08:48:24 -0800192
193 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
194 if (resp.size() != sizeof(session))
195 {
196 throw BlobException("Did not receive session.");
197 }
198
199 std::memcpy(&session, resp.data(), sizeof(session));
200 return session;
201}