summaryrefslogtreecommitdiffstats
path: root/extensions/openpower-pels/pldm_interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/openpower-pels/pldm_interface.cpp')
-rw-r--r--extensions/openpower-pels/pldm_interface.cpp279
1 files changed, 279 insertions, 0 deletions
diff --git a/extensions/openpower-pels/pldm_interface.cpp b/extensions/openpower-pels/pldm_interface.cpp
new file mode 100644
index 0000000..20ecc53
--- /dev/null
+++ b/extensions/openpower-pels/pldm_interface.cpp
@@ -0,0 +1,279 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "pldm_interface.hpp"
+
+#include <libpldm/base.h>
+#include <libpldm/file_io.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <phosphor-logging/log.hpp>
+
+namespace openpower::pels
+{
+
+using namespace phosphor::logging;
+using namespace sdeventplus;
+using namespace sdeventplus::source;
+
+constexpr auto eidPath = "/usr/share/pldm/host_eid";
+constexpr mctp_eid_t defaultEIDValue = 9;
+
+constexpr uint16_t pelFileType = 0;
+
+PLDMInterface::~PLDMInterface()
+{
+ closeFD();
+}
+
+void PLDMInterface::closeFD()
+{
+ if (_fd >= 0)
+ {
+ close(_fd);
+ _fd = -1;
+ }
+}
+
+void PLDMInterface::readEID()
+{
+ _eid = defaultEIDValue;
+
+ std::ifstream eidFile{eidPath};
+ if (!eidFile.good())
+ {
+ log<level::ERR>("Could not open host EID file");
+ }
+ else
+ {
+ std::string eid;
+ eidFile >> eid;
+ if (!eid.empty())
+ {
+ _eid = atoi(eid.c_str());
+ }
+ else
+ {
+ log<level::ERR>("EID file was empty");
+ }
+ }
+}
+
+void PLDMInterface::open()
+{
+ _fd = pldm_open();
+ if (_fd < 0)
+ {
+ auto e = errno;
+ log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e),
+ entry("RC=%d\n", _fd));
+ throw std::exception{};
+ }
+}
+
+CmdStatus PLDMInterface::sendNewLogCmd(uint32_t id, uint32_t size)
+{
+ try
+ {
+ closeFD();
+
+ open();
+
+ readInstanceID();
+
+ registerReceiveCallback();
+
+ doSend(id, size);
+ }
+ catch (const std::exception& e)
+ {
+ closeFD();
+
+ _inProgress = false;
+ _source.reset();
+ return CmdStatus::failure;
+ }
+
+ _inProgress = true;
+ _receiveTimer.restartOnce(_receiveTimeout);
+ return CmdStatus::success;
+}
+
+void PLDMInterface::registerReceiveCallback()
+{
+ _source = std::make_unique<IO>(
+ _event, _fd, EPOLLIN,
+ std::bind(std::mem_fn(&PLDMInterface::receive), this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3));
+}
+
+void PLDMInterface::readInstanceID()
+{
+ try
+ {
+ _instanceID = _dataIface.getPLDMInstanceID(_eid);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ "Failed to get instance ID from PLDM Requester D-Bus daemon",
+ entry("ERROR=%s", e.what()));
+ throw;
+ }
+}
+
+void PLDMInterface::doSend(uint32_t id, uint32_t size)
+{
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(pelFileType) +
+ sizeof(id) + sizeof(uint64_t)>
+ requestMsg;
+
+ auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+ auto rc = encode_new_file_req(_instanceID, pelFileType, id, size, request);
+ if (rc != PLDM_SUCCESS)
+ {
+ log<level::ERR>("encode_new_file_req failed", entry("RC=%d", rc));
+ throw std::exception{};
+ }
+
+ rc = pldm_send(_eid, _fd, requestMsg.data(), requestMsg.size());
+ if (rc < 0)
+ {
+ auto e = errno;
+ log<level::ERR>("pldm_send failed", entry("RC=%d", rc),
+ entry("ERRNO=%d", e));
+
+ throw std::exception{};
+ }
+}
+
+void PLDMInterface::receive(IO& io, int fd, uint32_t revents)
+{
+ if (!(revents & EPOLLIN))
+ {
+ return;
+ }
+
+ uint8_t* responseMsg = nullptr;
+ size_t responseSize = 0;
+ ResponseStatus status = ResponseStatus::success;
+
+ auto rc = pldm_recv(_eid, fd, _instanceID, &responseMsg, &responseSize);
+ if (rc < 0)
+ {
+ if (rc == PLDM_REQUESTER_INSTANCE_ID_MISMATCH)
+ {
+ // We got a response to someone else's message. Ignore it.
+ return;
+ }
+ else if (rc == PLDM_REQUESTER_NOT_RESP_MSG)
+ {
+ // Due to the MCTP loopback, we may get notified of the message
+ // we just sent.
+ return;
+ }
+
+ auto e = errno;
+ log<level::ERR>("pldm_recv failed", entry("RC=%d", rc),
+ entry("ERRNO=%d", e));
+ status = ResponseStatus::failure;
+
+ responseMsg = nullptr;
+ }
+
+ _inProgress = false;
+ _receiveTimer.setEnabled(false);
+ closeFD();
+ _source.reset();
+
+ if (status == ResponseStatus::success)
+ {
+ uint8_t completionCode = 0;
+ auto response = reinterpret_cast<pldm_msg*>(responseMsg);
+
+ auto decodeRC = decode_new_file_resp(response, PLDM_NEW_FILE_RESP_BYTES,
+ &completionCode);
+ if (decodeRC < 0)
+ {
+ log<level::ERR>("decode_new_file_resp failed",
+ entry("RC=%d", decodeRC));
+ status = ResponseStatus::failure;
+ }
+ else
+ {
+ if (completionCode != PLDM_SUCCESS)
+ {
+ log<level::ERR>("Bad PLDM completion code",
+ entry("COMPLETION_CODE=%d", completionCode));
+ status = ResponseStatus::failure;
+ }
+ }
+ }
+
+ if (_responseFunc)
+ {
+ try
+ {
+ (*_responseFunc)(status);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("PLDM response callback threw an exception",
+ entry("ERROR=%s", e.what()));
+ }
+ }
+
+ if (responseMsg)
+ {
+ free(responseMsg);
+ }
+}
+
+void PLDMInterface::receiveTimerExpired()
+{
+ log<level::ERR>("Timed out waiting for PLDM response");
+ cancelCmd();
+
+ if (_responseFunc)
+ {
+ try
+ {
+ (*_responseFunc)(ResponseStatus::failure);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("PLDM response callback threw an exception",
+ entry("ERROR=%s", e.what()));
+ }
+ }
+}
+
+void PLDMInterface::cancelCmd()
+{
+ _inProgress = false;
+ _source.reset();
+
+ if (_receiveTimer.isEnabled())
+ {
+ _receiveTimer.setEnabled(false);
+ }
+
+ closeFD();
+}
+
+} // namespace openpower::pels
OpenPOWER on IntegriCloud