oem:ibm :Implement SetFruRecordTable

The commit implements the setFruRecordTable command
and also updates the DBus property for the IBM cable cards
for which the host sends a setFruRecordTable command.

Tested: using pldmtool

Signed-off-by: Pavithra Barithaya <pavithra.b@ibm.com>
Change-Id: I70e4f85f627577d8ca1bc90447b10e9e2e8e7ccd
diff --git a/oem/ibm/libpldmresponder/fru_oem_ibm.cpp b/oem/ibm/libpldmresponder/fru_oem_ibm.cpp
new file mode 100644
index 0000000..805d0ec
--- /dev/null
+++ b/oem/ibm/libpldmresponder/fru_oem_ibm.cpp
@@ -0,0 +1,152 @@
+#include "fru_oem_ibm.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <ranges>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace responder
+{
+namespace oem_ibm_fru
+{
+
+void pldm::responder::oem_ibm_fru::Handler::setIBMFruHandler(
+    pldm::responder::fru::Handler* handler)
+{
+    fruHandler = handler;
+}
+
+int pldm::responder::oem_ibm_fru::Handler::processOEMFRUTable(
+    const std::vector<uint8_t>& fruData)
+{
+    uint8_t dataSize = 0;
+    const uint8_t* data = fruData.data();
+
+    while (dataSize < fruData.size())
+    {
+        auto record =
+            reinterpret_cast<const pldm_fru_record_data_format*>(data);
+        if (!record)
+        {
+            return PLDM_ERROR_INVALID_DATA;
+        }
+
+        auto& entityAssociationMap = getAssociateEntityMap();
+        uint16_t fruRSI = le16toh(record->record_set_id);
+
+        dataSize += sizeof(pldm_fru_record_data_format) -
+                    sizeof(pldm_fru_record_tlv);
+        data += dataSize;
+
+        for ([[maybe_unused]] const auto& i :
+             std::views::iota(0, (int)record->num_fru_fields))
+        {
+            auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(data);
+            if (!tlv)
+            {
+                return PLDM_ERROR_INVALID_DATA;
+            }
+
+            if (tlv->type == PLDM_OEM_FRU_FIELD_TYPE_PCIE_CONFIG_SPACE_DATA)
+            {
+                auto pcieData =
+                    reinterpret_cast<const PcieConfigSpaceData*>(tlv->value);
+
+                if (!pcieData)
+                {
+                    return PLDM_ERROR_INVALID_DATA;
+                }
+                auto vendorId = std::to_string(htole16(pcieData->vendorId));
+                auto deviceId = std::to_string(htole16(pcieData->deviceId));
+                auto revisionId = std::to_string(pcieData->revisionId);
+
+                std::stringstream ss;
+
+                for (const auto& ele : pcieData->classCode)
+                {
+                    ss << std::setfill('0') << std::setw(2) << std::hex << ele;
+                }
+                std::string classCode = ss.str();
+
+                auto subSystemVendorId =
+                    std::to_string(htole16(pcieData->subSystemVendorId));
+                auto subSystemId =
+                    std::to_string(htole16(pcieData->subSystemId));
+
+                updateDBusProperty(fruRSI, entityAssociationMap, vendorId,
+                                   deviceId, revisionId, classCode,
+                                   subSystemVendorId, subSystemId);
+            }
+            // length of tlv is removed from the structure pldm_fru_record_tlv
+            // and the new tlv length is added back.
+            dataSize += sizeof(pldm_fru_record_tlv) - sizeof(uint8_t) +
+                        tlv->length;
+            data += dataSize;
+        }
+    }
+
+    return PLDM_SUCCESS;
+}
+
+void Handler::updateDBusProperty(
+    uint16_t fruRSI, const AssociatedEntityMap& fruAssociationMap,
+    const std::string& vendorId, const std::string& deviceId,
+    const std::string& revisionId, const std::string& classCode,
+    const std::string& subSystemVendorId, const std::string& subSystemId)
+{
+    uint16_t entityType{};
+    uint16_t entityInstanceNum{};
+    uint16_t containerId{};
+    uint16_t terminusHandle{};
+    const pldm_pdr_record* record{};
+
+    record = pldm_pdr_fru_record_set_find_by_rsi(
+        pdrRepo, fruRSI, &terminusHandle, &entityType, &entityInstanceNum,
+        &containerId);
+
+    if (record)
+    {
+        for (const auto& [key, value] : fruAssociationMap)
+        {
+            if (entityInstanceNum == value.entity_instance_num &&
+                entityType == value.entity_type &&
+                containerId == value.entity_container_id)
+            {
+                dbus_map_update(key, "Function0VendorId", vendorId);
+                dbus_map_update(key, "Function0DeviceId", deviceId);
+                dbus_map_update(key, "Function0RevisionId", revisionId);
+                dbus_map_update(key, "Function0ClassCode", classCode);
+                dbus_map_update(key, "Function0SubsystemVendorId",
+                                subSystemVendorId);
+                dbus_map_update(key, "Function0SubsystemId", subSystemId);
+            }
+        }
+    }
+}
+
+void Handler::dbus_map_update(const std::string& adapterObjPath,
+                              const std::string& propertyName,
+                              const std::string& propValue)
+{
+    pldm::utils::PropertyValue value = propValue;
+    pldm::utils::DBusMapping dbusMapping;
+    dbusMapping.objectPath = adapterObjPath;
+    dbusMapping.interface = "xyz.openbmc_project.Inventory.Item.PCIeDevice";
+    dbusMapping.propertyName = propertyName;
+    dbusMapping.propertyType = "string";
+    try
+    {
+        pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
+    }
+    catch (const std::exception& e)
+    {
+        error("Failed to set '{PROPERTY}' property: {ERROR}", "PROPERTY",
+              propertyName, "ERROR", e);
+    }
+}
+} // namespace oem_ibm_fru
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/fru_oem_ibm.hpp b/oem/ibm/libpldmresponder/fru_oem_ibm.hpp
new file mode 100644
index 0000000..1fa7bda
--- /dev/null
+++ b/oem/ibm/libpldmresponder/fru_oem_ibm.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "common/utils.hpp"
+#include "libpldmresponder/fru.hpp"
+#include "libpldmresponder/oem_handler.hpp"
+
+#include <libpldm/oem/ibm/fru.h>
+
+namespace pldm
+{
+namespace responder
+{
+using ObjectPath = std::string;
+using AssociatedEntityMap = std::map<ObjectPath, pldm_entity>;
+
+namespace oem_ibm_fru
+{
+
+// structure of the PCIE config space data
+struct PcieConfigSpaceData
+{
+    uint16_t vendorId;
+    uint16_t deviceId;
+    uint32_t first_reserved;
+    uint8_t revisionId;
+    std::array<uint8_t, 3> classCode;
+    uint32_t second_reserved[8];
+    uint16_t subSystemVendorId;
+    uint16_t subSystemId;
+    uint32_t last_reserved[4];
+
+} __attribute__((packed));
+
+class Handler : public oem_fru::Handler
+{
+  public:
+    Handler(pldm_pdr* repo) : pdrRepo(repo) {}
+
+    /** @brief Method to set the fru handler in the
+     *    oem_ibm_handler class
+     *
+     *  @param[in] handler - pointer to PLDM platform handler
+     */
+    void setIBMFruHandler(pldm::responder::fru::Handler* handler);
+
+    /** @brief Process OEM FRU table
+     *
+     *  @param[in] fruData - the data of the fru records
+     *
+     *  @return success or failure
+     */
+    int processOEMFRUTable(const std::vector<uint8_t>& fruData);
+
+    virtual const AssociatedEntityMap& getAssociateEntityMap()
+    {
+        return fruHandler->getAssociateEntityMap();
+    }
+
+    ~Handler() = default;
+
+  private:
+    /** @brief pointer to BMC's primary PDR repo */
+    const pldm_pdr* pdrRepo;
+
+    pldm::responder::fru::Handler* fruHandler; //!< pointer to PLDM fru handler
+
+    /** @brief update the DBus property
+     *
+     *  @param[in] fruRSI - fru record set identifier
+     *  @param[in] fruAssociationMap - the dbus path to pldm entity stored while
+     *                                 creating the pldm fru records
+     *  @param[in] vendorId - the vendor ID
+     *  @param[in] deviceId - the device ID
+     *  @param[in] revisionId - the revision ID
+     *  @param[in] classCode - the class Code
+     *  @param[in] subSystemVendorId - the subSystemVendor ID
+     *  @param[in] subSystemId - the subSystem ID
+     */
+    void updateDBusProperty(
+        uint16_t fruRSI, const AssociatedEntityMap& fruAssociationMap,
+        const std::string& vendorId, const std::string& deviceId,
+        const std::string& revisionId, const std::string& classCode,
+        const std::string& subSystemVendorId, const std::string& subSystemId);
+
+    /** @brief DBus Map update
+     *
+     *  @param[in] adapterObjectPath - the fru object path
+     *  @param[in] propertyName - the fru property name
+     *  @param[in] propValue - the fru property value
+     */
+    void dbus_map_update(const std::string& adapterObjectPath,
+                         const std::string& propertyName,
+                         const std::string& propValue);
+};
+
+} // namespace oem_ibm_fru
+} // namespace responder
+} // namespace pldm