Added PLDM Dump offload command

Added support to sending the SetNumericEffecterValue
PLDM command to the host to start dump offload.

Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
Change-Id: I30fe59b198b55ad439a182877c6b21cfd070245b
diff --git a/Makefile.am b/Makefile.am
index b9c9928..751f3a7 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,8 @@
 	dump_utils.hpp \
 	watch.hpp \
 	elog_watch.hpp \
-	dump_serialize.hpp
+	dump_serialize.hpp \
+	pldm_interface.hpp
 
 nobase_nodist_include_HEADERS = \
 	xyz/openbmc_project/Dump/Internal/Create/server.hpp
@@ -26,7 +27,9 @@
 	watch.cpp \
 	xyz/openbmc_project/Dump/Internal/Create/server.cpp \
 	elog_watch.cpp \
-	dump_serialize.cpp
+	dump_serialize.cpp \
+	pldm_interface.cpp \
+	dump_utils.cpp
 
 phosphor_dump_monitor_SOURCES = \
 	watch.cpp \
@@ -36,7 +39,8 @@
 phosphor_dump_manager_CXXFLAGS = \
 	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
 	$(SDBUSPLUS_CFLAGS) \
-	$(PHOSPHOR_LOGGING_CFLAGS)
+	$(PHOSPHOR_LOGGING_CFLAGS) \
+	$(LIBPLDM_CFLAGS)
 
 phosphor_dump_monitor_CXXFLAGS = \
 	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
@@ -46,7 +50,8 @@
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
 	$(SDBUSPLUS_LIBS) \
 	$(PHOSPHOR_LOGGING_LIBS) \
-	-lstdc++fs
+	-lstdc++fs \
+	$(LIBPLDM_LIBS)
 
 phosphor_dump_monitor_LDADD = \
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
diff --git a/configure.ac b/configure.ac
old mode 100644
new mode 100755
index c6dcb65..5a3eb3a
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,7 @@
 PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces])
 PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
+PKG_CHECK_MODULES([LIBPLDM], [libpldm])
 
 # Check for sdbus++
 AC_PATH_PROG([SDBUSPLUSPLUS], [sdbus++])
diff --git a/dump_utils.cpp b/dump_utils.cpp
new file mode 100644
index 0000000..b013706
--- /dev/null
+++ b/dump_utils.cpp
@@ -0,0 +1,49 @@
+#include "dump_utils.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace phosphor
+{
+namespace dump
+{
+
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                       const std::string& interface)
+{
+    constexpr auto objectMapperName = "xyz.openbmc_project.ObjectMapper";
+    constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
+    using namespace phosphor::logging;
+
+    auto method = bus.new_method_call(objectMapperName, objectMapperPath,
+                                      objectMapperName, "GetObject");
+
+    method.append(path);
+    method.append(std::vector<std::string>({interface}));
+
+    std::vector<std::pair<std::string, std::vector<std::string>>> response;
+
+    try
+    {
+        auto reply = bus.call(method);
+        reply.read(response);
+        if (response.empty())
+        {
+            log<level::ERR>("Error in mapper response for getting service name",
+                            entry("PATH=%s", path.c_str()),
+                            entry("INTERFACE=%s", interface.c_str()));
+            return std::string{};
+        }
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Error in mapper method call",
+                        entry("ERROR=%s", e.what()),
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", interface.c_str()));
+        return std::string{};
+    }
+    return response[0].first;
+}
+
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_utils.hpp b/dump_utils.hpp
index ba351e1..a2c2604 100644
--- a/dump_utils.hpp
+++ b/dump_utils.hpp
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include <memory>
+#include <sdbusplus/bus.hpp>
 
 namespace phosphor
 {
@@ -59,5 +60,16 @@
     }
 };
 
+/**
+ * @brief Get the bus service
+ *
+ * @param[in] bus - Bus to attach to.
+ * @param[in] path - D-Bus path name.
+ * @param[in] interface - D-Bus interface name.
+ * @return the bus service as a string
+ **/
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                       const std::string& interface);
+
 } // namespace dump
 } // namespace phosphor
diff --git a/pldm_interface.cpp b/pldm_interface.cpp
new file mode 100644
index 0000000..57d7a2c
--- /dev/null
+++ b/pldm_interface.cpp
@@ -0,0 +1,166 @@
+/**
+ * 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 "dump_utils.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <libpldm/base.h>
+#include <libpldm/platform.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace pldm
+{
+
+using namespace phosphor::logging;
+
+constexpr auto eidPath = "/usr/share/pldm/host_eid";
+constexpr mctp_eid_t defaultEIDValue = 9;
+
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+void closeFD(int fd)
+{
+    if (fd >= 0)
+    {
+        close(fd);
+    }
+}
+
+mctp_eid_t readEID()
+{
+    mctp_eid_t eid = defaultEIDValue;
+
+    std::ifstream eidFile{eidPath};
+    if (!eidFile.good())
+    {
+        log<level::ERR>("Could not open host EID file");
+        elog<InternalFailure>();
+    }
+    else
+    {
+        std::string eid;
+        eidFile >> eid;
+        if (!eid.empty())
+        {
+            eid = strtol(eid.c_str(), nullptr, 10);
+        }
+        else
+        {
+            log<level::ERR>("EID file was empty");
+            elog<InternalFailure>();
+        }
+    }
+
+    return eid;
+}
+
+int open()
+{
+    auto fd = pldm_open();
+    if (fd < 0)
+    {
+        auto e = errno;
+        log<level::ERR>("pldm_open failed", entry("ERRNO=%d", e),
+                        entry("FD=%d\n", fd));
+        elog<InternalFailure>();
+    }
+    return fd;
+}
+
+void requestOffload(uint32_t id)
+{
+    uint16_t effecterId = 0x05; // TODO PhyP temporary Hardcoded value.
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(id) +
+                            sizeof(uint8_t)>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    std::array<uint8_t, sizeof(id)> effecterValue{};
+
+    memcpy(effecterValue.data(), &id, sizeof(id));
+
+    mctp_eid_t eid = readEID();
+
+    auto instanceID = getPLDMInstanceID(eid);
+
+    auto rc = encode_set_numeric_effecter_value_req(
+        instanceID, effecterId, PLDM_EFFECTER_DATA_SIZE_UINT32,
+        effecterValue.data(), request,
+        requestMsg.size() - sizeof(pldm_msg_hdr));
+
+    if (rc != PLDM_SUCCESS)
+    {
+        log<level::ERR>("Message encode failure. ", entry("RC=%d", rc));
+        elog<InternalFailure>();
+    }
+
+    uint8_t* responseMsg = nullptr;
+    size_t responseMsgSize{};
+
+    auto fd = open();
+
+    rc = pldm_send_recv(eid, fd, requestMsg.data(), requestMsg.size(),
+                        &responseMsg, &responseMsgSize);
+    if (rc < 0)
+    {
+        closeFD(fd);
+        auto e = errno;
+        log<level::ERR>("pldm_send failed", entry("RC=%d", rc),
+                        entry("ERRNO=%d", e));
+        elog<InternalFailure>();
+    }
+    pldm_msg* response = reinterpret_cast<pldm_msg*>(responseMsg);
+    log<level::INFO>(
+        "Done. PLDM message",
+        entry("RC=%d", static_cast<uint16_t>(response->payload[0])));
+
+    closeFD(fd);
+}
+
+uint8_t getPLDMInstanceID(uint8_t eid)
+{
+
+    constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
+    constexpr auto pldm = "/xyz/openbmc_project/pldm";
+
+    auto bus = sdbusplus::bus::new_default();
+    auto service = phosphor::dump::getService(bus, pldm, pldmRequester);
+
+    auto method = bus.new_method_call(service.c_str(), pldm, pldmRequester,
+                                      "GetInstanceId");
+    method.append(eid);
+    auto reply = bus.call(method);
+
+    uint8_t instanceID = 0;
+    reply.read(instanceID);
+
+    return instanceID;
+}
+} // namespace pldm
+} // namespace dump
+} // namespace phosphor
diff --git a/pldm_interface.hpp b/pldm_interface.hpp
new file mode 100644
index 0000000..fffa682
--- /dev/null
+++ b/pldm_interface.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <libpldm/pldm.h>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace pldm
+{
+
+/**
+ * PLDMInterface
+ *
+ * Handles sending the SetNumericEffecterValue PLDM
+ * command to the host to start dump offload.
+ *
+ */
+
+/**
+ * @brief Kicks of the SetNumericEffecterValue command to
+ *        start offload the dump
+ *
+ * @param[in] id - The Dump Source ID.
+ *
+ */
+
+void requestOffload(uint32_t id);
+
+/**
+ * @brief Reads the MCTP endpoint ID out of a file
+ */
+mctp_eid_t readEID();
+
+/**
+ * @brief Opens the PLDM file descriptor
+ */
+int open();
+
+/**
+ * @brief Closes the PLDM file descriptor
+ */
+void closeFD(int fd);
+
+/**
+ * @brief Returns the PLDM instance ID to use for PLDM commands
+ *
+ * @param[in] eid - The PLDM EID
+ *
+ * @return uint8_t - The instance ID
+ **/
+uint8_t getPLDMInstanceID(uint8_t eid);
+
+} // namespace pldm
+} // namespace dump
+} // namespace phosphor