/* * 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 #include namespace ipmiblob { namespace { const std::array ipmiPhosphorOen = {0xcf, 0xc2, 0x00}; } std::vector BlobHandler::sendIpmiPayload(BlobOEMCommands command, const std::vector& payload) { std::vector 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(&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(&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 payload; auto data = reinterpret_cast(&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& bytes) { std::vector payload; payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + bytes.size()); auto data = reinterpret_cast(&session); std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); data = reinterpret_cast(&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& bytes) { return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset, bytes); } void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset, const std::vector& bytes) { return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes); } std::vector BlobHandler::getBlobList() { std::vector 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 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 request, resp; auto addrFlags = reinterpret_cast(&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 request; auto addrSession = reinterpret_cast(&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 BlobHandler::readBytes(std::uint16_t session, std::uint32_t offset, std::uint32_t length) { std::vector payload; payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) + sizeof(std::uint32_t)); auto data = reinterpret_cast(&session); std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload)); data = reinterpret_cast(&offset); std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); data = reinterpret_cast(&length); std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload)); return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload); } } // namespace ipmiblob