oem-meta: Add file IO responder

Support handling file I/O request from BIC.

Tested: Verified in yosemite4 platform.

Change-Id: I4a630bffc5ab3fddea0618130d970b4c7ecf59f0
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
Signed-off-by: Sora Su <baxiche@gmail.com>
diff --git a/oem/meta/libpldmresponder/file_io.cpp b/oem/meta/libpldmresponder/file_io.cpp
new file mode 100644
index 0000000..e4800fb
--- /dev/null
+++ b/oem/meta/libpldmresponder/file_io.cpp
@@ -0,0 +1,132 @@
+#include "file_io.hpp"
+
+#include <libpldm/oem/meta/file_io.h>
+
+#include <filesystem>
+namespace pldm::responder::oem_meta
+{
+
+std::unique_ptr<FileHandler> FileIOHandler::getHandlerByType(
+    [[maybe_unused]] pldm_tid_t messageTid, FileIOType fileIOType)
+{
+    switch (fileIOType)
+    {
+        default:
+            error("Get invalid file io type {FILEIOTYPE}", "FILEIOTYPE",
+                  fileIOType);
+            break;
+    }
+    return nullptr;
+}
+
+Response FileIOHandler::writeFileIO(pldm_tid_t tid, const pldm_msg* request,
+                                    size_t payloadLength)
+{
+    static constexpr uint8_t decodeDataMaxLength = 240;
+    constexpr uint8_t decodereqlen =
+        PLDM_OEM_META_FILE_IO_WRITE_REQ_MIN_LENGTH + decodeDataMaxLength;
+    alignas(struct pldm_oem_meta_file_io_write_req) unsigned char
+        reqbuf[decodereqlen];
+    auto request_msg = new (reqbuf) pldm_oem_meta_file_io_write_req;
+    auto request_data = static_cast<uint8_t*>(
+        pldm_oem_meta_file_io_write_req_data(request_msg));
+
+    int rc = decode_oem_meta_file_io_write_req(request, payloadLength,
+                                               request_msg, decodereqlen);
+
+    if (rc != 0)
+    {
+        error("Failed to decode OEM Meta write file IO request");
+        return ccOnlyResponse(request, rc);
+    }
+
+    auto handler =
+        getHandlerByType(tid, static_cast<FileIOType>(request_msg->handle));
+    if (!handler)
+    {
+        return ccOnlyResponse(request, PLDM_ERROR_UNSUPPORTED_PLDM_CMD);
+    }
+
+    auto data = std::span(request_data, request_msg->length);
+
+    rc = handler->write(data);
+
+    return ccOnlyResponse(request, rc);
+}
+
+Response FileIOHandler::readFileIO(pldm_tid_t tid, const pldm_msg* request,
+                                   size_t payloadLength)
+{
+    int rc;
+    struct pldm_oem_meta_file_io_read_req request_msg = {};
+    request_msg.version = sizeof(struct pldm_oem_meta_file_io_read_req);
+
+    rc = decode_oem_meta_file_io_read_req(request, payloadLength, &request_msg);
+
+    if (rc != 0)
+    {
+        error("Failed to decode OEM Meta read file IO request");
+        return ccOnlyResponse(request, rc);
+    }
+
+    auto handler =
+        getHandlerByType(tid, static_cast<FileIOType>(request_msg.handle));
+
+    if (!handler)
+    {
+        return ccOnlyResponse(request, PLDM_ERROR_UNSUPPORTED_PLDM_CMD);
+    }
+
+    size_t encoderesplen = PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE;
+    if (request_msg.option == PLDM_OEM_META_FILE_IO_READ_ATTR)
+    {
+        encoderesplen += PLDM_OEM_META_FILE_IO_READ_ATTR_INFO_LENGTH;
+    }
+    else
+    {
+        encoderesplen += PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH;
+    }
+
+    std::vector<uint8_t> respbuf(
+        sizeof(struct pldm_oem_meta_file_io_read_resp) + request_msg.length);
+    auto* response_msg = new (respbuf.data()) pldm_oem_meta_file_io_read_resp;
+
+    response_msg->version = sizeof(struct pldm_oem_meta_file_io_read_resp);
+    response_msg->handle = request_msg.handle;
+    response_msg->option = request_msg.option;
+    response_msg->length = request_msg.length;
+
+    if (response_msg->option != PLDM_OEM_META_FILE_IO_READ_ATTR)
+    {
+        response_msg->info.data.transferFlag =
+            request_msg.info.data.transferFlag;
+        response_msg->info.data.offset = request_msg.info.data.offset;
+    }
+
+    rc = handler->read(response_msg);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    response_msg->completion_code = PLDM_SUCCESS;
+    encoderesplen += response_msg->length;
+
+    Response response(sizeof(pldm_msg_hdr) + encoderesplen, 0);
+    auto responseMsg = new (response.data()) pldm_msg;
+
+    rc = encode_oem_meta_file_io_read_resp(
+        request->hdr.instance_id, response_msg,
+        sizeof(struct pldm_oem_meta_file_io_read_resp) + response_msg->length,
+        responseMsg, encoderesplen);
+
+    if (rc != 0)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    return response;
+}
+
+} // namespace pldm::responder::oem_meta
diff --git a/oem/meta/libpldmresponder/file_io.hpp b/oem/meta/libpldmresponder/file_io.hpp
new file mode 100644
index 0000000..510d650
--- /dev/null
+++ b/oem/meta/libpldmresponder/file_io.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "common/utils.hpp"
+#include "file_io_by_type.hpp"
+#include "pldmd/handler.hpp"
+
+#include <libpldm/oem/meta/file_io.h>
+
+#include <phosphor-logging/lg2.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm::responder::oem_meta
+{
+
+class FileIOHandler : public CmdHandler
+{
+  public:
+    FileIOHandler()
+    {
+        handlers.emplace(
+            PLDM_OEM_META_FILE_IO_CMD_WRITE_FILE,
+            [this](pldm_tid_t tid, const pldm_msg* request,
+                   size_t payloadLength) {
+                return this->writeFileIO(tid, request, payloadLength);
+            });
+        handlers.emplace(
+            PLDM_OEM_META_FILE_IO_CMD_READ_FILE,
+            [this](pldm_tid_t tid, const pldm_msg* request,
+                   size_t payloadLength) {
+                return this->readFileIO(tid, request, payloadLength);
+            });
+    }
+
+    FileIOHandler(const FileIOHandler&) = delete;
+    FileIOHandler(FileIOHandler&&) = delete;
+    FileIOHandler& operator=(const FileIOHandler&) = delete;
+    FileIOHandler& operator=(FileIOHandler&&) = delete;
+    virtual ~FileIOHandler() = default;
+
+  private:
+    /** @brief Handler for writeFileIO command
+     *
+     *  @param[in] tid - the device tid
+     *  @param[in] request - pointer to PLDM request payload
+     *  @param[in] payloadLength - length of the message
+     *
+     *  @return PLDM response message
+     */
+    Response writeFileIO(pldm_tid_t tid, const pldm_msg* request,
+                         size_t payloadLength);
+
+    /** @brief Handler for readFileIO command
+     *
+     *  @param[in] tid - the device tid
+     *  @param[in] request - pointer to PLDM request payload
+     *  @param[in] payloadLength - length of the message
+     *
+     *  @return PLDM response message
+     */
+    Response readFileIO(pldm_tid_t tid, const pldm_msg* request,
+                        size_t payloadLength);
+
+    std::unique_ptr<FileHandler> getHandlerByType(pldm_tid_t tid,
+                                                  FileIOType fileIOType);
+};
+
+} // namespace pldm::responder::oem_meta
diff --git a/oem/meta/libpldmresponder/file_io_by_type.hpp b/oem/meta/libpldmresponder/file_io_by_type.hpp
new file mode 100644
index 0000000..c8e7d05
--- /dev/null
+++ b/oem/meta/libpldmresponder/file_io_by_type.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <libpldm/base.h>
+#include <libpldm/oem/meta/file_io.h>
+
+#include <cstdint>
+#include <span>
+
+namespace pldm::responder::oem_meta
+{
+using message = std::span<uint8_t>;
+
+enum class FileIOType : uint8_t
+{
+};
+
+/**
+ *  @class FileHandler
+ *
+ *  Base class to handle read/write of all OEM file types
+ */
+class FileHandler
+{
+  protected:
+    FileHandler() = default;
+
+  public:
+    FileHandler(const FileHandler&) = delete;
+    FileHandler(FileHandler&&) = delete;
+    FileHandler& operator=(const FileHandler&) = delete;
+    FileHandler& operator=(FileHandler&&) = delete;
+    virtual ~FileHandler() = default;
+
+    /** @brief Method to write data to BMC
+     *  @param[in] data - eventData
+     *  @return  PLDM status code
+     */
+    virtual int write([[maybe_unused]] const message& data)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    /** @brief Method to read data from BIC
+     *  @param[in] data - eventData
+     *  @return  PLDM status code
+     */
+    virtual int read(
+        [[maybe_unused]] struct pldm_oem_meta_file_io_read_resp* data)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+};
+
+} // namespace pldm::responder::oem_meta
diff --git a/oem/meta/libpldmresponder/meson.build b/oem/meta/libpldmresponder/meson.build
new file mode 100644
index 0000000..5494ada
--- /dev/null
+++ b/oem/meta/libpldmresponder/meson.build
@@ -0,0 +1 @@
+oem_files += files('file_io.cpp')
diff --git a/oem/meta/meson.build b/oem/meta/meson.build
index 13d3a8e..b4fc15e 100644
--- a/oem/meta/meson.build
+++ b/oem/meta/meson.build
@@ -1,3 +1,4 @@
 oem_files += files('oem_meta.cpp', 'utils.cpp')
 
+subdir('libpldmresponder')
 subdir('event')
diff --git a/oem/meta/oem_meta.cpp b/oem/meta/oem_meta.cpp
index 540b334..661fa95 100644
--- a/oem/meta/oem_meta.cpp
+++ b/oem/meta/oem_meta.cpp
@@ -1,15 +1,21 @@
-
 #include "oem_meta.hpp"
 
 #include <libpldm/base.h>
 
+#include <utility>
+
 namespace pldm::oem_meta
 {
 
-OemMETA::OemMETA(pldm::responder::platform::Handler* platformHandler)
+OemMETA::OemMETA(pldm::responder::Invoker& invoker,
+                 pldm::responder::platform::Handler* platformHandler)
 {
     oemEventManager = std::make_unique<oem_meta::OemEventManager>();
     registerOemEventHandler(platformHandler);
+
+    auto fileIOHandler =
+        std::make_unique<pldm::responder::oem_meta::FileIOHandler>();
+    registerOemHandler(invoker, std::move(fileIOHandler));
 }
 
 void OemMETA::registerOemEventHandler(
@@ -24,4 +30,11 @@
         }});
 }
 
+void OemMETA::registerOemHandler(
+    pldm::responder::Invoker& invoker,
+    std::unique_ptr<pldm::responder::oem_meta::FileIOHandler> fileIOHandler)
+{
+    invoker.registerHandler(PLDM_OEM, std::move(fileIOHandler));
+}
+
 } // namespace pldm::oem_meta
diff --git a/oem/meta/oem_meta.hpp b/oem/meta/oem_meta.hpp
index 266a8bf..df67367 100644
--- a/oem/meta/oem_meta.hpp
+++ b/oem/meta/oem_meta.hpp
@@ -2,6 +2,8 @@
 
 #include "libpldmresponder/platform.hpp"
 #include "oem/meta/event/oem_event_manager.hpp"
+#include "oem/meta/libpldmresponder/file_io.hpp"
+#include "pldmd/invoker.hpp"
 
 namespace pldm::oem_meta
 {
@@ -24,14 +26,21 @@
 
   public:
     /** @brief Constucts OemMETA object
+     *  @param[in] invoker - invoker handler
      *  @param[in] platformHandler - platformHandler handler
      */
-    explicit OemMETA(pldm::responder::platform::Handler* platformHandler);
+    explicit OemMETA(pldm::responder::Invoker& invoker,
+                     pldm::responder::platform::Handler* platformHandler);
 
   private:
     void registerOemEventHandler(
         pldm::responder::platform::Handler* platformHandler);
 
+    void registerOemHandler(
+        pldm::responder::Invoker& invoker,
+        std::unique_ptr<pldm::responder::oem_meta::FileIOHandler>
+            fileIOHandler);
+
     std::unique_ptr<oem_meta::OemEventManager> oemEventManager{};
 };