| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 1 | /* | 
 | 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 Venture | 22fcc84 | 2019-05-13 09:06:24 -0700 | [diff] [blame] | 22 | #include "ipmi_interface.hpp" | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 23 |  | 
 | 24 | #include <array> | 
 | 25 | #include <cstring> | 
| Patrick Venture | 22fcc84 | 2019-05-13 09:06:24 -0700 | [diff] [blame] | 26 | #include <memory> | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 27 |  | 
| Patrick Venture | 1470bec | 2019-03-06 07:33:12 -0800 | [diff] [blame] | 28 | namespace ipmiblob | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 29 | { | 
 | 30 |  | 
 | 31 | namespace | 
 | 32 | { | 
 | 33 | const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00}; | 
 | 34 | } | 
 | 35 |  | 
| Patrick Venture | 22fcc84 | 2019-05-13 09:06:24 -0700 | [diff] [blame] | 36 | std::unique_ptr<BlobInterface> | 
 | 37 |     BlobHandler::CreateBlobHandler(std::unique_ptr<IpmiInterface> ipmi) | 
 | 38 | { | 
 | 39 |     return std::make_unique<BlobHandler>(std::move(ipmi)); | 
 | 40 | } | 
 | 41 |  | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 42 | std::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 Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 111 |     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 |  | 
 | 124 | int 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 Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 143 |     return count; | 
 | 144 | } | 
 | 145 |  | 
 | 146 | std::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 |  | 
| Patrick Venture | 8865e40 | 2019-05-14 13:29:10 -0700 | [diff] [blame^] | 165 | void BlobHandler::commit(std::uint16_t session, | 
 | 166 |                          const std::vector<std::uint8_t>& bytes) | 
 | 167 | { | 
 | 168 |     std::vector<std::uint8_t> request; | 
 | 169 |     auto addrSession = reinterpret_cast<const std::uint8_t*>(&session); | 
 | 170 |     std::copy(addrSession, addrSession + sizeof(session), | 
 | 171 |               std::back_inserter(request)); | 
 | 172 |  | 
 | 173 |     /* You have one byte to describe the length. */ | 
 | 174 |     if (bytes.size() > std::numeric_limits<std::uint8_t>::max()) | 
 | 175 |     { | 
 | 176 |         throw BlobException("Commit data length greater than 8-bit limit\n"); | 
 | 177 |     } | 
 | 178 |  | 
 | 179 |     std::uint8_t length = static_cast<std::uint8_t>(bytes.size()); | 
 | 180 |     auto addrLength = reinterpret_cast<const std::uint8_t*>(&length); | 
 | 181 |     std::copy(addrLength, addrLength + sizeof(length), | 
 | 182 |               std::back_inserter(request)); | 
 | 183 |  | 
 | 184 |     std::copy(bytes.begin(), bytes.end(), std::back_inserter(request)); | 
 | 185 |  | 
 | 186 |     sendIpmiPayload(BlobOEMCommands::bmcBlobCommit, request); | 
 | 187 | } | 
 | 188 |  | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 189 | void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session, | 
 | 190 |                                std::uint32_t offset, | 
 | 191 |                                const std::vector<std::uint8_t>& bytes) | 
 | 192 | { | 
 | 193 |     std::vector<std::uint8_t> payload; | 
 | 194 |  | 
 | 195 |     payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + | 
 | 196 |                     bytes.size()); | 
 | 197 |  | 
 | 198 |     auto data = reinterpret_cast<const std::uint8_t*>(&session); | 
 | 199 |     std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); | 
 | 200 |  | 
 | 201 |     data = reinterpret_cast<const std::uint8_t*>(&offset); | 
 | 202 |     std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); | 
 | 203 |  | 
 | 204 |     std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload)); | 
 | 205 |  | 
 | 206 |     auto resp = sendIpmiPayload(command, payload); | 
 | 207 | } | 
 | 208 |  | 
 | 209 | void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset, | 
 | 210 |                             const std::vector<std::uint8_t>& bytes) | 
 | 211 | { | 
 | 212 |     return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, | 
 | 213 |                         bytes); | 
 | 214 | } | 
 | 215 |  | 
 | 216 | void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset, | 
 | 217 |                              const std::vector<std::uint8_t>& bytes) | 
 | 218 | { | 
 | 219 |     return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes); | 
 | 220 | } | 
 | 221 |  | 
 | 222 | std::vector<std::string> BlobHandler::getBlobList() | 
 | 223 | { | 
 | 224 |     std::vector<std::string> list; | 
 | 225 |     int blobCount = getBlobCount(); | 
 | 226 |  | 
 | 227 |     for (int i = 0; i < blobCount; i++) | 
 | 228 |     { | 
 | 229 |         auto name = enumerateBlob(i); | 
 | 230 |         /* Currently ignore failures. */ | 
 | 231 |         if (!name.empty()) | 
 | 232 |         { | 
 | 233 |             list.push_back(name); | 
 | 234 |         } | 
 | 235 |     } | 
 | 236 |  | 
 | 237 |     return list; | 
 | 238 | } | 
 | 239 |  | 
| Patrick Venture | 17186ae | 2019-05-06 10:30:55 -0700 | [diff] [blame] | 240 | StatResponse BlobHandler::statGeneric(BlobOEMCommands command, | 
 | 241 |                                       const std::vector<std::uint8_t>& request) | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 242 | { | 
 | 243 |     StatResponse meta; | 
| Patrick Venture | 17186ae | 2019-05-06 10:30:55 -0700 | [diff] [blame] | 244 |     std::vector<std::uint8_t> resp; | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 245 |  | 
 | 246 |     try | 
 | 247 |     { | 
| Patrick Venture | 17186ae | 2019-05-06 10:30:55 -0700 | [diff] [blame] | 248 |         resp = sendIpmiPayload(command, request); | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 249 |     } | 
 | 250 |     catch (const BlobException& b) | 
 | 251 |     { | 
 | 252 |         throw; | 
 | 253 |     } | 
 | 254 |  | 
 | 255 |     std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state)); | 
 | 256 |     std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size)); | 
 | 257 |     int offset = sizeof(meta.blob_state) + sizeof(meta.size); | 
 | 258 |     std::uint8_t len = resp[offset]; | 
 | 259 |     if (len > 0) | 
 | 260 |     { | 
| Patrick Venture | d39b6f8 | 2019-05-07 11:04:35 -0700 | [diff] [blame] | 261 |         std::copy(resp.begin() + offset + sizeof(len), resp.end(), | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 262 |                   std::back_inserter(meta.metadata)); | 
 | 263 |     } | 
 | 264 |  | 
 | 265 |     return meta; | 
 | 266 | } | 
 | 267 |  | 
| Patrick Venture | 17186ae | 2019-05-06 10:30:55 -0700 | [diff] [blame] | 268 | StatResponse BlobHandler::getStat(const std::string& id) | 
 | 269 | { | 
 | 270 |     std::vector<std::uint8_t> name; | 
 | 271 |     std::copy(id.begin(), id.end(), std::back_inserter(name)); | 
 | 272 |     name.push_back(0x00); /* need to add nul-terminator. */ | 
 | 273 |  | 
 | 274 |     return statGeneric(BlobOEMCommands::bmcBlobStat, name); | 
 | 275 | } | 
 | 276 |  | 
| Patrick Venture | 16a99a6 | 2019-05-03 17:21:30 -0700 | [diff] [blame] | 277 | StatResponse BlobHandler::getStat(std::uint16_t session) | 
 | 278 | { | 
| Patrick Venture | 16a99a6 | 2019-05-03 17:21:30 -0700 | [diff] [blame] | 279 |     std::vector<std::uint8_t> request; | 
 | 280 |     auto addrSession = reinterpret_cast<const std::uint8_t*>(&session); | 
 | 281 |     std::copy(addrSession, addrSession + sizeof(session), | 
 | 282 |               std::back_inserter(request)); | 
 | 283 |  | 
| Patrick Venture | 17186ae | 2019-05-06 10:30:55 -0700 | [diff] [blame] | 284 |     return statGeneric(BlobOEMCommands::bmcBlobSessionStat, request); | 
| Patrick Venture | 16a99a6 | 2019-05-03 17:21:30 -0700 | [diff] [blame] | 285 | } | 
 | 286 |  | 
| Patrick Venture | 123b5c0 | 2019-03-05 14:01:00 -0800 | [diff] [blame] | 287 | std::uint16_t BlobHandler::openBlob(const std::string& id, | 
 | 288 |                                     std::uint16_t handlerFlags) | 
 | 289 | { | 
 | 290 |     std::uint16_t session; | 
 | 291 |     std::vector<std::uint8_t> request, resp; | 
 | 292 |     auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags); | 
 | 293 |  | 
 | 294 |     std::copy(addrFlags, addrFlags + sizeof(handlerFlags), | 
 | 295 |               std::back_inserter(request)); | 
 | 296 |     std::copy(id.begin(), id.end(), std::back_inserter(request)); | 
 | 297 |     request.push_back(0x00); /* need to add nul-terminator. */ | 
 | 298 |  | 
 | 299 |     try | 
 | 300 |     { | 
 | 301 |         resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request); | 
 | 302 |     } | 
 | 303 |     catch (const BlobException& b) | 
 | 304 |     { | 
 | 305 |         throw; | 
 | 306 |     } | 
 | 307 |  | 
 | 308 |     if (resp.size() != sizeof(session)) | 
 | 309 |     { | 
 | 310 |         throw BlobException("Did not receive session."); | 
 | 311 |     } | 
 | 312 |  | 
 | 313 |     std::memcpy(&session, resp.data(), sizeof(session)); | 
 | 314 |     return session; | 
 | 315 | } | 
 | 316 |  | 
 | 317 | void BlobHandler::closeBlob(std::uint16_t session) | 
 | 318 | { | 
 | 319 |     std::vector<std::uint8_t> request; | 
 | 320 |     auto addrSession = reinterpret_cast<const std::uint8_t*>(&session); | 
 | 321 |     std::copy(addrSession, addrSession + sizeof(session), | 
 | 322 |               std::back_inserter(request)); | 
 | 323 |  | 
 | 324 |     try | 
 | 325 |     { | 
 | 326 |         sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request); | 
 | 327 |     } | 
 | 328 |     catch (const BlobException& b) | 
 | 329 |     { | 
 | 330 |         std::fprintf(stderr, "Received failure on close: %s\n", b.what()); | 
 | 331 |     } | 
 | 332 |  | 
 | 333 |     return; | 
 | 334 | } | 
 | 335 |  | 
 | 336 | std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session, | 
 | 337 |                                                  std::uint32_t offset, | 
 | 338 |                                                  std::uint32_t length) | 
 | 339 | { | 
 | 340 |     std::vector<std::uint8_t> payload; | 
 | 341 |  | 
 | 342 |     payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + | 
 | 343 |                     sizeof(std::uint32_t)); | 
 | 344 |  | 
 | 345 |     auto data = reinterpret_cast<const std::uint8_t*>(&session); | 
 | 346 |     std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); | 
 | 347 |  | 
 | 348 |     data = reinterpret_cast<const std::uint8_t*>(&offset); | 
 | 349 |     std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); | 
 | 350 |  | 
 | 351 |     data = reinterpret_cast<const std::uint8_t*>(&length); | 
 | 352 |     std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); | 
 | 353 |  | 
 | 354 |     return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload); | 
 | 355 | } | 
 | 356 |  | 
| Patrick Venture | 1470bec | 2019-03-06 07:33:12 -0800 | [diff] [blame] | 357 | } // namespace ipmiblob |