oem-ibm : Support New record to the backplane vpd

This commit is to support a new VPD record called PSPD that contains
few keywords called #D and VM. Since these keywords are typically
larger in size (4K bytes) than what is managed by PLDM, we are adding
a file into which these keywords will be extracted. This keyword file
is transferred to the HB PLDM layer with a readFileByType request
from HB.

This also includes transfer of PSPD record data via PLDM with
offset included.

Tested by using pldmtool to send read request command to PLDM

Change-Id: Iba814a802cbbd2c2d641ee5a4c81919c3b8bdf20
Signed-off-by: Varsha Kaverappa <vkaverap@in.ibm.com>
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index 5a43bd6..7a4429c 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -6,6 +6,7 @@
 #include "file_io_type_lid.hpp"
 #include "file_io_type_pel.hpp"
 #include "file_io_type_progress_src.hpp"
+#include "file_io_type_vpd.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
 #include <libpldm/base.h>
@@ -167,6 +168,10 @@
             return std::make_unique<LidHandler>(fileHandle, false,
                                                 PLDM_FILE_TYPE_LID_RUNNING);
         }
+        case PLDM_FILE_TYPE_PSPD_VPD_PDD_KEYWORD:
+        {
+            return std::make_unique<keywordHandler>(fileHandle, fileType);
+        }
         default:
         {
             throw InternalFailure();
diff --git a/oem/ibm/libpldmresponder/file_io_type_vpd.cpp b/oem/ibm/libpldmresponder/file_io_type_vpd.cpp
new file mode 100644
index 0000000..783683e
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_vpd.cpp
@@ -0,0 +1,118 @@
+#include "file_io_type_vpd.hpp"
+
+#include "libpldm/base.h"
+#include "libpldm/file_io.h"
+
+#include "common/utils.hpp"
+
+#include <stdint.h>
+
+#include <iostream>
+
+typedef uint8_t byte;
+
+namespace pldm
+{
+namespace responder
+{
+int keywordHandler::read(uint32_t offset, uint32_t& length, Response& response,
+                         oem_platform::Handler* /*oemPlatformHandler*/)
+{
+    const char* keywrdObjPath =
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard";
+    const char* keywrdPropName = "PD_D";
+    const char* keywrdInterface = "com.ibm.ipzvpd.PSPD";
+
+    std::variant<std::vector<byte>> keywrd;
+
+    try
+    {
+        auto& bus = pldm::utils::DBusHandler::getBus();
+        auto service = pldm::utils::DBusHandler().getService(keywrdObjPath,
+                                                             keywrdInterface);
+        auto method = bus.new_method_call(service.c_str(), keywrdObjPath,
+                                          "org.freedesktop.DBus.Properties",
+                                          "Get");
+        method.append(keywrdInterface, keywrdPropName);
+        auto reply = bus.call(method);
+        reply.read(keywrd);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Get keyword error from dbus interface : "
+                  << keywrdInterface << " ERROR= " << e.what() << std::endl;
+    }
+
+    uint32_t keywrdSize = std::get<std::vector<byte>>(keywrd).size();
+
+    if (length < keywrdSize)
+    {
+        std::cerr << "length requested is less the keyword size, length: "
+                  << length << " keyword size: " << keywrdSize << std::endl;
+        return PLDM_ERROR_INVALID_DATA;
+    }
+
+    namespace fs = std::filesystem;
+    constexpr auto keywrdDirPath = "/tmp/pldm/";
+    constexpr auto keywrdFilePath = "/tmp/pldm/vpdKeywrd.bin";
+
+    if (!fs::exists(keywrdDirPath))
+    {
+        fs::create_directories(keywrdDirPath);
+        fs::permissions(keywrdDirPath,
+                        fs::perms::others_read | fs::perms::owner_write);
+    }
+
+    std::ofstream keywrdFile(keywrdFilePath);
+    auto fd = open(keywrdFilePath, std::ios::out | std::ofstream::binary);
+    if (!keywrdFile)
+    {
+        std::cerr << "VPD keyword file open error: " << keywrdFilePath
+                  << " errno: " << errno << std::endl;
+        pldm::utils::reportError(
+            "xyz.openbmc_project.PLDM.Error.readKeywordHandler.keywordFileOpenError");
+        return PLDM_ERROR;
+    }
+
+    if (offset > keywrdSize)
+    {
+        std::cerr << "Offset exceeds file size, OFFSET=" << offset
+                  << " FILE_SIZE=" << keywrdSize << std::endl;
+        return PLDM_DATA_OUT_OF_RANGE;
+    }
+
+    // length of keyword data should be same as keyword data size in dbus object
+    length = static_cast<uint32_t>(keywrdSize) - offset;
+
+    auto returnCode = lseek(fd, offset, SEEK_SET);
+    if (returnCode == -1)
+    {
+        std::cerr
+            << "Could not find keyword data at given offset. File Seek failed"
+            << std::endl;
+        return PLDM_ERROR;
+    }
+
+    keywrdFile.write((const char*)std::get<std::vector<byte>>(keywrd).data(),
+                     keywrdSize);
+    if (keywrdFile.bad())
+    {
+        std::cerr << "Error while writing to file: " << keywrdFilePath
+                  << std::endl;
+    }
+    keywrdFile.close();
+
+    auto rc = readFile(keywrdFilePath, offset, keywrdSize, response);
+    fs::remove(keywrdFilePath);
+    if (rc)
+    {
+        std::cerr << "Read error for keyword file with size: " << keywrdSize
+                  << std::endl;
+        pldm::utils::reportError(
+            "xyz.openbmc_project.PLDM.Error.readKeywordHandler.keywordFileReadError");
+        return PLDM_ERROR;
+    }
+    return PLDM_SUCCESS;
+}
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_type_vpd.hpp b/oem/ibm/libpldmresponder/file_io_type_vpd.hpp
new file mode 100644
index 0000000..d5cf412
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_vpd.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "file_io_by_type.hpp"
+
+namespace pldm
+{
+namespace responder
+{
+using vpdFileType = uint16_t;
+/** @class keywordFileHandler
+ *
+ *  @brief Inherits and implements FileHandler. This class is used
+ *  to read #D keyword file
+ */
+class keywordHandler : public FileHandler
+{
+  public:
+    /** @brief Handler constructor
+     */
+    keywordHandler(uint32_t fileHandle, uint16_t fileType) :
+        FileHandler(fileHandle), vpdFileType(fileType)
+    {}
+    virtual int writeFromMemory(uint32_t /*offset*/, uint32_t /*length*/,
+                                uint64_t /*address*/,
+                                oem_platform::Handler* /*oemPlatformHandler*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+    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*/);
+    virtual int write(const char* /*buffer*/, uint32_t /*offset*/,
+                      uint32_t& /*length*/,
+                      oem_platform::Handler* /*oemPlatformHandler*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+    virtual int fileAck(uint8_t /*fileStatus*/)
+    {
+        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 keywordHandler destructor
+     */
+    ~keywordHandler() {}
+
+  private:
+    uint16_t vpdFileType; //!< type of the VPD file
+};
+} // namespace responder
+} // namespace pldm