diff options
author | Patrick Venture <venture@google.com> | 2019-03-05 14:01:00 -0800 |
---|---|---|
committer | Patrick Venture <venture@google.com> | 2019-03-06 07:51:32 -0800 |
commit | 123b5c0910e000cf9b00a37146aae99a835f3063 (patch) | |
tree | e1aacf85711d9c5f7e8ad87a1c8e671e09f61db8 /src/ipmiblob/blob_handler.cpp | |
parent | 85e320906546f3e5a0dfe1ab54a826517dae2a0d (diff) | |
download | ipmi-blob-tool-123b5c0910e000cf9b00a37146aae99a835f3063.tar.gz ipmi-blob-tool-123b5c0910e000cf9b00a37146aae99a835f3063.zip |
initial commit
Add initial code from phosphor-ipmi-flash/tools that was not specific to
firmware update over ipmi-blobs.
Change-Id: I360537a7392347fe989397a699f6a712bc36e62c
Signed-off-by: Patrick Venture <venture@google.com>
Diffstat (limited to 'src/ipmiblob/blob_handler.cpp')
-rw-r--r-- | src/ipmiblob/blob_handler.cpp | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/ipmiblob/blob_handler.cpp b/src/ipmiblob/blob_handler.cpp new file mode 100644 index 0000000..5be0b2d --- /dev/null +++ b/src/ipmiblob/blob_handler.cpp @@ -0,0 +1,315 @@ +/* + * Copyright 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blob_handler.hpp" + +#include "blob_errors.hpp" +#include "crc.hpp" +#include "ipmi_errors.hpp" + +#include <array> +#include <cstring> + +namespace host_tool +{ + +namespace +{ +const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00}; +} + +std::vector<std::uint8_t> + BlobHandler::sendIpmiPayload(BlobOEMCommands command, + const std::vector<std::uint8_t>& payload) +{ + std::vector<std::uint8_t> request, reply, bytes; + + std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(), + std::back_inserter(request)); + request.push_back(command); + + if (payload.size() > 0) + { + /* Grow the vector to hold the bytes. */ + request.reserve(request.size() + sizeof(std::uint16_t)); + + /* CRC required. */ + std::uint16_t crc = generateCrc(payload); + auto src = reinterpret_cast<const std::uint8_t*>(&crc); + + std::copy(src, src + sizeof(crc), std::back_inserter(request)); + + /* Copy the payload. */ + std::copy(payload.begin(), payload.end(), std::back_inserter(request)); + } + + try + { + reply = ipmi->sendPacket(request); + } + catch (const IpmiException& e) + { + throw BlobException(e.what()); + } + + /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for + * now. + */ + if (reply.size() == 0) + { + return reply; + } + + /* This cannot be a response because it's smaller than the smallest + * response. + */ + if (reply.size() < ipmiPhosphorOen.size()) + { + throw BlobException("Invalid response length"); + } + + /* Verify the OEN. */ + if (std::memcmp(ipmiPhosphorOen.data(), reply.data(), + ipmiPhosphorOen.size()) != 0) + { + throw BlobException("Invalid OEN received"); + } + + /* In this case there was no data, as there was no CRC. */ + std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t); + if (reply.size() < headerSize) + { + return {}; + } + + /* Validate CRC. */ + std::uint16_t crc; + auto ptr = reinterpret_cast<std::uint8_t*>(&crc); + std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc)); + + for (const auto& byte : reply) + { + std::fprintf(stderr, "0x%02x ", byte); + } + std::fprintf(stderr, "\n"); + + bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end()); + + auto computed = generateCrc(bytes); + if (crc != computed) + { + std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n", + crc, computed); + throw BlobException("Invalid CRC on received data."); + } + + return bytes; +} + +int BlobHandler::getBlobCount() +{ + std::uint32_t count; + try + { + auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {}); + if (resp.size() != sizeof(count)) + { + return 0; + } + + /* LE to LE (need to make this portable as some point. */ + std::memcpy(&count, resp.data(), sizeof(count)); + } + catch (const BlobException& b) + { + return 0; + } + + std::fprintf(stderr, "BLOB Count: %d\n", count); + return count; +} + +std::string BlobHandler::enumerateBlob(std::uint32_t index) +{ + std::vector<std::uint8_t> payload; + auto data = reinterpret_cast<const std::uint8_t*>(&index); + + std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); + + try + { + auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload); + return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1]) + : ""; + } + catch (const BlobException& b) + { + return ""; + } +} + +void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session, + std::uint32_t offset, + const std::vector<std::uint8_t>& bytes) +{ + std::vector<std::uint8_t> payload; + + payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + + bytes.size()); + + auto data = reinterpret_cast<const std::uint8_t*>(&session); + std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); + + data = reinterpret_cast<const std::uint8_t*>(&offset); + std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); + + std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload)); + + auto resp = sendIpmiPayload(command, payload); +} + +void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset, + const std::vector<std::uint8_t>& bytes) +{ + return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, + bytes); +} + +void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset, + const std::vector<std::uint8_t>& bytes) +{ + return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes); +} + +std::vector<std::string> BlobHandler::getBlobList() +{ + std::vector<std::string> list; + int blobCount = getBlobCount(); + + for (int i = 0; i < blobCount; i++) + { + auto name = enumerateBlob(i); + /* Currently ignore failures. */ + if (!name.empty()) + { + list.push_back(name); + } + } + + return list; +} + +StatResponse BlobHandler::getStat(const std::string& id) +{ + StatResponse meta; + std::vector<std::uint8_t> name, resp; + + std::copy(id.begin(), id.end(), std::back_inserter(name)); + name.push_back(0x00); /* need to add nul-terminator. */ + + try + { + resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name); + } + catch (const BlobException& b) + { + throw; + } + + std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state)); + std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size)); + int offset = sizeof(meta.blob_state) + sizeof(meta.size); + std::uint8_t len = resp[offset]; + if (len > 0) + { + std::copy(&resp[offset + 1], &resp[resp.size()], + std::back_inserter(meta.metadata)); + } + + return meta; +} + +std::uint16_t BlobHandler::openBlob(const std::string& id, + std::uint16_t handlerFlags) +{ + std::uint16_t session; + std::vector<std::uint8_t> request, resp; + auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags); + + std::copy(addrFlags, addrFlags + sizeof(handlerFlags), + std::back_inserter(request)); + std::copy(id.begin(), id.end(), std::back_inserter(request)); + request.push_back(0x00); /* need to add nul-terminator. */ + + try + { + resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request); + } + catch (const BlobException& b) + { + throw; + } + + if (resp.size() != sizeof(session)) + { + throw BlobException("Did not receive session."); + } + + std::memcpy(&session, resp.data(), sizeof(session)); + return session; +} + +void BlobHandler::closeBlob(std::uint16_t session) +{ + std::vector<std::uint8_t> request; + auto addrSession = reinterpret_cast<const std::uint8_t*>(&session); + std::copy(addrSession, addrSession + sizeof(session), + std::back_inserter(request)); + + try + { + sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request); + } + catch (const BlobException& b) + { + std::fprintf(stderr, "Received failure on close: %s\n", b.what()); + } + + return; +} + +std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session, + std::uint32_t offset, + std::uint32_t length) +{ + std::vector<std::uint8_t> payload; + + payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + + sizeof(std::uint32_t)); + + auto data = reinterpret_cast<const std::uint8_t*>(&session); + std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); + + data = reinterpret_cast<const std::uint8_t*>(&offset); + std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); + + data = reinterpret_cast<const std::uint8_t*>(&length); + std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); + + return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload); +} + +} // namespace host_tool |