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/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