summaryrefslogtreecommitdiffstats
path: root/ipmi.cpp
diff options
context:
space:
mode:
authorPatrick Venture <venture@google.com>2018-09-12 08:53:29 -0700
committerPatrick Venture <venture@google.com>2018-09-15 13:11:30 -0700
commitef3aeadc9be37c47d0627e576e81a74a5bb9e94f (patch)
tree95c183977468b107bdd3faaaa988b5d853be2f00 /ipmi.cpp
parentbaa73da1abaaf05ea1133319405fb2b891825618 (diff)
downloadphosphor-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.cpp324
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
OpenPOWER on IntegriCloud