Handle FRU records from host

- Get FRU record table from host and traverse the entity structure
  from the FRU record set PDR, and create D-Bus object.

- When the FRU field type is a PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE
  , the location code is used to  populate the
  xyz.openbmc_project.Inventory.Decorator.LocationCode D-Bus
  interface for the corresponding FRU.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I482c4d371f76b4881109ef420dfd9543e1aa810b
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index 3ceae13..32993ee 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -1,5 +1,12 @@
 #include "host_pdr_handler.hpp"
 
+#include "libpldm/fru.h"
+#ifdef OEM_IBM
+#include "libpldm/fru_oem_ibm.h"
+#endif
+
+#include "custom_dbus.hpp"
+
 #include <assert.h>
 #include <libpldm/pldm.h>
 
@@ -21,6 +28,7 @@
 using namespace sdbusplus::bus::match::rules;
 using Json = nlohmann::json;
 namespace fs = std::filesystem;
+using namespace pldm::dbus;
 constexpr auto fruJson = "host_frus.json";
 const Json emptyJson{};
 const std::vector<Json> emptyJsonList{};
@@ -403,6 +411,7 @@
 {
     static bool merged = false;
     static PDRList stateSensorPDRs{};
+    static PDRList fruRecordSetPDRs{};
     uint32_t nextRecordHandle{};
     uint8_t tlEid = 0;
     bool tlValid = true;
@@ -521,6 +530,7 @@
                 {
                     pdrTerminusHandle =
                         extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
+                    fruRecordSetPDRs.emplace_back(pdr);
                 }
                 else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
                 {
@@ -558,11 +568,13 @@
 
         /*received last record*/
         this->parseStateSensorPDRs(stateSensorPDRs);
+        this->createDbusObjects(fruRecordSetPDRs);
         if (isHostUp())
         {
             this->setHostSensorState(stateSensorPDRs);
         }
         stateSensorPDRs.clear();
+        fruRecordSetPDRs.clear();
         entityAssociations.clear();
 
         if (merged)
@@ -817,4 +829,211 @@
         }
     }
 }
+
+void HostPDRHandler::getFRURecordTableMetadataByRemote(
+    const PDRList& fruRecordSetPDRs)
+{
+    auto instanceId = instanceIdDb.next(mctp_eid);
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
+
+    // GetFruRecordTableMetadata
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    auto rc = encode_get_fru_record_table_metadata_req(
+        instanceId, request, requestMsg.size() - sizeof(pldm_msg_hdr));
+    if (rc != PLDM_SUCCESS)
+    {
+        instanceIdDb.free(mctp_eid, instanceId);
+        lg2::error(
+            "Failed to encode_get_fru_record_table_metadata_req, rc = {RC}",
+            "RC", lg2::hex, rc);
+        return;
+    }
+
+    auto getFruRecordTableMetadataResponseHandler =
+        [this, fruRecordSetPDRs](mctp_eid_t /*eid*/, const pldm_msg* response,
+                                 size_t respMsgLen) {
+        if (response == nullptr || !respMsgLen)
+        {
+            lg2::error(
+                "Failed to receive response for the Get FRU Record Table Metadata");
+            return;
+        }
+
+        uint8_t cc = 0;
+        uint8_t fru_data_major_version, fru_data_minor_version;
+        uint32_t fru_table_maximum_size, fru_table_length;
+        uint16_t total_record_set_identifiers;
+        uint16_t total;
+        uint32_t checksum;
+
+        auto rc = decode_get_fru_record_table_metadata_resp(
+            response, respMsgLen, &cc, &fru_data_major_version,
+            &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
+            &total_record_set_identifiers, &total, &checksum);
+
+        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
+        {
+            lg2::error(
+                "Faile to decode get fru record table metadata resp, Message Error: {RC}, cc: {CC}",
+                "RC", lg2::hex, rc, "CC", cc);
+            return;
+        }
+
+        // pass total to getFRURecordTableByRemote
+        this->getFRURecordTableByRemote(fruRecordSetPDRs, total);
+    };
+
+    rc = handler->registerRequest(
+        mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA,
+        std::move(requestMsg),
+        std::move(getFruRecordTableMetadataResponseHandler));
+    if (rc != PLDM_SUCCESS)
+    {
+        lg2::error("Failed to send the the Set State Effecter States request");
+    }
+
+    return;
+}
+
+void HostPDRHandler::getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
+                                               uint16_t totalTableRecords)
+{
+    fruRecordData.clear();
+
+    if (!totalTableRecords)
+    {
+        lg2::error("Failed to get fru record table");
+        return;
+    }
+
+    auto instanceId = instanceIdDb.next(mctp_eid);
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                    PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
+
+    // send the getFruRecordTable command
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    auto rc = encode_get_fru_record_table_req(
+        instanceId, 0, PLDM_GET_FIRSTPART, request,
+        requestMsg.size() - sizeof(pldm_msg_hdr));
+    if (rc != PLDM_SUCCESS)
+    {
+        instanceIdDb.free(mctp_eid, instanceId);
+        lg2::error("Failed to encode_get_fru_record_table_req, rc = {RC}", "RC",
+                   lg2::hex, rc);
+        return;
+    }
+
+    auto getFruRecordTableResponseHandler =
+        [totalTableRecords, this, fruRecordSetPDRs](
+            mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
+        if (response == nullptr || !respMsgLen)
+        {
+            lg2::error(
+                "Failed to receive response for the Get FRU Record Table");
+            return;
+        }
+
+        uint8_t cc = 0;
+        uint32_t next_data_transfer_handle = 0;
+        uint8_t transfer_flag = 0;
+        size_t fru_record_table_length = 0;
+        std::vector<uint8_t> fru_record_table_data(respMsgLen -
+                                                   sizeof(pldm_msg_hdr));
+        auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
+        auto rc = decode_get_fru_record_table_resp(
+            responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &cc,
+            &next_data_transfer_handle, &transfer_flag,
+            fru_record_table_data.data(), &fru_record_table_length);
+
+        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
+        {
+            lg2::error(
+                "Failed to decode get fru record table resp, Message Error: {RC}, cc: {CC}",
+                "RC", lg2::hex, rc, "CC", cc);
+            return;
+        }
+
+        fruRecordData = responder::pdr_utils::parseFruRecordTable(
+            fru_record_table_data.data(), fru_record_table_length);
+
+        if (totalTableRecords != fruRecordData.size())
+        {
+            fruRecordData.clear();
+
+            lg2::error("failed to parse fru recrod data format.");
+            return;
+        }
+
+        this->setFRUDataOnDBus(fruRecordSetPDRs, fruRecordData);
+    };
+
+    rc = handler->registerRequest(
+        mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE,
+        std::move(requestMsg), std::move(getFruRecordTableResponseHandler));
+    if (rc != PLDM_SUCCESS)
+    {
+        lg2::error("Failed to send the the Set State Effecter States request");
+    }
+}
+
+std::optional<uint16_t> HostPDRHandler::getRSI(const PDRList& fruRecordSetPDRs,
+                                               const pldm_entity& entity)
+{
+    for (const auto& pdr : fruRecordSetPDRs)
+    {
+        auto fruPdr = reinterpret_cast<const pldm_pdr_fru_record_set*>(
+            const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
+
+        if (fruPdr->entity_type == entity.entity_type &&
+            fruPdr->entity_instance_num == entity.entity_instance_num)
+        {
+            return fruPdr->fru_rsi;
+        }
+    }
+
+    return std::nullopt;
+}
+
+void HostPDRHandler::setFRUDataOnDBus(
+    const PDRList& fruRecordSetPDRs,
+    const std::vector<responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
+{
+    for (const auto& entity : objPathMap)
+    {
+        pldm_entity node = pldm_entity_extract(entity.second);
+        auto fruRSI = getRSI(fruRecordSetPDRs, node);
+
+        for (const auto& data : fruRecordData)
+        {
+            if (!fruRSI || *fruRSI != data.fruRSI)
+            {
+                continue;
+            }
+
+            if (data.fruRecType == PLDM_FRU_RECORD_TYPE_OEM)
+            {
+                for (const auto& tlv : data.fruTLV)
+                {
+                    if (tlv.fruFieldType ==
+                        PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE)
+                    {
+                        CustomDBus::getCustomDBus().setLocationCode(
+                            entity.first,
+                            std::string(reinterpret_cast<const char*>(
+                                            tlv.fruFieldValue.data()),
+                                        tlv.fruFieldLen));
+                    }
+                }
+            }
+        }
+    }
+}
+void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
+{
+    // TODO: Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
+
+    getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
+}
+
 } // namespace pldm