summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-12-12 13:53:53 -0600
committerMatt Spinler <spinler@us.ibm.com>2020-01-27 08:25:12 -0600
commit5c350fdfb1a92ebdf33d960cc69ac16413974889 (patch)
tree98ad165aa8007f9cb6376f41f6e0654bfe14d719
parenta19b6234410703c3b45d30636978e35f07f18394 (diff)
downloadphosphor-logging-5c350fdfb1a92ebdf33d960cc69ac16413974889.tar.gz
phosphor-logging-5c350fdfb1a92ebdf33d960cc69ac16413974889.zip
PEL: Add PLDM command handler
Derive a PLDMInterface class from the HostInterface class to implement sending the 'new file available' PLDM command to notify the host of the ID and size of PELs. The command response is received asynchronously by watching for activity on the MCTP file descriptor that was used for the command. If a response isn't received in 10 seconds, it will be considered a failure. Both on response success and failure the class will call the registered callback function and pass it the result. Signed-off-by: Matt Spinler <spinler@us.ibm.com> Change-Id: Ica00da590628cc81114a48e2831a436dc2115269
-rw-r--r--configure.ac8
-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
4 files changed, 451 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac
index f3dee4a..8481175 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,11 +163,13 @@ AS_IF([test "x$enable_openpower_pel_extension" == "xyes"],
[AC_CHECK_HEADER(
nlohmann/json.hpp,
[],
- [AC_MSG_ERROR([Could not find nlohmann/json.hpp])]])
- [AC_CHECK_HEADER(
+ [AC_MSG_ERROR([Could not find nlohmann/json.hpp])])
+ AC_CHECK_HEADER(
fifo_map.hpp,
[],
- [AC_MSG_ERROR([Could not find fifo_map.hpp])]])
+ [AC_MSG_ERROR([Could not find fifo_map.hpp])])
+
+ AX_PKG_CHECK_MODULES([LIBPLDM], [libpldm])]
)
AC_CONFIG_HEADERS([config.h])
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