Add HostPDRHandler class

HostPDRHandler has an API to fetch PDRs from the host.

The class HostPDRHandler has a function fetchPDR which basically calls
GetPDR on the host using libpldm's pldm_send_recv API.

The retrieved PDRs are stored in the BMCs primary PDR repo.

Change-Id: Ifd727316caf37d49f17e117b32ee105f6d941e0e
Signed-off-by: Pavithra Barithaya <pbaritha@in.ibm.com>
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/host_pdr_handler.cpp b/host_pdr_handler.cpp
new file mode 100644
index 0000000..1e78846
--- /dev/null
+++ b/host_pdr_handler.cpp
@@ -0,0 +1,81 @@
+#include "host_pdr_handler.hpp"
+
+#include "libpldm/requester/pldm.h"
+
+namespace pldm
+{
+
+void HostPDRHandler::fetchPDR(const std::vector<uint32_t>& recordHandles)
+{
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                    PLDM_GET_PDR_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    for (auto recordHandle : recordHandles)
+    {
+        auto instanceId = requester.getInstanceId(mctp_eid);
+
+        auto rc =
+            encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART,
+                               UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
+        if (rc != PLDM_SUCCESS)
+        {
+            requester.markFree(mctp_eid, instanceId);
+            std::cerr << "Failed to encode_get_pdr_req, rc = " << rc << "\n";
+            return;
+        }
+
+        uint8_t* responseMsg = nullptr;
+        size_t responseMsgSize{};
+        auto requesterRc =
+            pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(),
+                           requestMsg.size(), &responseMsg, &responseMsgSize);
+        std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
+            responseMsg, std::free};
+        requester.markFree(mctp_eid, instanceId);
+        if (requesterRc != PLDM_REQUESTER_SUCCESS)
+        {
+            std::cerr << "Failed to send msg to fetch pdrs, rc = "
+                      << requesterRc << "\n";
+            return;
+        }
+
+        auto responsePtr =
+            reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get());
+        uint8_t completionCode{};
+        uint32_t nextRecordHandle{};
+        uint32_t nextDataTransferHandle{};
+        uint8_t transferFlag{};
+        uint16_t respCount{};
+        uint8_t transferCRC{};
+        rc = decode_get_pdr_resp(
+            responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
+            &completionCode, &nextRecordHandle, &nextDataTransferHandle,
+            &transferFlag, &respCount, nullptr, 0, &transferCRC);
+        if (rc != PLDM_SUCCESS)
+        {
+            std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc << "\n";
+        }
+        else
+        {
+            std::vector<uint8_t> pdr(respCount, 0);
+            rc = decode_get_pdr_resp(
+                responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
+                &completionCode, &nextRecordHandle, &nextDataTransferHandle,
+                &transferFlag, &respCount, pdr.data(), respCount, &transferCRC);
+            if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
+            {
+                std::cerr << "Failed to decode_get_pdr_resp: "
+                          << "rc=" << rc
+                          << ", cc=" << static_cast<int>(completionCode)
+                          << "\n";
+            }
+            else
+            {
+                pldm_pdr_add(repo, pdr.data(), respCount, 0);
+            }
+        }
+    }
+}
+
+} // namespace pldm
diff --git a/host_pdr_handler.hpp b/host_pdr_handler.hpp
new file mode 100644
index 0000000..b590e7c
--- /dev/null
+++ b/host_pdr_handler.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "dbus_impl_requester.hpp"
+#include "utils.hpp"
+
+#include <sdeventplus/event.hpp>
+#include <vector>
+
+#include "libpldm/base.h"
+#include "libpldm/platform.h"
+
+using namespace pldm::dbus_api;
+
+namespace pldm
+{
+
+/** @class HostPDRHandler
+ *  @brief This class can fetch and process PDRs from host firmware
+ *  @details Provides an API to fetch PDRs from the host firmware. Upon
+ *  receiving the PDRs, they are stored into the BMC's primary PDR repo.
+ *  Adjustments are made to entity association PDRs received from the host,
+ *  because they need to be assimilated into the BMC's entity association
+ *  tree. A PLDM event containing the record handles of the updated entity
+ *  association PDRs is sent to the host.
+ */
+class HostPDRHandler
+{
+  public:
+    HostPDRHandler() = delete;
+    HostPDRHandler(const HostPDRHandler&) = delete;
+    HostPDRHandler(HostPDRHandler&&) = delete;
+    HostPDRHandler& operator=(const HostPDRHandler&) = delete;
+    HostPDRHandler& operator=(HostPDRHandler&&) = delete;
+    ~HostPDRHandler() = default;
+
+    /** @brief Constructor
+     *  @param[in] mctp_fd - fd of MCTP communications socket
+     *  @param[in] mctp_eid - MCTP EID of host firmware
+     *  @param[in] event - reference of main event loop of pldmd
+     *  @param[in] repo - pointer to BMC's primary PDR repo
+     *  @param[in] requester - reference to Requester object
+     */
+    explicit HostPDRHandler(int mctp_fd, uint8_t mctp_eid,
+                            sdeventplus::Event& event, pldm_pdr* repo,
+                            Requester& requester) :
+        mctp_fd(mctp_fd),
+        mctp_eid(mctp_eid), event(event), repo(repo), requester(requester)
+    {
+    }
+
+    /** @brief fetch PDRs from host firmware. See @class.
+     *  @param[in] recordHandles - list of record handles pointing to host's
+     *             PDRs that need to be fetched.
+     */
+    void fetchPDR(const std::vector<uint32_t>& recordHandles);
+
+  private:
+    /** @brief fd of MCTP communications socket */
+    int mctp_fd;
+    /** @brief MCTP EID of host firmware */
+    uint8_t mctp_eid;
+    /** @brief reference of main event loop of pldmd, primarily used to schedule
+     *  work.
+     */
+    sdeventplus::Event& event;
+    /** @brief pointer to BMC's primary PDR repo, host PDRs are added here */
+    pldm_pdr* repo;
+    /** @brief reference to Requester object, primarily used to access API to
+     *  obtain PLDM instance id.
+     */
+    Requester& requester;
+};
+
+} // namespace pldm
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 696434b..893a709 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -3,6 +3,7 @@
 #include "config.h"
 
 #include "handler.hpp"
+#include "host_pdr_handler.hpp"
 #include "libpldmresponder/pdr.hpp"
 #include "libpldmresponder/pdr_utils.hpp"
 #include "utils.hpp"
@@ -53,8 +54,10 @@
 {
   public:
     Handler(const std::string& dir, pldm_pdr* repo,
+            HostPDRHandler* hostPDRHandler,
             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
-        pdrRepo(repo)
+        pdrRepo(repo),
+        hostPDRHandler(hostPDRHandler)
     {
         generate(dir, pdrRepo);
 
@@ -340,6 +343,7 @@
     pdr_utils::Repo pdrRepo;
     uint16_t nextEffecterId{};
     DbusObjMaps dbusObjMaps{};
+    HostPDRHandler* hostPDRHandler;
 };
 
 } // namespace platform
diff --git a/meson.build b/meson.build
index 923197a..d6fd8c9 100644
--- a/meson.build
+++ b/meson.build
@@ -56,6 +56,7 @@
 deps = [
   libpldm,
   libpldmresponder,
+  libpldmutils,
   dependency('sdbusplus'),
   dependency('sdeventplus'),
   dependency('phosphor-dbus-interfaces')
@@ -66,6 +67,7 @@
   'pldmd.cpp',
   'dbus_impl_requester.cpp',
   'instance_id.cpp',
+  'host_pdr_handler.cpp',
   implicit_include_directories: false,
   dependencies: deps,
   install: true,
diff --git a/pldmd.cpp b/pldmd.cpp
index 501de5a..030fcb1 100644
--- a/pldmd.cpp
+++ b/pldmd.cpp
@@ -1,4 +1,5 @@
 #include "dbus_impl_requester.hpp"
+#include "host_pdr_handler.hpp"
 #include "invoker.hpp"
 #include "libpldmresponder/base.hpp"
 #include "libpldmresponder/bios.hpp"
@@ -17,6 +18,7 @@
 
 #include <cstdio>
 #include <cstring>
+#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <iterator>
@@ -146,26 +148,6 @@
             break;
     }
 
-    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
-        pldm_pdr_init(), pldm_pdr_destroy);
-    std::unique_ptr<pldm_entity_association_tree,
-                    decltype(&pldm_entity_association_tree_destroy)>
-        entityTree(pldm_entity_association_tree_init(),
-                   pldm_entity_association_tree_destroy);
-
-    Invoker invoker{};
-    invoker.registerHandler(PLDM_BASE, std::make_unique<base::Handler>());
-    invoker.registerHandler(PLDM_BIOS, std::make_unique<bios::Handler>());
-    invoker.registerHandler(PLDM_PLATFORM, std::make_unique<platform::Handler>(
-                                               PDR_JSONS_DIR, pdrRepo.get()));
-    invoker.registerHandler(
-        PLDM_FRU, std::make_unique<fru::Handler>(FRU_JSONS_DIR, pdrRepo.get(),
-                                                 entityTree.get()));
-
-#ifdef OEM_IBM
-    invoker.registerHandler(PLDM_OEM, std::make_unique<oem_ibm::Handler>());
-#endif
-
     /* Create local socket. */
     int returnCode = 0;
     int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
@@ -176,6 +158,37 @@
         exit(EXIT_FAILURE);
     }
 
+    auto event = Event::get_default();
+    std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
+        pldm_pdr_init(), pldm_pdr_destroy);
+    std::unique_ptr<pldm_entity_association_tree,
+                    decltype(&pldm_entity_association_tree_destroy)>
+        entityTree(pldm_entity_association_tree_init(),
+                   pldm_entity_association_tree_destroy);
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");
+    std::unique_ptr<HostPDRHandler> hostPDRHandler;
+    auto hostEID = pldm::utils::readHostEID();
+    if (hostEID)
+    {
+        hostPDRHandler = std::make_unique<HostPDRHandler>(
+            sockfd, hostEID, event, pdrRepo.get(), dbusImplReq);
+    }
+
+    Invoker invoker{};
+    invoker.registerHandler(PLDM_BASE, std::make_unique<base::Handler>());
+    invoker.registerHandler(PLDM_BIOS, std::make_unique<bios::Handler>());
+    invoker.registerHandler(
+        PLDM_PLATFORM, std::make_unique<platform::Handler>(
+                           PDR_JSONS_DIR, pdrRepo.get(), hostPDRHandler.get()));
+    invoker.registerHandler(
+        PLDM_FRU, std::make_unique<fru::Handler>(FRU_JSONS_DIR, pdrRepo.get(),
+                                                 entityTree.get()));
+
+#ifdef OEM_IBM
+    invoker.registerHandler(PLDM_OEM, std::make_unique<oem_ibm::Handler>());
+#endif
+
     pldm::utils::CustomFD socketFd(sockfd);
 
     struct sockaddr_un addr
@@ -203,8 +216,6 @@
         exit(EXIT_FAILURE);
     }
 
-    auto& bus = pldm::utils::DBusHandler::getBus();
-    dbus_api::Requester dbusImplReq(bus, "/xyz/openbmc_project/pldm");
     auto callback = [verbose, &invoker, &dbusImplReq](IO& /*io*/, int fd,
                                                       uint32_t revents) {
         if (!(revents & EPOLLIN))
@@ -292,7 +303,6 @@
         }
     };
 
-    auto event = Event::get_default();
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
     bus.request_name("xyz.openbmc_project.PLDM");
     IO io(event, socketFd(), EPOLLIN, std::move(callback));
diff --git a/test/libpldmresponder_pdr_state_effecter_test.cpp b/test/libpldmresponder_pdr_state_effecter_test.cpp
index 73bcfbb..e17fe89 100644
--- a/test/libpldmresponder_pdr_state_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_state_effecter_test.cpp
@@ -15,7 +15,7 @@
     auto inPDRRepo = pldm_pdr_init();
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
 
@@ -107,7 +107,8 @@
 {
     auto pdrRepo = pldm_pdr_init();
 
-    ASSERT_THROW(Handler("./pdr_jsons/not_there", pdrRepo), std::exception);
+    ASSERT_THROW(Handler("./pdr_jsons/not_there", pdrRepo, nullptr),
+                 std::exception);
 
     pldm_pdr_destroy(pdrRepo);
 }
@@ -117,7 +118,7 @@
     auto inPDRRepo = pldm_pdr_init();
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
 
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index 8dfe6f1..818d5b2 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -24,7 +24,7 @@
     request->request_count = 100;
 
     auto pdrRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -55,7 +55,7 @@
     request->request_count = 1;
 
     auto pdrRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -80,7 +80,7 @@
     request->request_count = 1;
 
     auto pdrRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -103,7 +103,7 @@
     request->record_handle = 1;
 
     auto pdrRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -128,7 +128,7 @@
     request->request_count = 100;
 
     auto pdrRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", pdrRepo, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -179,7 +179,7 @@
     auto inPDRRepo = pldm_pdr_init();
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
@@ -214,7 +214,7 @@
     auto inPDRRepo = pldm_pdr_init();
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo);
+    Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
diff --git a/test/meson.build b/test/meson.build
index 159cfc2..3adab01 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -14,7 +14,11 @@
 
 gtest = dependency('gtest', main: true, disabler: true, required: true)
 gmock = dependency('gmock', disabler: true, required: true)
-pldmd = declare_dependency(sources: '../instance_id.cpp')
+pldmd = declare_dependency(
+          sources: [
+            '../instance_id.cpp',
+            '../dbus_impl_requester.cpp',
+            '../host_pdr_handler.cpp'])
 
 tests = [
   'libpldmresponder_base_test',
diff --git a/utils.cpp b/utils.cpp
index 20d2b06..98e8d21 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -2,6 +2,7 @@
 
 #include <array>
 #include <ctime>
+#include <fstream>
 #include <iostream>
 #include <map>
 #include <stdexcept>
@@ -17,6 +18,34 @@
 constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
 constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper";
 constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";
+constexpr auto eidPath = "/usr/share/pldm/host_eid";
+
+uint8_t readHostEID()
+{
+    uint8_t eid{};
+    std::ifstream eidFile{eidPath};
+    if (!eidFile.good())
+    {
+        std::cerr << "Could not open host EID file"
+                  << "\n";
+    }
+    else
+    {
+        std::string eidStr;
+        eidFile >> eidStr;
+        if (!eidStr.empty())
+        {
+            eid = atoi(eidStr.c_str());
+        }
+        else
+        {
+            std::cerr << "Host EID file was empty"
+                      << "\n";
+        }
+    }
+
+    return eid;
+}
 
 uint8_t getNumPadBytes(uint32_t data)
 {
diff --git a/utils.hpp b/utils.hpp
index 567138a..830a97f 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -241,5 +241,11 @@
     return p.parent_path().string();
 }
 
+/** @brief Read (static) MCTP EID of host firmware from a file
+ *
+ *  @return uint8_t - MCTP EID
+ */
+uint8_t readHostEID();
+
 } // namespace utils
 } // namespace pldm