oem_ibm: Persist the remote PDRs before merging

This commit persists the remote range PDRs in the repo
before they are merged. This change was needed as the
remote terminus would not send down to BMC the entity
Association PDRs of the remote terminus after the BMC
takes a reset after the PDR exchange as the PDRs will be
in BMC range of record handles.

TESTED: Tested by triggering a PDR exchange with the
remote terminus by doing poweron test and also reset the
BMC after the PDR exchange.

Change-Id: Iec2109073f984e5980d9b6e445ce0fa4198d4a0f
Signed-off-by: Sagar Srinivas <sagar.srinivas@ibm.com>
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index 99bc13d..7b5e057 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -3,8 +3,9 @@
 #include "libpldm/fru.h"
 #ifdef OEM_IBM
 #include "libpldm/fru_oem_ibm.h"
-#endif
 
+#include "oem/ibm/libpldmresponder/utils.hpp"
+#endif
 #include "custom_dbus.hpp"
 
 #include <assert.h>
@@ -26,6 +27,7 @@
 using namespace pldm::responder::events;
 using namespace pldm::utils;
 using namespace sdbusplus::bus::match::rules;
+using namespace pldm::responder::pdr_utils;
 using Json = nlohmann::json;
 namespace fs = std::filesystem;
 using namespace pldm::dbus;
@@ -89,11 +91,13 @@
     const std::string& eventsJsonsDir, pldm_entity_association_tree* entityTree,
     pldm_entity_association_tree* bmcEntityTree,
     pldm::InstanceIdDb& instanceIdDb,
-    pldm::requester::Handler<pldm::requester::Request>* handler) :
+    pldm::requester::Handler<pldm::requester::Request>* handler,
+    pldm::responder::oem_platform::Handler* oemPlatformHandler) :
     mctp_fd(mctp_fd),
     mctp_eid(mctp_eid), event(event), repo(repo),
     stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
-    bmcEntityTree(bmcEntityTree), instanceIdDb(instanceIdDb), handler(handler)
+    bmcEntityTree(bmcEntityTree), instanceIdDb(instanceIdDb), handler(handler),
+    oemPlatformHandler(oemPlatformHandler)
 {
     mergedHostParents = false;
     fs::path hostFruJson(fs::path(HOST_JSONS_DIR) / fruJson);
@@ -246,7 +250,9 @@
     return PLDM_SUCCESS;
 }
 
-void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr)
+void HostPDRHandler::mergeEntityAssociations(
+    const std::vector<uint8_t>& pdr, [[maybe_unused]] const uint32_t& size,
+    [[maybe_unused]] const uint32_t& record_handle)
 {
     size_t numEntities{};
     pldm_entity* entities = nullptr;
@@ -254,6 +260,14 @@
     auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
 
+    if (oemPlatformHandler &&
+        oemPlatformHandler->checkRecordHandleInRange(record_handle))
+    {
+        // Adding the remote range PDRs to the repo before merging it
+        uint32_t handle = record_handle;
+        pldm_pdr_add_check(repo, pdr.data(), size, true, 0xFFFF, &handle);
+    }
+
     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
                                         &entities);
     if (numEntities > 0)
@@ -278,9 +292,21 @@
         entityAssoc.push_back(pNode);
         for (size_t i = 1; i < numEntities; ++i)
         {
+            bool isUpdateContainerId = true;
+            if (oemPlatformHandler)
+            {
+                isUpdateContainerId =
+                    pldm::responder::utils::checkIfLogicalBitSet(
+                        entities[i].entity_container_id);
+            }
             auto node = pldm_entity_association_tree_add_entity(
                 entityTree, &entities[i], entities[i].entity_instance_num,
-                pNode, entityPdr->association_type, true, true, 0xFFFF);
+                pNode, entityPdr->association_type, true, isUpdateContainerId,
+                0xFFFF);
+            if (!node)
+            {
+                continue;
+            }
             merged = true;
             entityAssoc.push_back(node);
         }
@@ -303,8 +329,25 @@
         }
         else
         {
-            int rc = pldm_entity_association_pdr_add_from_node_check(
-                node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
+            int rc = 0;
+            if (oemPlatformHandler)
+            {
+                auto record = oemPlatformHandler->fetchLastBMCRecord(repo);
+
+                uint32_t record_handle = pldm_pdr_get_record_handle(repo,
+                                                                    record);
+
+                rc =
+                    pldm_entity_association_pdr_add_from_node_with_record_handle(
+                        node, repo, &entities, numEntities, true,
+                        TERMINUS_HANDLE, (record_handle + 1));
+            }
+            else
+            {
+                rc = pldm_entity_association_pdr_add_from_node_check(
+                    node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
+            }
+
             if (rc)
             {
                 error(
@@ -509,7 +552,7 @@
 
             if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
             {
-                this->mergeEntityAssociations(pdr);
+                this->mergeEntityAssociations(pdr, respCount, rh);
                 merged = true;
             }
             else
@@ -585,6 +628,14 @@
                 {
                     pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
                                            tlValid);
+
+                    if (!isHostUp())
+                    {
+                        // The terminus PDR becomes invalid when the terminus
+                        // itself is down. We don't need to do PDR exchange in
+                        // that case, so setting the next record handle to 0.
+                        nextRecordHandle = 0;
+                    }
                 }
                 else
                 {
diff --git a/host-bmc/host_pdr_handler.hpp b/host-bmc/host_pdr_handler.hpp
index a76cc20..8a4e21a 100644
--- a/host-bmc/host_pdr_handler.hpp
+++ b/host-bmc/host_pdr_handler.hpp
@@ -4,6 +4,7 @@
 #include "common/types.hpp"
 #include "common/utils.hpp"
 #include "libpldmresponder/event_parser.hpp"
+#include "libpldmresponder/oem_handler.hpp"
 #include "libpldmresponder/pdr_utils.hpp"
 #include "requester/handler.hpp"
 
@@ -93,7 +94,8 @@
         pldm_entity_association_tree* entityTree,
         pldm_entity_association_tree* bmcEntityTree,
         pldm::InstanceIdDb& instanceIdDb,
-        pldm::requester::Handler<pldm::requester::Request>* handler);
+        pldm::requester::Handler<pldm::requester::Request>* handler,
+        pldm::responder::oem_platform::Handler* oemPlatformHandler);
 
     /** @brief fetch PDRs from host firmware. See @class.
      *  @param[in] recordHandles - list of record handles pointing to host's
@@ -182,8 +184,13 @@
      *  @details A merge operation involves adding a pldm_entity under the
      *  appropriate parent, and updating container ids.
      *  @param[in] pdr - entity association pdr
+     *  @param[in] size - size of input PDR record in bytes
+     *  @param[in] record_handle - record handle of the PDR
      */
-    void mergeEntityAssociations(const std::vector<uint8_t>& pdr);
+    void
+        mergeEntityAssociations(const std::vector<uint8_t>& pdr,
+                                [[maybe_unused]] const uint32_t& size,
+                                [[maybe_unused]] const uint32_t& record_handle);
 
     /** @brief process the Host's PDR and add to BMC's PDR repo
      *  @param[in] eid - MCTP id of Host
@@ -324,6 +331,9 @@
      */
     std::vector<responder::pdr_utils::FruRecordDataFormat> fruRecordData;
 
+    /** @OEM platform handler */
+    pldm::responder::oem_platform::Handler* oemPlatformHandler;
+
     /** @brief Object path and entity association and is only loaded once
      */
     bool objPathEntityAssociation;
diff --git a/libpldmresponder/oem_handler.hpp b/libpldmresponder/oem_handler.hpp
index accfc80..c21cf09 100644
--- a/libpldmresponder/oem_handler.hpp
+++ b/libpldmresponder/oem_handler.hpp
@@ -90,6 +90,23 @@
     /** @brief Interface to check the BMC state */
     virtual int checkBMCState() = 0;
 
+    /** @brief Interface to fetch the last BMC record from the PDR repository
+     *
+     *  @param[in] repo - pointer to BMC's primary PDR repo
+     *
+     *  @return the last BMC record from the repo
+     */
+    virtual const pldm_pdr_record* fetchLastBMCRecord(const pldm_pdr* repo) = 0;
+
+    /** @brief Interface to check if the record handle passed is in remote PDR
+     *         record handle range
+     *
+     *  @param[in] record_handle - record handle of the PDR
+     *
+     *  @return true if record handle passed is in host PDR record handle range
+     */
+    virtual bool checkRecordHandleInRange(const uint32_t& record_handle) = 0;
+
     virtual ~Handler() = default;
 
   protected:
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.cpp b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
index 3c877d8..98903a4 100644
--- a/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
@@ -597,6 +597,21 @@
     return PLDM_SUCCESS;
 }
 
+const pldm_pdr_record*
+    pldm::responder::oem_ibm_platform::Handler::fetchLastBMCRecord(
+        const pldm_pdr* repo)
+{
+    return pldm_pdr_find_last_in_range(repo, BMC_PDR_START_RANGE,
+                                       BMC_PDR_END_RANGE);
+}
+
+bool pldm::responder::oem_ibm_platform::Handler::checkRecordHandleInRange(
+    const uint32_t& record_handle)
+{
+    return record_handle >= HOST_PDR_START_RANGE &&
+           record_handle <= HOST_PDR_END_RANGE;
+}
+
 } // namespace oem_ibm_platform
 } // namespace responder
 } // namespace pldm
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.hpp b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
index 964534e..03369a4 100644
--- a/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
@@ -21,6 +21,11 @@
 constexpr uint16_t ENTITY_INSTANCE_0 = 0;
 constexpr uint16_t ENTITY_INSTANCE_1 = 1;
 
+constexpr uint32_t BMC_PDR_START_RANGE = 0x00000000;
+constexpr uint32_t BMC_PDR_END_RANGE = 0x00FFFFFF;
+constexpr uint32_t HOST_PDR_START_RANGE = 0x01000000;
+constexpr uint32_t HOST_PDR_END_RANGE = 0x01FFFFFF;
+
 enum SetEventReceiverCount
 {
     SET_EVENT_RECEIVER_SENT = 0x2,
@@ -181,6 +186,23 @@
     /** @brief to check the BMC state*/
     int checkBMCState();
 
+    /** @brief Method to fetch the last BMC record from the PDR repo
+     *
+     * @param[in] repo - pointer to BMC's primary PDR repo
+     *
+     * @return the last BMC record from the repo
+     */
+    const pldm_pdr_record* fetchLastBMCRecord(const pldm_pdr* repo);
+
+    /** @brief Method to check if the record handle passed is in remote PDR
+     *         record handle range
+     *
+     *  @param[in] record_handle - record handle of the PDR
+     *
+     *  @return true if record handle passed is in host PDR record handle range
+     */
+    bool checkRecordHandleInRange(const uint32_t& record_handle);
+
     ~Handler() = default;
 
     pldm::responder::CodeUpdate* codeUpdate; //!< pointer to CodeUpdate object
diff --git a/oem/ibm/libpldmresponder/utils.cpp b/oem/ibm/libpldmresponder/utils.cpp
index c41211e..231a476 100644
--- a/oem/ibm/libpldmresponder/utils.cpp
+++ b/oem/ibm/libpldmresponder/utils.cpp
@@ -139,6 +139,11 @@
     return 0;
 }
 
+bool checkIfLogicalBitSet(const uint16_t& containerId)
+{
+    return !(containerId & 0x8000);
+}
+
 } // namespace utils
 } // namespace responder
 } // namespace pldm
diff --git a/oem/ibm/libpldmresponder/utils.hpp b/oem/ibm/libpldmresponder/utils.hpp
index 4a1055e..0ad6e74 100644
--- a/oem/ibm/libpldmresponder/utils.hpp
+++ b/oem/ibm/libpldmresponder/utils.hpp
@@ -35,6 +35,14 @@
  */
 int writeToUnixSocket(const int sock, const char* buf,
                       const uint64_t blockSize);
+
+/** @brief Method to check if the logical bit is set
+ *
+ *  @param[containerId] - container id of the entity
+ *
+ *  @return true or false based on the logic bit set
+ * */
+bool checkIfLogicalBitSet(const uint16_t& containerId);
 } // namespace utils
 } // namespace responder
 } // namespace pldm
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index 23499e4..a85157f 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -225,23 +225,6 @@
         hostEffecterParser;
     std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
     DBusHandler dbusHandler;
-    if (hostEID)
-    {
-        hostPDRHandler = std::make_shared<HostPDRHandler>(
-            pldmTransport.getEventSource(), hostEID, event, pdrRepo.get(),
-            EVENTS_JSONS_DIR, entityTree.get(), bmcEntityTree.get(),
-            instanceIdDb, &reqHandler);
-        // HostFirmware interface needs access to hostPDR to know if host
-        // is running
-        dbusImplHost.setHostPdrObj(hostPDRHandler);
-
-        hostEffecterParser =
-            std::make_unique<pldm::host_effecters::HostEffecterParser>(
-                &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(),
-                &dbusHandler, HOST_JSONS_DIR, &reqHandler);
-        dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>(
-            pldmTransport.getEventSource(), hostEID, instanceIdDb, &reqHandler);
-    }
     std::unique_ptr<oem_platform::Handler> oemPlatformHandler{};
     std::unique_ptr<oem_bios::Handler> oemBiosHandler{};
 
@@ -259,7 +242,23 @@
                                           hostEID, &instanceIdDb, &reqHandler));
     oemBiosHandler = std::make_unique<oem::ibm::bios::Handler>(&dbusHandler);
 #endif
+    if (hostEID)
+    {
+        hostPDRHandler = std::make_shared<HostPDRHandler>(
+            pldmTransport.getEventSource(), hostEID, event, pdrRepo.get(),
+            EVENTS_JSONS_DIR, entityTree.get(), bmcEntityTree.get(),
+            instanceIdDb, &reqHandler, oemPlatformHandler.get());
+        // HostFirmware interface needs access to hostPDR to know if host
+        // is running
+        dbusImplHost.setHostPdrObj(hostPDRHandler);
 
+        hostEffecterParser =
+            std::make_unique<pldm::host_effecters::HostEffecterParser>(
+                &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(),
+                &dbusHandler, HOST_JSONS_DIR, &reqHandler);
+        dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>(
+            pldmTransport.getEventSource(), hostEID, instanceIdDb, &reqHandler);
+    }
     auto biosHandler = std::make_unique<bios::Handler>(
         pldmTransport.getEventSource(), hostEID, &instanceIdDb, &reqHandler,
         oemBiosHandler.get());