oem-ibm: implement WriteFileByTypeFromMemory handler

This commit implements a framework for handling oem file types
received to/from host. Along with that it also implements the responder
for oem command WriteFileByTypeFromMemory and processes PELs received
from the host firmware.

Change-Id: Ice866aed0343b90769013c4be31a0c730f6e6bcd
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index 64b020b..692ae1b 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -2,9 +2,11 @@
 
 #include "file_io.hpp"
 
+#include "file_io_by_type.hpp"
 #include "file_table.hpp"
 #include "libpldmresponder/utils.hpp"
 #include "registration.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
 
 #include <fcntl.h>
 #include <sys/mman.h>
@@ -14,6 +16,8 @@
 
 #include <cstring>
 #include <fstream>
+#include <memory>
+#include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 
 #include "libpldm/base.h"
@@ -21,6 +25,9 @@
 namespace pldm
 {
 
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
 namespace responder
 {
 
@@ -36,6 +43,8 @@
                     std::move(writeFileFromMemory));
     registerHandler(PLDM_OEM, PLDM_READ_FILE, std::move(readFile));
     registerHandler(PLDM_OEM, PLDM_WRITE_FILE, std::move(writeFile));
+    registerHandler(PLDM_OEM, PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
+                    std::move(writeFileByTypeFromMemory));
 }
 
 } // namespace oem_ibm
@@ -144,8 +153,12 @@
 
     if (!upstream)
     {
-        std::ofstream stream(path.string(),
-                             std::ios::in | std::ios::out | std::ios::binary);
+        std::ios_base::openmode mode = std::ios::out | std::ios::binary;
+        if (fs::exists(path))
+        {
+            mode |= std::ios::in;
+        }
+        std::ofstream stream(path.string(), mode);
 
         stream.seekp(offset);
         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
@@ -523,5 +536,66 @@
     return response;
 }
 
+Response writeFileByTypeFromMemory(const pldm_msg* request,
+                                   size_t payloadLength)
+{
+    Response response(
+        sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_MEM_RESP_BYTES, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    if (payloadLength != PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES)
+    {
+        encode_rw_file_by_type_memory_resp(
+            request->hdr.instance_id, PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
+            PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
+        return response;
+    }
+
+    uint16_t fileType{};
+    uint32_t fileHandle{};
+    uint32_t offset{};
+    uint32_t length{};
+    uint64_t address{};
+    auto rc = decode_rw_file_by_type_memory_req(request, payloadLength,
+                                                &fileType, &fileHandle, &offset,
+                                                &length, &address);
+    if (rc != PLDM_SUCCESS)
+    {
+        encode_rw_file_by_type_memory_resp(request->hdr.instance_id,
+                                           PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
+                                           rc, 0, responsePtr);
+        return response;
+    }
+    if (length % dma::minSize)
+    {
+        log<level::ERR>("Write length is not a multiple of DMA minSize",
+                        entry("LENGTH=%d", length));
+        encode_rw_file_by_type_memory_resp(
+            request->hdr.instance_id, PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
+            PLDM_INVALID_WRITE_LENGTH, 0, responsePtr);
+        return response;
+    }
+
+    std::unique_ptr<FileHandler> handler{};
+    try
+    {
+        handler = getHandlerByType(fileType, fileHandle);
+    }
+    catch (const InternalFailure& e)
+    {
+        log<level::ERR>("unknown file type ", entry("TYPE=%d", fileType));
+        encode_rw_file_by_type_memory_resp(
+            request->hdr.instance_id, PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY,
+            PLDM_INVALID_FILE_TYPE, 0, responsePtr);
+        return response;
+    }
+
+    rc = handler->writeFromMemory(offset, length, address);
+    encode_rw_file_by_type_memory_resp(request->hdr.instance_id,
+                                       PLDM_WRITE_FILE_BY_TYPE_FROM_MEMORY, rc,
+                                       length, responsePtr);
+    return response;
+}
+
 } // namespace responder
 } // namespace pldm