oem-ibm: PCIe Topology support

This is the infrastructure commit to support PCIe Topology

PCIe topology refers to the physical arrangement of PCIe devices within
the system. Topology data contains links connecting PCIe device to the
PCIe Controller.  This commit adds support for PCIe Topology and Cable
information filetype. Topology information is provided by host using
FILIO mechanism.
This commit adds support for handling
1. Topology file(PLDM_FILE_TYPE_PCIE_TOPOLOGY)
2. Cable information file(PLDM_FILE_TYPE_CABLE_INFO)

Tested: Updated Unit test and tested power on.

Change-Id: Ic77f4f9921dd855c61c3929b5a458d80b1691b48
Signed-off-by: Archana Kakani <archana.kakani@ibm.com>
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 50bafe3..45594b0 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -52,6 +52,7 @@
     '../oem/ibm/requester/dbus_to_file_handler.cpp',
     '../oem/ibm/libpldmresponder/file_io_type_progress_src.cpp',
     '../oem/ibm/libpldmresponder/file_io_type_vpd.cpp',
+    '../oem/ibm/libpldmresponder/file_io_type_pcie.cpp',
   ]
 endif
 
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index 9826fdb..42d963e 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -4,6 +4,7 @@
 #include "file_io_type_cert.hpp"
 #include "file_io_type_dump.hpp"
 #include "file_io_type_lid.hpp"
+#include "file_io_type_pcie.hpp"
 #include "file_io_type_pel.hpp"
 #include "file_io_type_progress_src.hpp"
 #include "file_io_type_vpd.hpp"
@@ -172,6 +173,11 @@
         {
             return std::make_unique<keywordHandler>(fileHandle, fileType);
         }
+        case PLDM_FILE_TYPE_PCIE_TOPOLOGY:
+        case PLDM_FILE_TYPE_CABLE_INFO:
+        {
+            return std::make_unique<PCIeInfoHandler>(fileHandle, fileType);
+        }
         default:
         {
             throw InternalFailure();
diff --git a/oem/ibm/libpldmresponder/file_io_type_pcie.cpp b/oem/ibm/libpldmresponder/file_io_type_pcie.cpp
new file mode 100644
index 0000000..92ceff9
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_pcie.cpp
@@ -0,0 +1,116 @@
+#include "file_io_type_pcie.hpp"
+
+#include "libpldm/base.h"
+#include "libpldm/file_io.h"
+
+#include <stdint.h>
+
+#include <phosphor-logging/lg2.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace responder
+{
+
+static constexpr auto pciePath = "/var/lib/pldm/pcie-topology/";
+constexpr auto topologyFile = "topology";
+constexpr auto cableInfoFile = "cableinfo";
+
+namespace fs = std::filesystem;
+std::unordered_map<uint16_t, bool> PCIeInfoHandler::receivedFiles;
+
+PCIeInfoHandler::PCIeInfoHandler(uint32_t fileHandle, uint16_t fileType) :
+    FileHandler(fileHandle), infoType(fileType)
+{
+    receivedFiles.emplace(infoType, false);
+}
+
+int PCIeInfoHandler::writeFromMemory(
+    uint32_t offset, uint32_t length, uint64_t address,
+    oem_platform::Handler* /*oemPlatformHandler*/)
+{
+    if (!fs::exists(pciePath))
+    {
+        fs::create_directories(pciePath);
+        fs::permissions(pciePath,
+                        fs::perms::others_read | fs::perms::owner_write);
+    }
+
+    fs::path infoFile(fs::path(pciePath) / topologyFile);
+    if (infoType == PLDM_FILE_TYPE_CABLE_INFO)
+    {
+        infoFile = (fs::path(pciePath) / cableInfoFile);
+    }
+
+    try
+    {
+        std::ofstream pcieData(infoFile, std::ios::out | std::ios::binary);
+        auto rc = transferFileData(infoFile, false, offset, length, address);
+        if (rc != PLDM_SUCCESS)
+        {
+            error("TransferFileData failed in PCIeTopology with error {ERROR}",
+                  "ERROR", rc);
+            return rc;
+        }
+        return PLDM_SUCCESS;
+    }
+    catch (const std::exception& e)
+    {
+        error("Create/Write data to the File type {TYPE}, failed {ERROR}",
+              "TYPE", (int)infoType, "ERROR", e);
+        return PLDM_ERROR;
+    }
+}
+
+int PCIeInfoHandler::write(const char* buffer, uint32_t, uint32_t& length,
+                           oem_platform::Handler* /*oemPlatformHandler*/)
+{
+    fs::path infoFile(fs::path(pciePath) / topologyFile);
+    if (infoType == PLDM_FILE_TYPE_CABLE_INFO)
+    {
+        infoFile = (fs::path(pciePath) / cableInfoFile);
+    }
+
+    try
+    {
+        std::ofstream pcieData(infoFile, std::ios::out | std::ios::binary |
+                                             std::ios::app);
+
+        if (!buffer)
+        {
+            pcieData.write(buffer, length);
+        }
+        pcieData.close();
+    }
+    catch (const std::exception& e)
+    {
+        error("Create/Write data to the File type {TYPE}, failed {ERROR}",
+              "TYPE", (int)infoType, "ERROR", e);
+        return PLDM_ERROR;
+    }
+
+    return PLDM_SUCCESS;
+}
+
+int PCIeInfoHandler::fileAck(uint8_t /*fileStatus*/)
+{
+    receivedFiles[infoType] = true;
+    try
+    {
+        if (receivedFiles.at(PLDM_FILE_TYPE_CABLE_INFO) &&
+            receivedFiles.at(PLDM_FILE_TYPE_PCIE_TOPOLOGY))
+        {
+            receivedFiles.clear();
+        }
+    }
+    catch (const std::out_of_range& e)
+    {
+        info("Received only one of the topology file");
+    }
+    return PLDM_SUCCESS;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_type_pcie.hpp b/oem/ibm/libpldmresponder/file_io_type_pcie.hpp
new file mode 100644
index 0000000..947d397
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_pcie.hpp
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "file_io_by_type.hpp"
+
+#include <unordered_map>
+
+namespace pldm
+{
+namespace responder
+{
+
+/** @class PCIeInfoHandler
+ *
+ *  @brief Inherits and implements FileHandler. This class is used to handle the
+ *  pcie topology file and cable information from remote PLDM terminus to the
+ *  bmc
+ */
+class PCIeInfoHandler : public FileHandler
+{
+  public:
+    /** @brief PCIeInfoHandler constructor
+     */
+    PCIeInfoHandler(uint32_t fileHandle, uint16_t fileType);
+
+    virtual int writeFromMemory(uint32_t offset, uint32_t length,
+                                uint64_t address,
+                                oem_platform::Handler* /*oemPlatformHandler*/);
+
+    virtual int write(const char* buffer, uint32_t offset, uint32_t& length,
+                      oem_platform::Handler* /*oemPlatformHandler*/);
+
+    virtual int fileAck(uint8_t fileStatus);
+
+    virtual int readIntoMemory(uint32_t /*offset*/, uint32_t& /*length*/,
+                               uint64_t /*address*/,
+                               oem_platform::Handler* /*oemPlatformHandler*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    virtual int read(uint32_t /*offset*/, uint32_t& /*length*/,
+                     Response& /*response*/,
+                     oem_platform::Handler* /*oemPlatformHandler*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    virtual int newFileAvailable(uint64_t /*length*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    virtual int fileAckWithMetaData(uint8_t /*fileStatus*/,
+                                    uint32_t /*metaDataValue1*/,
+                                    uint32_t /*metaDataValue2*/,
+                                    uint32_t /*metaDataValue3*/,
+                                    uint32_t /*metaDataValue4*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    virtual int newFileAvailableWithMetaData(uint64_t /*length*/,
+                                             uint32_t /*metaDataValue1*/,
+                                             uint32_t /*metaDataValue2*/,
+                                             uint32_t /*metaDataValue3*/,
+                                             uint32_t /*metaDataValue4*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    /** @brief PCIeInfoHandler destructor
+     */
+    ~PCIeInfoHandler() {}
+
+  private:
+    uint16_t infoType; //!< type of the information
+
+    /** @brief A static unordered map storing information about received files.
+     *
+     *  This unordered map associates file type with a boolean value indicating
+     *  whether the file of that type has been received or not.
+     */
+    static std::unordered_map<uint16_t, bool> receivedFiles;
+};
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index d9d68e7..2ca1016 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -4,6 +4,7 @@
 #include "libpldmresponder/file_io_type_cert.hpp"
 #include "libpldmresponder/file_io_type_dump.hpp"
 #include "libpldmresponder/file_io_type_lid.hpp"
+#include "libpldmresponder/file_io_type_pcie.hpp"
 #include "libpldmresponder/file_io_type_pel.hpp"
 #include "libpldmresponder/file_table.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
@@ -883,6 +884,14 @@
     certType = dynamic_cast<CertHandler*>(handler.get());
     ASSERT_TRUE(certType != nullptr);
 
+    handler = getHandlerByType(PLDM_FILE_TYPE_PCIE_TOPOLOGY, fileHandle);
+    auto pcieTopologyType = dynamic_cast<PCIeInfoHandler*>(handler.get());
+    ASSERT_TRUE(pcieTopologyType != nullptr);
+
+    handler = getHandlerByType(PLDM_FILE_TYPE_CABLE_INFO, fileHandle);
+    auto cableInfoType = dynamic_cast<PCIeInfoHandler*>(handler.get());
+    ASSERT_TRUE(cableInfoType != nullptr);
+
     handler = getHandlerByType(PLDM_FILE_TYPE_ROOT_CERT, fileHandle);
     certType = dynamic_cast<CertHandler*>(handler.get());
     ASSERT_TRUE(certType != nullptr);