diff options
author | Patrick Venture <venture@google.com> | 2018-09-12 08:53:29 -0700 |
---|---|---|
committer | Patrick Venture <venture@google.com> | 2018-09-15 13:11:30 -0700 |
commit | ef3aeadc9be37c47d0627e576e81a74a5bb9e94f (patch) | |
tree | 95c183977468b107bdd3faaaa988b5d853be2f00 /ipmi.cpp | |
parent | baa73da1abaaf05ea1133319405fb2b891825618 (diff) | |
download | phosphor-ipmi-blobs-ef3aeadc9be37c47d0627e576e81a74a5bb9e94f.tar.gz phosphor-ipmi-blobs-ef3aeadc9be37c47d0627e576e81a74a5bb9e94f.zip |
initial drop of phosphor-ipmi-blobs
This implements a majority of the OEM IPMI BLOBS protocol. The only
piece missing from this is the timed expiration of sessions.
Change-Id: I82c9d17b625c94fc3340edcfabbbf1ffeb5ad7ac
Signed-off-by: Patrick Venture <venture@google.com>
Diffstat (limited to 'ipmi.cpp')
-rw-r--r-- | ipmi.cpp | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/ipmi.cpp b/ipmi.cpp new file mode 100644 index 0000000..6942b11 --- /dev/null +++ b/ipmi.cpp @@ -0,0 +1,324 @@ +/* + * 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 "ipmi.hpp" + +#include <cstring> +#include <string> +#include <unordered_map> + +namespace blobs +{ + +bool validateRequestLength(BlobOEMCommands command, size_t requestLen) +{ + /* The smallest string is one letter and the nul-terminator. */ + static const int kMinStrLen = 2; + + static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = { + {BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)}, + {BlobOEMCommands::bmcBlobOpen, + sizeof(struct BmcBlobOpenTx) + kMinStrLen}, + {BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)}, + {BlobOEMCommands::bmcBlobDelete, + sizeof(struct BmcBlobDeleteTx) + kMinStrLen}, + {BlobOEMCommands::bmcBlobStat, + sizeof(struct BmcBlobStatTx) + kMinStrLen}, + {BlobOEMCommands::bmcBlobSessionStat, + sizeof(struct BmcBlobSessionStatTx)}, + {BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)}, + {BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)}, + {BlobOEMCommands::bmcBlobWrite, + sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)}, + }; + + auto results = minimumLengths.find(command); + if (results == minimumLengths.end()) + { + /* Valid length by default if we don't care. */ + return true; + } + + /* If the request is shorter than the minimum, it's invalid. */ + if (requestLen < results->second) + { + return false; + } + + return true; +} + +std::string stringFromBuffer(const char* start, size_t length) +{ + if (!start) + { + return ""; + } + + auto end = static_cast<const char*>(std::memchr(start, '\0', length)); + if (end != &start[length - 1]) + { + return ""; + } + + return (end == nullptr) ? std::string() : std::string(start, end); +} + +ipmi_ret_t getBlobCount(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + struct BmcBlobCountRx resp; + resp.crc = 0; + resp.blobCount = mgr->buildBlobList(); + + /* Copy the response into the reply buffer */ + std::memcpy(replyCmdBuf, &resp, sizeof(resp)); + (*dataLen) = sizeof(resp); + + return IPMI_CC_OK; +} + +ipmi_ret_t enumerateBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + /* Verify datalen is >= sizeof(request) */ + struct BmcBlobEnumerateTx request; + auto reply = reinterpret_cast<struct BmcBlobEnumerateRx*>(replyCmdBuf); + + std::memcpy(&request, reqBuf, sizeof(request)); + + std::string blobId = mgr->getBlobId(request.blobIdx); + if (blobId == "") + { + return IPMI_CC_INVALID; + } + + /* TODO(venture): Need to do a hard-code check against the maximum + * reply buffer size. */ + reply->crc = 0; + /* Explicilty copies the NUL-terminator. */ + std::memcpy(&reply->blobId, blobId.c_str(), blobId.length() + 1); + + (*dataLen) = sizeof(reply->crc) + blobId.length() + 1; + + return IPMI_CC_OK; +} + +ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + size_t requestLen = (*dataLen); + auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(reqBuf); + uint16_t session; + + std::string path = stringFromBuffer( + request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx))); + if (path.empty()) + { + return IPMI_CC_INVALID; + } + + /* Attempt to open. */ + if (!mgr->open(request->flags, path, &session)) + { + return IPMI_CC_INVALID; + } + + struct BmcBlobOpenRx reply; + reply.crc = 0; + reply.sessionId = session; + + std::memcpy(replyCmdBuf, &reply, sizeof(reply)); + (*dataLen) = sizeof(reply); + + return IPMI_CC_OK; +} + +ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + struct BmcBlobCloseTx request; + std::memcpy(&request, reqBuf, sizeof(request)); + + /* Attempt to close. */ + if (!mgr->close(request.sessionId)) + { + return IPMI_CC_INVALID; + } + + (*dataLen) = 0; + return IPMI_CC_OK; +} + +ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + size_t requestLen = (*dataLen); + auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf); + + std::string path = stringFromBuffer( + request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx))); + if (path.empty()) + { + return IPMI_CC_INVALID; + } + + /* Attempt to delete. */ + if (!mgr->deleteBlob(path)) + { + return IPMI_CC_INVALID; + } + + (*dataLen) = 0; + return IPMI_CC_OK; +} + +static ipmi_ret_t returnStatBlob(struct BlobMeta* meta, uint8_t* replyCmdBuf, + size_t* dataLen) +{ + struct BmcBlobStatRx reply; + reply.crc = 0; + reply.blobState = meta->blobState; + reply.size = meta->size; + reply.metadataLen = meta->metadata.size(); + + std::memcpy(replyCmdBuf, &reply, sizeof(reply)); + + /* If there is metadata, copy it over. */ + if (meta->metadata.size()) + { + uint8_t* metadata = &replyCmdBuf[sizeof(reply)]; + std::memcpy(metadata, meta->metadata.data(), reply.metadataLen); + } + + (*dataLen) = sizeof(reply) + reply.metadataLen; + return IPMI_CC_OK; +} + +ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + size_t requestLen = (*dataLen); + auto request = reinterpret_cast<const struct BmcBlobStatTx*>(reqBuf); + + std::string path = stringFromBuffer( + request->blobId, (requestLen - sizeof(struct BmcBlobStatTx))); + if (path.empty()) + { + return IPMI_CC_INVALID; + } + + /* Attempt to stat. */ + struct BlobMeta meta; + if (!mgr->stat(path, &meta)) + { + return IPMI_CC_INVALID; + } + + return returnStatBlob(&meta, replyCmdBuf, dataLen); +} + +ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + struct BmcBlobSessionStatTx request; + std::memcpy(&request, reqBuf, sizeof(request)); + + /* Attempt to stat. */ + struct BlobMeta meta; + + if (!mgr->stat(request.sessionId, &meta)) + { + return IPMI_CC_INVALID; + } + + return returnStatBlob(&meta, replyCmdBuf, dataLen); +} + +ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + size_t requestLen = (*dataLen); + auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf); + + /* Sanity check the commitDataLen */ + if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx))) + { + return IPMI_CC_INVALID; + } + + std::vector<uint8_t> data(request->commitDataLen); + std::memcpy(data.data(), request->commitData, request->commitDataLen); + + if (!mgr->commit(request->sessionId, data)) + { + return IPMI_CC_INVALID; + } + + (*dataLen) = 0; + return IPMI_CC_OK; +} + +ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + struct BmcBlobReadTx request; + std::memcpy(&request, reqBuf, sizeof(request)); + + /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet. + */ + + std::vector<uint8_t> result = + mgr->read(request.sessionId, request.offset, request.requestedSize); + + /* If the Read fails, it returns success but with only the crc and 0 bytes + * of data. + * If there was data returned, copy into the reply buffer. + */ + (*dataLen) = sizeof(struct BmcBlobReadRx); + + if (result.size()) + { + uint8_t* output = &replyCmdBuf[sizeof(struct BmcBlobReadRx)]; + std::memcpy(output, result.data(), result.size()); + + (*dataLen) = sizeof(struct BmcBlobReadRx) + result.size(); + } + + return IPMI_CC_OK; +} + +ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) +{ + size_t requestLen = (*dataLen); + auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf); + + uint32_t size = requestLen - sizeof(struct BmcBlobWriteTx); + std::vector<uint8_t> data(size); + + std::memcpy(data.data(), request->data, size); + + /* Attempt to write the bytes. */ + if (!mgr->write(request->sessionId, request->offset, data)) + { + return IPMI_CC_INVALID; + } + + return IPMI_CC_OK; +} + +} // namespace blobs |