Sync ibm-pldm-oem repository changes to pldm repository

Change-Id: I6f30b39f483647ad84fe2fbe1c24298841040801
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index 2fec526..6b893ed 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -1,5 +1,8 @@
+#include "config.h"
+
 #include "file_io.hpp"
 
+#include "file_table.hpp"
 #include "libpldmresponder/utils.hpp"
 #include "registration.hpp"
 
@@ -26,6 +29,7 @@
 
 void registerHandlers()
 {
+    registerHandler(PLDM_OEM, PLDM_GET_FILE_TABLE, std::move(getFileTable));
     registerHandler(PLDM_OEM, PLDM_READ_FILE_INTO_MEMORY,
                     std::move(readFileIntoMemory));
     registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY,
@@ -98,10 +102,17 @@
 
     if (upstream)
     {
-        std::ifstream stream(path.string());
-
+        std::ifstream stream(path.string(), std::ios::in | std::ios::binary);
         stream.seekg(offset);
-        stream.read(static_cast<char*>(vgaMemPtr.get()), length);
+
+        // 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)
         {
@@ -131,7 +142,8 @@
 
     if (!upstream)
     {
-        std::ofstream stream(path.string());
+        std::ofstream stream(path.string(),
+                             std::ios::in | std::ios::out | std::ios::binary);
 
         stream.seekp(offset);
         stream.write(static_cast<const char*>(vgaMemPtr.get()), length);
@@ -148,7 +160,6 @@
     uint32_t offset = 0;
     uint32_t length = 0;
     uint64_t address = 0;
-    fs::path path("");
 
     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -163,7 +174,24 @@
     decode_rw_file_memory_req(request->payload, payloadLength, &fileHandle,
                               &offset, &length, &address);
 
-    if (!fs::exists(path))
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(FILE_TABLE_JSON);
+    FileEntry value{};
+
+    try
+    {
+        value = table.at(fileHandle);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("File handle does not exist in the file table",
+                        entry("HANDLE=%d", fileHandle));
+        encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
+                                   PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
+        return response;
+    }
+
+    if (!fs::exists(value.fsPath))
     {
         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
@@ -171,7 +199,7 @@
         return response;
     }
 
-    auto fileSize = fs::file_size(path);
+    auto fileSize = fs::file_size(value.fsPath);
     if (offset >= fileSize)
     {
         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
@@ -197,8 +225,8 @@
 
     using namespace dma;
     DMA intf;
-    return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, path, offset,
-                            length, address, true);
+    return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
+                            offset, length, address, true);
 }
 
 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength)
@@ -207,7 +235,6 @@
     uint32_t offset = 0;
     uint32_t length = 0;
     uint64_t address = 0;
-    fs::path path("");
 
     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -231,7 +258,24 @@
         return response;
     }
 
-    if (!fs::exists(path))
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(FILE_TABLE_JSON);
+    FileEntry value{};
+
+    try
+    {
+        value = table.at(fileHandle);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("File handle does not exist in the file table",
+                        entry("HANDLE=%d", fileHandle));
+        encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
+                                   PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
+        return response;
+    }
+
+    if (!fs::exists(value.fsPath))
     {
         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
@@ -239,7 +283,7 @@
         return response;
     }
 
-    auto fileSize = fs::file_size(path);
+    auto fileSize = fs::file_size(value.fsPath);
     if (offset >= fileSize)
     {
         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
@@ -251,8 +295,59 @@
 
     using namespace dma;
     DMA intf;
-    return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, path, offset,
-                            length, address, false);
+    return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
+                            offset, length, address, false);
+}
+
+Response getFileTable(const pldm_msg* request, size_t payloadLength)
+{
+    uint32_t transferHandle = 0;
+    uint8_t transferFlag = 0;
+    uint8_t tableType = 0;
+
+    Response response(sizeof(pldm_msg_hdr) +
+                      PLDM_GET_FILE_TABLE_MIN_RESP_BYTES);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    if (payloadLength != PLDM_GET_FILE_TABLE_REQ_BYTES)
+    {
+        encode_get_file_table_resp(0, PLDM_ERROR_INVALID_LENGTH, 0, 0, nullptr,
+                                   0, responsePtr);
+        return response;
+    }
+
+    auto rc =
+        decode_get_file_table_req(request->payload, payloadLength,
+                                  &transferHandle, &transferFlag, &tableType);
+    if (rc)
+    {
+        encode_get_file_table_resp(0, rc, 0, 0, nullptr, 0, responsePtr);
+        return response;
+    }
+
+    if (tableType != PLDM_FILE_ATTRIBUTE_TABLE)
+    {
+        encode_get_file_table_resp(0, PLDM_INVALID_FILE_TABLE_TYPE, 0, 0,
+                                   nullptr, 0, responsePtr);
+        return response;
+    }
+
+    using namespace pldm::filetable;
+    auto table = buildFileTable(FILE_TABLE_JSON);
+    auto attrTable = table();
+    response.resize(response.size() + attrTable.size());
+    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    if (attrTable.empty())
+    {
+        encode_get_file_table_resp(0, PLDM_FILE_TABLE_UNAVAILABLE, 0, 0,
+                                   nullptr, 0, responsePtr);
+        return response;
+    }
+
+    encode_get_file_table_resp(0, PLDM_SUCCESS, 0, PLDM_START_AND_END,
+                               attrTable.data(), attrTable.size(), responsePtr);
+    return response;
 }
 
 } // namespace responder
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index 9f01782..106e2b2 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -134,5 +134,14 @@
  *  @return PLDM response message
  */
 Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength);
+
+/** @brief Handler for GetFileTable command
+ *
+ *  @param[in] request - pointer to PLDM request payload
+ *  @param[in] payloadLength - length of the message payload
+ *
+ *  @return PLDM response message
+ */
+Response getFileTable(const pldm_msg* request, size_t payloadLength);
 } // namespace responder
 } // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_table.cpp b/oem/ibm/libpldmresponder/file_table.cpp
new file mode 100644
index 0000000..9264f21
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_table.cpp
@@ -0,0 +1,129 @@
+#include "file_table.hpp"
+
+#include <boost/crc.hpp>
+#include <fstream>
+#include <phosphor-logging/log.hpp>
+
+namespace pldm
+{
+
+namespace filetable
+{
+
+using namespace phosphor::logging;
+
+FileTable::FileTable(const std::string& fileTableConfigPath)
+{
+    std::ifstream jsonFile(fileTableConfigPath);
+    if (!jsonFile.is_open())
+    {
+        log<level::ERR>("File table config file does not exist",
+                        entry("FILE=%s", fileTableConfigPath.c_str()));
+        return;
+    }
+
+    auto data = Json::parse(jsonFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        log<level::ERR>("Parsing config file failed");
+        return;
+    }
+
+    uint16_t fileNameLength = 0;
+    uint32_t fileSize = 0;
+    uint32_t traits = 0;
+    size_t tableSize = 0;
+    Handle handle = 0;
+    auto iter = fileTable.begin();
+
+    // Iterate through each JSON object in the config file
+    for (const auto& record : data)
+    {
+        constexpr auto path = "path";
+        constexpr auto fileTraits = "file_traits";
+
+        std::string filepath = record.value(path, "");
+        traits = static_cast<uint32_t>(record.value(fileTraits, 0));
+
+        fs::path fsPath(filepath);
+        if (!fs::is_regular_file(fsPath))
+        {
+            continue;
+        }
+
+        fileNameLength =
+            static_cast<uint16_t>(fsPath.filename().string().size());
+        fileSize = static_cast<uint32_t>(fs::file_size(fsPath));
+        tableSize = fileTable.size();
+
+        fileTable.resize(tableSize + sizeof(handle) + sizeof(fileNameLength) +
+                         fileNameLength + sizeof(fileSize) + sizeof(traits));
+        iter = fileTable.begin() + tableSize;
+
+        // Populate the file table with the contents of the JSON entry
+        std::copy_n(reinterpret_cast<uint8_t*>(&handle), sizeof(handle), iter);
+        std::advance(iter, sizeof(handle));
+
+        std::copy_n(reinterpret_cast<uint8_t*>(&fileNameLength),
+                    sizeof(fileNameLength), iter);
+        std::advance(iter, sizeof(fileNameLength));
+
+        std::copy_n(reinterpret_cast<const uint8_t*>(fsPath.filename().c_str()),
+                    fileNameLength, iter);
+        std::advance(iter, fileNameLength);
+
+        std::copy_n(reinterpret_cast<uint8_t*>(&fileSize), sizeof(fileSize),
+                    iter);
+        std::advance(iter, sizeof(fileSize));
+
+        std::copy_n(reinterpret_cast<uint8_t*>(&traits), sizeof(traits), iter);
+        std::advance(iter, sizeof(traits));
+
+        // Create the file entry for the JSON entry
+        FileEntry entry{};
+        entry.handle = handle;
+        entry.fsPath = std::move(fsPath);
+        entry.traits.value = traits;
+
+        // Insert the file entries in the map
+        tableEntries.emplace(handle, std::move(entry));
+        handle++;
+    }
+
+    constexpr uint8_t padWidth = 4;
+    tableSize = fileTable.size();
+    // Add pad bytes
+    if ((tableSize % padWidth) != 0)
+    {
+        padCount = padWidth - (tableSize % padWidth);
+        fileTable.resize(tableSize + padCount, 0);
+    }
+
+    // Calculate the checksum
+    boost::crc_32_type result;
+    result.process_bytes(fileTable.data(), fileTable.size());
+    checkSum = result.checksum();
+}
+
+Table FileTable::operator()() const
+{
+    Table table(fileTable);
+    table.resize(fileTable.size() + sizeof(checkSum));
+    auto iter = table.begin() + fileTable.size();
+    std::copy_n(reinterpret_cast<const uint8_t*>(&checkSum), sizeof(checkSum),
+                iter);
+    return table;
+}
+
+FileTable& buildFileTable(const std::string& fileTablePath)
+{
+    static FileTable table;
+    if (table.isEmpty())
+    {
+        table = std::move(FileTable(fileTablePath));
+    }
+    return table;
+}
+
+} // namespace filetable
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_table.hpp b/oem/ibm/libpldmresponder/file_table.hpp
new file mode 100644
index 0000000..4b82f29
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_table.hpp
@@ -0,0 +1,125 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <filesystem>
+#include <nlohmann/json.hpp>
+#include <vector>
+
+#include "libpldm/pldm_types.h"
+
+namespace pldm
+{
+
+namespace filetable
+{
+
+namespace fs = std::filesystem;
+using Handle = uint32_t;
+using Json = nlohmann::json;
+using Table = std::vector<uint8_t>;
+
+/** @struct FileEntry
+ *
+ *  Data structure for storing information regarding the files supported by
+ *  PLDM File I/O. The file handle is used to uniquely identify the file. The
+ *  traits provide information whether the file is Read only, Read/Write and
+ *  preserved across firmware upgrades.
+ */
+struct FileEntry
+{
+    Handle handle;       //!< File handle
+    fs::path fsPath;     //!< File path
+    bitfield32_t traits; //!< File traits
+};
+
+/** @class FileTable
+ *
+ *  FileTable class encapsulates the data related to files supported by PLDM
+ *  File I/O and provides interfaces to lookup files information based on the
+ *  file handle and extract the file attribute table. The file attribute table
+ *  comprises of metadata for files. Metadata includes the file handle, file
+ *  name, current file size and file traits.
+ */
+class FileTable
+{
+  public:
+    /** @brief The file table is initialised by parsing the config file
+     *         containing information about the files.
+     *
+     * @param[in] fileTableConfigPath - path to the json file containing
+     *                                  information
+     */
+    FileTable(const std::string& fileTableConfigPath);
+    FileTable() = default;
+    ~FileTable() = default;
+    FileTable(const FileTable&) = default;
+    FileTable& operator=(const FileTable&) = default;
+    FileTable(FileTable&&) = default;
+    FileTable& operator=(FileTable&&) = default;
+
+    /** @brief Get the file attribute table
+     *
+     * @return Table- contents of the file attribute table
+     */
+    Table operator()() const;
+
+    /** @brief Get the FileEntry at the file handle
+     *
+     * @param[in] handle - file handle
+     *
+     * @return FileEntry - file entry at the handle
+     */
+    FileEntry at(Handle handle) const
+    {
+        return tableEntries.at(handle);
+    }
+
+    /** @brief Check is file attribute table is empty
+     *
+     * @return bool - true if file attribute table is empty, false otherwise.
+     */
+    bool isEmpty() const
+    {
+        return fileTable.empty();
+    }
+
+    /** @brief Clear the file table contents
+     *
+     */
+    void clear()
+    {
+        tableEntries.clear();
+        fileTable.clear();
+        padCount = 0;
+        checkSum = 0;
+    }
+
+  private:
+    /** @brief handle to FileEntry mappings for lookups based on file handle */
+    std::unordered_map<Handle, FileEntry> tableEntries;
+
+    /** @brief file attribute table including the pad bytes, except the checksum
+     */
+    std::vector<uint8_t> fileTable;
+
+    /** @brief the pad count of the file attribute table, the number of pad
+     * bytes is between 0 and 3 */
+    uint8_t padCount = 0;
+
+    /** @brief the checksum of the file attribute table */
+    uint32_t checkSum = 0;
+};
+
+/** @brief Build the file attribute table if not already built using the
+ *         file table config.
+ *
+ *  @param[in] fileTablePath - path of the file table config
+ *
+ *  @return FileTable& - Reference to instance of file table
+ */
+
+FileTable& buildFileTable(const std::string& fileTablePath);
+
+} // namespace filetable
+} // namespace pldm