blob: 2cbecb12355fbe76e491d3c1fbce7f755e59082b [file] [log] [blame]
/*
* 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 "process.hpp"
#include "ipmi.hpp"
#include <cstring>
#include <ipmiblob/crc.hpp>
#include <ipmid/api-types.hpp>
#include <span>
#include <unordered_map>
#include <utility>
#include <vector>
namespace blobs
{
/* Used by all commands with data. */
struct BmcRx
{
uint16_t crc;
uint8_t data; /* one byte minimum of data. */
} __attribute__((packed));
static const std::unordered_map<BlobOEMCommands, IpmiBlobHandler> handlers = {
{BlobOEMCommands::bmcBlobGetCount, getBlobCount},
{BlobOEMCommands::bmcBlobEnumerate, enumerateBlob},
{BlobOEMCommands::bmcBlobOpen, openBlob},
{BlobOEMCommands::bmcBlobRead, readBlob},
{BlobOEMCommands::bmcBlobWrite, writeBlob},
{BlobOEMCommands::bmcBlobCommit, commitBlob},
{BlobOEMCommands::bmcBlobClose, closeBlob},
{BlobOEMCommands::bmcBlobDelete, deleteBlob},
{BlobOEMCommands::bmcBlobStat, statBlob},
{BlobOEMCommands::bmcBlobSessionStat, sessionStatBlob},
{BlobOEMCommands::bmcBlobWriteMeta, writeMeta},
};
IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span<const uint8_t> data)
{
size_t requestLength = data.size();
/* We know dataLen is at least 1 already */
auto command = static_cast<BlobOEMCommands>(cmd);
/* Validate it's at least well-formed. */
if (!validateRequestLength(command, requestLength))
{
return [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseReqDataLenInvalid();
};
}
/* If there is a payload. */
if (requestLength > sizeof(cmd))
{
/* Verify the request includes: command, crc16, data */
if (requestLength < sizeof(struct BmcRx))
{
return [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseReqDataLenInvalid();
};
}
/* We don't include the command byte at offset 0 as part of the crc
* payload area or the crc bytes at the beginning.
*/
size_t requestBodyLen = requestLength - 3;
/* We start after the command byte. */
std::vector<uint8_t> bytes(requestBodyLen);
/* It likely has a well-formed payload.
* Get the first two bytes of the request for crc.
*/
uint16_t crc;
if (data.size() < sizeof(crc))
{
return [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseReqDataLenInvalid();
};
}
std::memcpy(&crc, data.data(), sizeof(crc));
/* Set the in-place CRC to zero.
* Remove the first two bytes for crc and get the reset of the request.
*/
data = data.subspan(sizeof(crc));
/* Crc expected but didn't match. */
if (crc != ipmiblob::generateCrc(
std::vector<uint8_t>(data.begin(), data.end())))
{
return [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseUnspecifiedError();
};
};
}
/* Grab the corresponding handler for the command. */
auto found = handlers.find(command);
if (found == handlers.end())
{
return [](ManagerInterface*, std::span<const uint8_t>) {
return ipmi::responseInvalidFieldRequest();
};
}
return found->second;
}
Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
std::span<const uint8_t> data, size_t maxSize)
{
Resp result = cmd(mgr, data);
if (std::get<0>(result) != ipmi::ccSuccess)
{
return result;
}
std::vector<uint8_t>& response = std::get<0>(
// std::variant<std::vector<uint8_t>>
*std::get<1>(result));
size_t replyLength = response.size();
/* The command, whatever it was, returned success. */
if (replyLength == 0)
{
return result;
}
/* Read can return 0 bytes, and just a CRC, otherwise you need a CRC and 1
* byte, therefore the limit is 2 bytes.
*/
if (replyLength < (sizeof(uint16_t)))
{
return ipmi::responseUnspecifiedError();
}
/* Make sure the reply size fits the ipmi buffer */
if (replyLength > maxSize)
{
return ipmi::responseResponseError();
}
/* The command, whatever it was, replied, so let's set the CRC. */
std::span<const uint8_t> responseView = response;
responseView = responseView.subspan(sizeof(uint16_t));
std::vector<std::uint8_t> crcBuffer(responseView.begin(),
responseView.end());
/* Copy the CRC into place. */
uint16_t crcValue = ipmiblob::generateCrc(crcBuffer);
if (response.size() < sizeof(crcValue))
{
return ipmi::responseReqDataLenInvalid();
}
std::memcpy(response.data(), &crcValue, sizeof(crcValue));
return result;
}
Resp handleBlobCommand(uint8_t cmd, std::vector<uint8_t> data, size_t maxSize)
{
/* on failure rc is set to the corresponding IPMI error. */
return processBlobCommand(validateBlobCommand(cmd, data), getBlobManager(),
data, maxSize);
}
} // namespace blobs