blob: 75d3cff7904a53386ef3eb150c56baff9d3c7f42 [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
95 std::vector<std::uint8_t> bytes;
96 std::copy(&reply[headerSize], &reply[reply.size()],
97 std::back_inserter(bytes));
98
99 auto computed = generateCrc(bytes);
100 if (crc != computed)
101 {
102 std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
103 crc, computed);
104 return {};
105 }
106
107 return bytes;
108}
109
110int BlobHandler::getBlobCount()
111{
112 std::uint32_t count;
113 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
114 if (resp.size() != sizeof(count))
115 {
116 return 0;
117 }
118
119 /* LE to LE (need to make this portable as some point. */
120 std::memcpy(&count, resp.data(), sizeof(count));
121 return count;
122}
123
124std::string BlobHandler::enumerateBlob(std::uint32_t index)
125{
126 std::vector<std::uint8_t> payload;
127 std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&index);
128 std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
129
130 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
131 std::string output;
132 std::copy(resp.begin(), resp.end(), std::back_inserter(output));
133 return output;
134}
135
Patrick Venture00887592018-12-11 10:57:06 -0800136std::vector<std::string> BlobHandler::getBlobList()
137{
Patrick Venturec79faa12018-12-12 13:12:21 -0800138 std::vector<std::string> list;
139 int blobCount = getBlobCount();
140
141 for (int i = 0; i < blobCount; i++)
142 {
143 auto name = enumerateBlob(i);
144 /* Currently ignore failures. */
145 if (!name.empty())
146 {
147 list.push_back(name);
148 }
149 }
150
151 return list;
Patrick Venture00887592018-12-11 10:57:06 -0800152}
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800153
154StatResponse BlobHandler::getStat(const std::string& id)
155{
156 StatResponse meta;
157 std::vector<std::uint8_t> name;
158 std::copy(id.begin(), id.end(), std::back_inserter(name));
Patrick Venture0d88a122018-12-17 07:52:04 -0800159 name.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800160
161 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
162 std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
163 std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
164 int offset = sizeof(meta.blob_state) + sizeof(meta.size);
165 std::uint8_t len = resp[offset];
166 if (len > 0)
167 {
168 std::copy(&resp[offset + 1], &resp[resp.size()],
169 std::back_inserter(meta.metadata));
170 }
171
172 return meta;
173}
Patrick Venture0533d0b2018-12-13 08:48:24 -0800174
175std::uint16_t
176 BlobHandler::openBlob(const std::string& id,
177 blobs::FirmwareBlobHandler::UpdateFlags handlerFlags)
178{
179 std::uint16_t session;
180 std::vector<std::uint8_t> request;
181 std::uint16_t flags =
182 blobs::FirmwareBlobHandler::UpdateFlags::openWrite | handlerFlags;
183 auto addrFlags = reinterpret_cast<std::uint8_t*>(&flags);
184 std::copy(addrFlags, addrFlags + sizeof(flags),
185 std::back_inserter(request));
186 std::copy(id.begin(), id.end(), std::back_inserter(request));
Patrick Venture0d88a122018-12-17 07:52:04 -0800187 request.push_back(0x00); /* need to add nul-terminator. */
Patrick Venture0533d0b2018-12-13 08:48:24 -0800188
189 auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
190 if (resp.size() != sizeof(session))
191 {
192 throw BlobException("Did not receive session.");
193 }
194
195 std::memcpy(&session, resp.data(), sizeof(session));
196 return session;
197}