ibm-oem: add handler to read PELs

Extend ReadFileByType* commands to let the host firmware read PELs off
of the BMC.

Signed-off-by: vkaverap <vkaverap@in.ibm.com>
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I3721e1ffe61a66d0f011535504a6915e0c16764b
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index 6f65b97..d191d3d 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -49,8 +49,8 @@
 
 constexpr auto xdmaDev = "/dev/aspeed-xdma";
 
-int DMA::transferDataHost(const fs::path& path, uint32_t offset,
-                          uint32_t length, uint64_t address, bool upstream)
+int DMA::transferDataHost(int fd, uint32_t offset, uint32_t length,
+                          uint64_t address, bool upstream)
 {
     static const size_t pageSize = getpagesize();
     uint32_t numPages = length / pageSize;
@@ -65,17 +65,17 @@
         munmap(vgaMem, pageAlignedLength);
     };
 
-    int fd = -1;
+    int dmaFd = -1;
     int rc = 0;
-    fd = open(xdmaDev, O_RDWR);
-    if (fd < 0)
+    dmaFd = open(xdmaDev, O_RDWR);
+    if (dmaFd < 0)
     {
         rc = -errno;
         std::cerr << "Failed to open the XDMA device, RC=" << rc << "\n";
         return rc;
     }
 
-    utils::CustomFD xdmaFd(fd);
+    utils::CustomFD xdmaFd(dmaFd);
 
     void* vgaMem;
     vgaMem = mmap(nullptr, pageAlignedLength, upstream ? PROT_WRITE : PROT_READ,
@@ -91,25 +91,37 @@
 
     if (upstream)
     {
-        std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
-        stream.seekg(offset);
+        rc = lseek(fd, offset, SEEK_SET);
+        if (rc == -1)
+        {
+            std::cerr << "lseek failed, ERROR=" << errno
+                      << ", UPSTREAM=" << upstream << ", OFFSET=" << offset
+                      << "\n";
+            return rc;
+        }
 
         // Writing to the VGA memory should be aligned at page boundary,
         // otherwise write data into a buffer aligned at page boundary and
         // then write to the VGA memory.
         std::vector<char> buffer{};
         buffer.resize(pageAlignedLength);
-        stream.read(buffer.data(), length);
-        memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
-               pageAlignedLength);
-
-        if (static_cast<uint32_t>(stream.gcount()) != length)
+        rc = read(fd, buffer.data(), length);
+        if (rc == -1)
+        {
+            std::cerr << "file read failed, ERROR=" << errno
+                      << ", UPSTREAM=" << upstream << ", LENGTH=" << length
+                      << ", OFFSET=" << offset << "\n";
+            return rc;
+        }
+        if (rc != static_cast<int>(length))
         {
             std::cerr << "mismatch between number of characters to read and "
-                      << "the length read, LENGTH=" << length
-                      << " COUNT=" << stream.gcount() << "\n";
+                      << "the length read, LENGTH=" << length << " COUNT=" << rc
+                      << "\n";
             return -1;
         }
+        memcpy(static_cast<char*>(vgaMemPtr.get()), buffer.data(),
+               pageAlignedLength);
     }
 
     AspeedXdmaOp xdmaOp;
@@ -129,15 +141,22 @@
 
     if (!upstream)
     {
-        std::ios_base::openmode mode = std::ios::out | std::ios::binary;
-        if (fs::exists(path))
+        rc = lseek(fd, offset, SEEK_SET);
+        if (rc == -1)
         {
-            mode |= std::ios::in;
+            std::cerr << "lseek failed, ERROR=" << errno
+                      << ", UPSTREAM=" << upstream << ", OFFSET=" << offset
+                      << "\n";
+            return rc;
         }
-        std::ofstream stream(path.string(), mode);
-
-        stream.seekp(offset);
-        stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
+        rc = write(fd, static_cast<const char*>(vgaMemPtr.get()), length);
+        if (rc == -1)
+        {
+            std::cerr << "file write failed, ERROR=" << errno
+                      << ", UPSTREAM=" << upstream << ", LENGTH=" << length
+                      << ", OFFSET=" << offset << "\n";
+            return rc;
+        }
     }
 
     return 0;
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index 231222c..5c357d5 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -1,11 +1,16 @@
 #pragma once
 
 #include "handler.hpp"
+#include "libpldmresponder/utils.hpp"
 
+#include <fcntl.h>
 #include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <filesystem>
+#include <iostream>
 #include <vector>
 
 #include "libpldm/base.h"
@@ -49,7 +54,7 @@
      *
      * @return returns 0 on success, negative errno on failure
      */
-    int transferDataHost(const fs::path& path, uint32_t offset, uint32_t length,
+    int transferDataHost(int fd, uint32_t offset, uint32_t length,
                          uint64_t address, bool upstream);
 };
 
@@ -81,9 +86,32 @@
     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
+    int flags{};
+    if (upstream)
+    {
+        flags = O_RDONLY;
+    }
+    else if (fs::exists(path))
+    {
+        flags = O_RDWR;
+    }
+    else
+    {
+        flags = O_WRONLY;
+    }
+    int file = open(path.string().c_str(), flags);
+    if (file == -1)
+    {
+        std::cerr << "File does not exist, path = " << path.string() << "\n";
+        encode_rw_file_memory_resp(instanceId, command, PLDM_ERROR, 0,
+                                   responsePtr);
+        return response;
+    }
+    utils::CustomFD fd(file);
+
     while (length > dma::maxSize)
     {
-        auto rc = intf->transferDataHost(path, offset, dma::maxSize, address,
+        auto rc = intf->transferDataHost(fd(), offset, dma::maxSize, address,
                                          upstream);
         if (rc < 0)
         {
@@ -97,7 +125,7 @@
         address += dma::maxSize;
     }
 
-    auto rc = intf->transferDataHost(path, offset, length, address, upstream);
+    auto rc = intf->transferDataHost(fd(), offset, length, address, upstream);
     if (rc < 0)
     {
         encode_rw_file_memory_resp(instanceId, command, PLDM_ERROR, 0,
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index 7a95a6d..7d001b9 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -27,13 +27,36 @@
 
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
+int FileHandler::transferFileData(int32_t fd, bool upstream, uint32_t offset,
+                                  uint32_t& length, uint64_t address)
+{
+    dma::DMA xdmaInterface;
+    while (length > dma::maxSize)
+    {
+        auto rc = xdmaInterface.transferDataHost(fd, offset, dma::maxSize,
+                                                 address, upstream);
+        if (rc < 0)
+        {
+            return PLDM_ERROR;
+        }
+        offset += dma::maxSize;
+        length -= dma::maxSize;
+        address += dma::maxSize;
+    }
+    auto rc =
+        xdmaInterface.transferDataHost(fd, offset, length, address, upstream);
+    return rc < 0 ? PLDM_ERROR : PLDM_SUCCESS;
+}
+
 int FileHandler::transferFileData(const fs::path& path, bool upstream,
                                   uint32_t offset, uint32_t& length,
                                   uint64_t address)
 {
+    bool fileExists = false;
     if (upstream)
     {
-        if (!fs::exists(path))
+        fileExists = fs::exists(path);
+        if (!fileExists)
         {
             std::cerr << "File does not exist. PATH=" << path.c_str() << "\n";
             return PLDM_INVALID_FILE_HANDLE;
@@ -52,23 +75,29 @@
         }
     }
 
-    dma::DMA xdmaInterface;
-
-    while (length > dma::maxSize)
+    int flags{};
+    if (upstream)
     {
-        auto rc = xdmaInterface.transferDataHost(path, offset, dma::maxSize,
-                                                 address, upstream);
-        if (rc < 0)
-        {
-            return PLDM_ERROR;
-        }
-        offset += dma::maxSize;
-        length -= dma::maxSize;
-        address += dma::maxSize;
+        flags = O_RDONLY;
     }
-    auto rc =
-        xdmaInterface.transferDataHost(path, offset, length, address, upstream);
-    return rc < 0 ? PLDM_ERROR : PLDM_SUCCESS;
+    else if (fileExists)
+    {
+        flags = O_RDWR;
+    }
+    else
+    {
+        flags = O_WRONLY;
+    }
+    int file = open(path.string().c_str(), flags);
+    if (file == -1)
+    {
+        std::cerr << "File does not exist, PATH = " << path.string() << "\n";
+        ;
+        return PLDM_ERROR;
+    }
+    utils::CustomFD fd(file);
+
+    return transferFileData(fd(), upstream, offset, length, address);
 }
 
 std::unique_ptr<FileHandler> getHandlerByType(uint16_t fileType,
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.hpp b/oem/ibm/libpldmresponder/file_io_by_type.hpp
index 779eaef..b8621f5 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.hpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.hpp
@@ -75,6 +75,9 @@
                                  uint32_t offset, uint32_t& length,
                                  uint64_t address);
 
+    virtual int transferFileData(int fd, bool upstream, uint32_t offset,
+                                 uint32_t& length, uint64_t address);
+
     /** @brief Constructor to create a FileHandler object
      */
     FileHandler(uint32_t fileHandle) : fileHandle(fileHandle)
diff --git a/oem/ibm/libpldmresponder/file_io_type_pel.cpp b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
index c919c27..06ced26 100644
--- a/oem/ibm/libpldmresponder/file_io_type_pel.cpp
+++ b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
@@ -24,16 +24,98 @@
 namespace responder
 {
 
-int PelHandler::readIntoMemory(uint32_t /*offset*/, uint32_t& /*length*/,
-                               uint64_t /*address*/)
+int PelHandler::readIntoMemory(uint32_t offset, uint32_t& length,
+                               uint64_t address)
 {
-    return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
+    static constexpr auto logInterface = "org.open_power.Logging.PEL";
+
+    static sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+
+    try
+    {
+        auto service = getService(bus, logObjPath, logInterface);
+        auto method = bus.new_method_call(service.c_str(), logObjPath,
+                                          logInterface, "GetPEL");
+        method.append(fileHandle);
+        auto reply = bus.call(method);
+        sdbusplus::message::unix_fd fd{};
+        reply.read(fd);
+        auto rc = transferFileData(fd, true, offset, length, address);
+        return rc;
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "GetPEL D-Bus call failed, PEL id = " << fileHandle
+                  << ", error = " << e.what() << "\n";
+        return PLDM_ERROR;
+    }
+
+    return PLDM_SUCCESS;
 }
 
-int PelHandler::read(uint32_t /*offset*/, uint32_t& /*length*/,
-                     Response& /*response*/)
+int PelHandler::read(uint32_t offset, uint32_t& length, Response& response)
 {
-    return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
+    static constexpr auto logInterface = "org.open_power.Logging.PEL";
+    static sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+
+    try
+    {
+        auto service = getService(bus, logObjPath, logInterface);
+        auto method = bus.new_method_call(service.c_str(), logObjPath,
+                                          logInterface, "GetPEL");
+        method.append(fileHandle);
+        auto reply = bus.call(method);
+        sdbusplus::message::unix_fd fd{};
+        reply.read(fd);
+        std::cerr << "GetPEL D-Bus call done\n";
+        off_t fileSize = lseek(fd, 0, SEEK_END);
+        if (fileSize == -1)
+        {
+            std::cerr << "file seek failed";
+            return PLDM_ERROR;
+        }
+        if (offset >= fileSize)
+        {
+            std::cerr << "Offset exceeds file size, OFFSET=" << offset
+                      << " FILE_SIZE=" << fileSize << std::endl;
+            return PLDM_DATA_OUT_OF_RANGE;
+        }
+        if (offset + length > fileSize)
+        {
+            length = fileSize - offset;
+        }
+        auto rc = lseek(fd, offset, SEEK_SET);
+        if (rc == -1)
+        {
+            std::cerr << "file seek failed";
+            return PLDM_ERROR;
+        }
+        size_t currSize = response.size();
+        response.resize(currSize + length);
+        auto filePos = reinterpret_cast<char*>(response.data());
+        filePos += currSize;
+        rc = ::read(fd, filePos, length);
+        if (rc == -1)
+        {
+            std::cerr << "file read failed";
+            return PLDM_ERROR;
+        }
+        if (rc != length)
+        {
+            std::cerr << "mismatch between number of characters to read and "
+                      << "the length read, LENGTH=" << length << " COUNT=" << rc
+                      << std::endl;
+            return PLDM_ERROR;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "GetPEL D-Bus call failed";
+        return PLDM_ERROR;
+    }
+    return PLDM_SUCCESS;
 }
 
 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length,
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index e62e8e1..86df25e 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -96,9 +96,8 @@
 class MockDMA
 {
   public:
-    MOCK_METHOD5(transferDataHost,
-                 int(const fs::path& file, uint32_t offset, uint32_t length,
-                     uint64_t address, bool upstream));
+    MOCK_METHOD5(transferDataHost, int(int fd, uint32_t offset, uint32_t length,
+                                       uint64_t address, bool upstream));
 };
 
 } // namespace dma
@@ -113,13 +112,16 @@
     using namespace pldm::responder::dma;
 
     MockDMA dmaObj;
-    fs::path path("");
+    char tmpfile[] = "/tmp/pldm_fileio_table.XXXXXX";
+    int fd = mkstemp(tmpfile);
+    close(fd);
+    fs::path path(tmpfile);
 
     // Minimum length of 16 and expect transferDataHost to be called once
     // returns the default value of 0 (the return type of transferDataHost is
     // int, the default value for int is 0)
     uint32_t length = minSize;
-    EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1);
+    EXPECT_CALL(dmaObj, transferDataHost(_, 0, length, 0, true)).Times(1);
     auto response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY,
                                          path, 0, length, 0, true, 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -129,7 +131,7 @@
 
     // maxsize of DMA
     length = maxSize;
-    EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1);
+    EXPECT_CALL(dmaObj, transferDataHost(_, 0, length, 0, true)).Times(1);
     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
                                     0, length, 0, true, 0);
     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -139,8 +141,8 @@
 
     // length greater than maxsize of DMA
     length = maxSize + minSize;
-    EXPECT_CALL(dmaObj, transferDataHost(path, 0, maxSize, 0, true)).Times(1);
-    EXPECT_CALL(dmaObj, transferDataHost(path, maxSize, minSize, maxSize, true))
+    EXPECT_CALL(dmaObj, transferDataHost(_, 0, maxSize, 0, true)).Times(1);
+    EXPECT_CALL(dmaObj, transferDataHost(_, maxSize, minSize, maxSize, true))
         .Times(1);
     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
                                     0, length, 0, true, 0);
@@ -161,7 +163,7 @@
 
     // check for downstream(copy data from host to BMC) parameter
     length = minSize;
-    EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, false)).Times(1);
+    EXPECT_CALL(dmaObj, transferDataHost(_, 0, length, 0, false)).Times(1);
     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
                                     0, length, 0, false, 0);
     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -175,7 +177,10 @@
     using namespace pldm::responder::dma;
 
     MockDMA dmaObj;
-    fs::path path("");
+    char tmpfile[] = "/tmp/pldm_fileio_table.XXXXXX";
+    int fd = mkstemp(tmpfile);
+    close(fd);
+    fs::path path(tmpfile);
 
     // Minimum length of 16 and transferDataHost returning a negative errno
     uint32_t length = minSize;