summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/openpower-pels/openpower-pels.mk7
-rw-r--r--extensions/openpower-pels/pldm_interface.cpp279
-rw-r--r--extensions/openpower-pels/pldm_interface.hpp160
3 files changed, 446 insertions, 0 deletions
diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk
index 220668f..e510b42 100644
--- a/extensions/openpower-pels/openpower-pels.mk
+++ b/extensions/openpower-pels/openpower-pels.mk
@@ -2,12 +2,19 @@ phosphor_log_manager_SOURCES += \
extensions/openpower-pels/entry_points.cpp \
extensions/openpower-pels/host_notifier.cpp \
extensions/openpower-pels/manager.cpp \
+ extensions/openpower-pels/pldm_interface.cpp \
extensions/openpower-pels/repository.cpp \
extensions/openpower-pels/user_data.cpp
phosphor_log_manager_LDADD = \
libpel.la
+phosphor_log_manager_LDFLAGS += \
+ $(LIBPLDM_LIBS)
+
+phosphor_log_manager_CFLAGS = \
+ $(LIBPLDM_CFLAGS)
+
noinst_LTLIBRARIES = libpel.la
libpel_la_SOURCES = \
diff --git a/extensions/openpower-pels/pldm_interface.cpp b/extensions/openpower-pels/pldm_interface.cpp
new file mode 100644
index 0000000..8e60315
--- /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(size)>
+ 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
diff --git a/extensions/openpower-pels/pldm_interface.hpp b/extensions/openpower-pels/pldm_interface.hpp
new file mode 100644
index 0000000..0d679a1
--- /dev/null
+++ b/extensions/openpower-pels/pldm_interface.hpp
@@ -0,0 +1,160 @@
+#pragma once
+
+#include "host_interface.hpp"
+
+#include <libpldm/pldm.h>
+
+#include <chrono>
+#include <memory>
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/source/io.hpp>
+#include <sdeventplus/utility/timer.hpp>
+
+namespace openpower::pels
+{
+
+/**
+ * @class PLDMInterface
+ *
+ * This class handles sending the 'new file available' PLDM
+ * command to the host to notify it of a new PEL's ID and size.
+ *
+ * The command response is asynchronous.
+ */
+class PLDMInterface : public HostInterface
+{
+ public:
+ PLDMInterface() = delete;
+ PLDMInterface(const PLDMInterface&) = default;
+ PLDMInterface& operator=(const PLDMInterface&) = default;
+ PLDMInterface(PLDMInterface&&) = default;
+ PLDMInterface& operator=(PLDMInterface&&) = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] event - The sd_event object pointer
+ * @param[in] dataIface - The DataInterface object
+ */
+ PLDMInterface(sd_event* event, DataInterfaceBase& dataIface) :
+ HostInterface(event, dataIface),
+ _receiveTimer(
+ event,
+ std::bind(std::mem_fn(&PLDMInterface::receiveTimerExpired), this))
+ {
+ readEID();
+ }
+
+ /**
+ * @brief Destructor
+ */
+ ~PLDMInterface();
+
+ /**
+ * @brief Kicks off the send of the 'new file available' command
+ * to send up the ID and size of the new PEL.
+ *
+ * @param[in] id - The PEL ID
+ * @param[in] size - The PEL size in bytes
+ *
+ * @return CmdStatus - the success/fail status of the send
+ */
+ CmdStatus sendNewLogCmd(uint32_t id, uint32_t size) override;
+
+ /**
+ * @brief Cancels waiting for a command response
+ */
+ void cancelCmd() override;
+
+ private:
+ /**
+ * @brief The asynchronous callback for getting the response
+ * of the 'new file available' command.
+ *
+ * Calls the response callback that is registered.
+ *
+ * @param[in] io - The event source object
+ * @param[in] fd - The FD used
+ * @param[in] revents - The event bits
+ */
+ void receive(sdeventplus::source::IO& io, int fd,
+ uint32_t revents) override;
+
+ /**
+ * @brief Function called when the receive timer expires.
+ *
+ * This is considered a failure and so will invoke the
+ * registered response callback function with a failure
+ * indication.
+ */
+ void receiveTimerExpired();
+
+ /**
+ * @brief Configures the sdeventplus::source::IO object to
+ * call receive() on EPOLLIN activity on the PLDM FD
+ * which is used for command responses.
+ */
+ void registerReceiveCallback();
+
+ /**
+ * @brief Reads the MCTP endpoint ID out of a file
+ */
+ void readEID();
+
+ /**
+ * @brief Opens the PLDM file descriptor
+ */
+ void open();
+
+ /**
+ * @brief Reads the PLDM instance ID to use for the upcoming
+ * command.
+ */
+ void readInstanceID();
+
+ /**
+ * @brief Encodes and sends the PLDM 'new file available' cmd
+ *
+ * @param[in] id - The PEL ID
+ * @param[in] size - The PEL size in bytes
+ */
+ void doSend(uint32_t id, uint32_t size);
+
+ /**
+ * @brief Closes the PLDM file descriptor
+ */
+ void closeFD();
+
+ /**
+ * @brief The MCTP endpoint ID
+ */
+ mctp_eid_t _eid;
+
+ /**
+ * @brief The PLDM instance ID of the current command
+ */
+ uint8_t _instanceID;
+
+ /**
+ * @brief The PLDM command file descriptor for the current command
+ */
+ int _fd = -1;
+
+ /**
+ * @brief The event object for handling callbacks on the PLDM FD
+ */
+ std::unique_ptr<sdeventplus::source::IO> _source;
+
+ /**
+ * @brief A timer to only allow a certain amount of time for the
+ * async PLDM receive before it is considered a failure.
+ */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _receiveTimer;
+
+ /**
+ * @brief The command timeout value
+ */
+ const std::chrono::milliseconds _receiveTimeout{10000};
+};
+
+} // namespace openpower::pels
OpenPOWER on IntegriCloud