Sync ibm-pldm-oem repository changes to pldm repository

Change-Id: I6f30b39f483647ad84fe2fbe1c24298841040801
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/.gitignore b/.gitignore
index 26a8d6d..f13de14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,3 +64,10 @@
 /test/libpldmresponder_base_test
 /test/libpldm_bios_test
 /test/libpldmresponder_bios_formatTime_test
+/test/libpldm_platform_test
+/test/libpldmoem_fileio_test
+/test/libpldmoemresponder_fileio_test
+/test/libpldmresponder_bios_test
+/test/libpldmresponder_file_io_test
+/test/libpldmresponder_pdr_state_effecter_test
+
diff --git a/configure.ac b/configure.ac
index 3ffeca4..e06f062 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,7 @@
 AS_IF([test "x$enable_oem_ibm" == "xyes"], [
     AX_APPEND_COMPILE_FLAGS([-DOEM_IBM], [CXXFLAGS])
     AX_APPEND_COMPILE_FLAGS([-DOEM_IBM], [CFLAGS])
+    AC_DEFINE(FILE_TABLE_JSON, "/var/lib/pldm/fileTable.json", [JSON file containing file info for File I/O])
 ])
 
 # Setup verbose option
diff --git a/libpldm/pldm_types.h b/libpldm/pldm_types.h
index 3470413..093d016 100644
--- a/libpldm/pldm_types.h
+++ b/libpldm/pldm_types.h
@@ -30,4 +30,42 @@
 
 typedef uint8_t bool8_t;
 
+typedef union {
+	uint32_t value;
+	struct {
+		uint8_t bit0 : 1;
+		uint8_t bit1 : 1;
+		uint8_t bit2 : 1;
+		uint8_t bit3 : 1;
+		uint8_t bit4 : 1;
+		uint8_t bit5 : 1;
+		uint8_t bit6 : 1;
+		uint8_t bit7 : 1;
+		uint8_t bit8 : 1;
+		uint8_t bit9 : 1;
+		uint8_t bit10 : 1;
+		uint8_t bit11 : 1;
+		uint8_t bit12 : 1;
+		uint8_t bit13 : 1;
+		uint8_t bit14 : 1;
+		uint8_t bit15 : 1;
+		uint8_t bit16 : 1;
+		uint8_t bit17 : 1;
+		uint8_t bit18 : 1;
+		uint8_t bit19 : 1;
+		uint8_t bit20 : 1;
+		uint8_t bit21 : 1;
+		uint8_t bit22 : 1;
+		uint8_t bit23 : 1;
+		uint8_t bit24 : 1;
+		uint8_t bit25 : 1;
+		uint8_t bit26 : 1;
+		uint8_t bit27 : 1;
+		uint8_t bit28 : 1;
+		uint8_t bit29 : 1;
+		uint8_t bit30 : 1;
+		uint8_t bit31 : 1;
+	} __attribute__((packed)) bits;
+} bitfield32_t;
+
 #endif /* PLDM_TYPES_H */
diff --git a/libpldmresponder/Makefile.am b/libpldmresponder/Makefile.am
index 0bd1ea5..c40b749 100644
--- a/libpldmresponder/Makefile.am
+++ b/libpldmresponder/Makefile.am
@@ -23,12 +23,13 @@
 
 libpldmresponder_la_CPPFLAGS = \
 	$(CODE_COVERAGE_CPPFLAGS) \
-   	-I$(top_builddir)/libpldm/ \
+	-I$(top_builddir)/libpldm/ \
 	-I$(top_srcdir)
 
 if OEM_IBM
 libpldmresponder_la_SOURCES += \
-    $(top_builddir)/oem/ibm/libpldmresponder/file_io.cpp
+    $(top_builddir)/oem/ibm/libpldmresponder/file_io.cpp \
+    $(top_builddir)/oem/ibm/libpldmresponder/file_table.cpp
 libpldmresponder_la_CPPFLAGS += \
         -I$(top_builddir)/oem/ibm/
 endif
diff --git a/oem/ibm/libpldm/file_io.c b/oem/ibm/libpldm/file_io.c
index 2fa3034..a415414 100644
--- a/oem/ibm/libpldm/file_io.c
+++ b/oem/ibm/libpldm/file_io.c
@@ -52,3 +52,56 @@
 
 	return PLDM_SUCCESS;
 }
+
+int decode_get_file_table_req(const uint8_t *msg, size_t payload_length,
+			      uint32_t *transfer_handle,
+			      uint8_t *transfer_opflag, uint8_t *table_type)
+{
+	if (msg == NULL || transfer_handle == NULL || transfer_opflag == NULL ||
+	    table_type == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length != PLDM_GET_FILE_TABLE_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_get_file_table_req *request =
+	    (struct pldm_get_file_table_req *)msg;
+
+	*transfer_handle = le32toh(request->transfer_handle);
+	*transfer_opflag = request->operation_flag;
+	*table_type = request->table_type;
+
+	return PLDM_SUCCESS;
+}
+
+int encode_get_file_table_resp(uint8_t instance_id, uint8_t completion_code,
+			       uint32_t next_transfer_handle,
+			       uint8_t transfer_flag, const uint8_t *table_data,
+			       size_t table_size, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_GET_FILE_TABLE;
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_get_file_table_resp *response =
+	    (struct pldm_get_file_table_resp *)msg->payload;
+	response->completion_code = completion_code;
+
+	if (completion_code == PLDM_SUCCESS) {
+		response->next_transfer_handle = htole32(next_transfer_handle);
+		response->transfer_flag = transfer_flag;
+		memcpy(response->table_data, table_data, table_size);
+	}
+
+	return PLDM_SUCCESS;
+}
diff --git a/oem/ibm/libpldm/file_io.h b/oem/ibm/libpldm/file_io.h
index 79a8035..eb23332 100644
--- a/oem/ibm/libpldm/file_io.h
+++ b/oem/ibm/libpldm/file_io.h
@@ -13,6 +13,7 @@
 /** @brief PLDM Commands in IBM OEM type
  */
 enum pldm_fileio_commands {
+	PLDM_GET_FILE_TABLE = 0x1,
 	PLDM_READ_FILE_INTO_MEMORY = 0x6,
 	PLDM_WRITE_FILE_FROM_MEMORY = 0x7,
 };
@@ -24,10 +25,21 @@
 	PLDM_DATA_OUT_OF_RANGE = 0x81,
 	PLDM_INVALID_READ_LENGTH = 0x82,
 	PLDM_INVALID_WRITE_LENGTH = 0x83,
+	PLDM_FILE_TABLE_UNAVAILABLE = 0x84,
+	PLDM_INVALID_FILE_TABLE_TYPE = 0x85,
+};
+
+/** @brief PLDM File I/O table types
+ */
+enum pldm_fileio_table_type {
+	PLDM_FILE_ATTRIBUTE_TABLE = 0,
+	PLDM_OEM_FILE_ATTRIBUTE_TABLE = 1,
 };
 
 #define PLDM_RW_FILE_MEM_REQ_BYTES 20
 #define PLDM_RW_FILE_MEM_RESP_BYTES 5
+#define PLDM_GET_FILE_TABLE_REQ_BYTES 6
+#define PLDM_GET_FILE_TABLE_MIN_RESP_BYTES 6
 
 /** @brief Decode ReadFileIntoMemory and WriteFileFromMemory commands request
  *         data
@@ -61,6 +73,58 @@
 			       uint8_t completion_code, uint32_t length,
 			       struct pldm_msg *msg);
 
+/** @struct pldm_get_file_table_req
+ *
+ *  Structure representing GetFileTable request
+ */
+struct pldm_get_file_table_req {
+	uint32_t transfer_handle; //!< Data transfer handle
+	uint8_t operation_flag;   //!< Transfer operation flag
+	uint8_t table_type;       //!< Table type
+} __attribute__((packed));
+
+/** @struct pldm_get_file_table_resp
+ *
+ *  Structure representing GetFileTable response fixed data
+ */
+struct pldm_get_file_table_resp {
+	uint8_t completion_code;       //!< Completion code
+	uint32_t next_transfer_handle; //!< Next data transfer handle
+	uint8_t transfer_flag;	 //!< Transfer flag
+	uint8_t table_data[1];	 //!< Table Data
+} __attribute__((packed));
+
+/** @brief Decode GetFileTable command request data
+ *
+ *  @param[in] msg - Pointer to PLDM request message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] trasnfer_handle - the handle of data
+ *  @param[out] transfer_opflag - Transfer operation flag
+ *  @param[out] table_type - the type of file table
+ *  @return pldm_completion_codes
+ */
+int decode_get_file_table_req(const uint8_t *msg, size_t payload_length,
+			      uint32_t *transfer_handle,
+			      uint8_t *transfer_opflag, uint8_t *table_type);
+
+/** @brief Create a PLDM response for GetFileTable command
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] next_transfer_handle - Handle to identify next portion of
+ *              data transfer
+ *  @param[in] transfer_flag - Represents the part of transfer
+ *  @param[in] table_data - pointer to file table data
+ *  @param[in] table_size - file table size
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg'
+ */
+int encode_get_file_table_resp(uint8_t instance_id, uint8_t completion_code,
+			       uint32_t next_transfer_handle,
+			       uint8_t transfer_flag, const uint8_t *table_data,
+			       size_t table_size, struct pldm_msg *msg);
+
 #ifdef __cplusplus
 }
 #endif
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
diff --git a/oem/ibm/test/libpldm_fileio_test.cpp b/oem/ibm/test/libpldm_fileio_test.cpp
index e9fa325..2a96742 100644
--- a/oem/ibm/test/libpldm_fileio_test.cpp
+++ b/oem/ibm/test/libpldm_fileio_test.cpp
@@ -130,3 +130,120 @@
     ASSERT_EQ(response->hdr.command, PLDM_WRITE_FILE_FROM_MEMORY);
     ASSERT_EQ(response->payload[0], PLDM_ERROR);
 }
+
+TEST(GetFileTable, GoodDecodeRequest)
+{
+    std::array<uint8_t, PLDM_GET_FILE_TABLE_REQ_BYTES> requestMsg{};
+
+    // Random value for DataTransferHandle, TransferOperationFlag, TableType
+    uint32_t transferHandle = 0x12345678;
+    uint8_t transferOpFlag = 1;
+    uint8_t tableType = 1;
+
+    memcpy(requestMsg.data(), &transferHandle, sizeof(transferHandle));
+    memcpy(requestMsg.data() + sizeof(transferHandle), &transferOpFlag,
+           sizeof(transferOpFlag));
+    memcpy(requestMsg.data() + sizeof(transferHandle) + sizeof(transferOpFlag),
+           &tableType, sizeof(tableType));
+
+    uint32_t retTransferHandle = 0;
+    uint8_t retTransferOpFlag = 0;
+    uint8_t retTableType = 0;
+
+    // Invoke decode get file table request
+    auto rc = decode_get_file_table_req(requestMsg.data(), requestMsg.size(),
+                                        &retTransferHandle, &retTransferOpFlag,
+                                        &retTableType);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(transferHandle, retTransferHandle);
+    ASSERT_EQ(transferOpFlag, retTransferOpFlag);
+    ASSERT_EQ(tableType, retTableType);
+}
+
+TEST(GetFileTable, BadDecodeRequest)
+{
+    uint32_t transferHandle = 0;
+    uint8_t transferOpFlag = 0;
+    uint8_t tableType = 0;
+
+    // Request payload message is missing
+    auto rc = decode_get_file_table_req(nullptr, 0, &transferHandle,
+                                        &transferOpFlag, &tableType);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    std::array<uint8_t, PLDM_GET_FILE_TABLE_REQ_BYTES> requestMsg{};
+
+    // TableType is NULL
+    rc = decode_get_file_table_req(requestMsg.data(), requestMsg.size(),
+                                   &transferHandle, &transferOpFlag, nullptr);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // Payload length is invalid
+    rc = decode_get_file_table_req(requestMsg.data(), 0, &transferHandle,
+                                   &transferOpFlag, &tableType);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(GetFileTable, GoodEncodeResponse)
+{
+    // Random value for NextDataTransferHandle and TransferFlag
+    uint8_t completionCode = 0;
+    uint32_t nextTransferHandle = 0x87654321;
+    uint8_t transferFlag = 5;
+    // Mock file table contents of size 5
+    std::array<uint8_t, 5> fileTable = {1, 2, 3, 4, 5};
+    constexpr size_t responseSize = sizeof(completionCode) +
+                                    sizeof(nextTransferHandle) +
+                                    sizeof(transferFlag) + fileTable.size();
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + responseSize> responseMsg{};
+    pldm_msg* response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    // GetFileTable
+    auto rc = encode_get_file_table_resp(0, PLDM_SUCCESS, nextTransferHandle,
+                                         transferFlag, fileTable.data(),
+                                         fileTable.size(), response);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(response->hdr.request, PLDM_RESPONSE);
+    ASSERT_EQ(response->hdr.instance_id, 0);
+    ASSERT_EQ(response->hdr.type, PLDM_OEM);
+    ASSERT_EQ(response->hdr.command, PLDM_GET_FILE_TABLE);
+    ASSERT_EQ(response->payload[0], PLDM_SUCCESS);
+    ASSERT_EQ(0, memcmp(response->payload + sizeof(response->payload[0]),
+                        &nextTransferHandle, sizeof(nextTransferHandle)));
+    ASSERT_EQ(0, memcmp(response->payload + sizeof(response->payload[0]) +
+                            sizeof(nextTransferHandle),
+                        &transferFlag, sizeof(transferFlag)));
+    ASSERT_EQ(0, memcmp(response->payload + sizeof(response->payload[0]) +
+                            sizeof(nextTransferHandle),
+                        &transferFlag, sizeof(transferFlag)));
+    ASSERT_EQ(0, memcmp(response->payload + sizeof(response->payload[0]) +
+                            sizeof(nextTransferHandle) + sizeof(transferFlag),
+                        fileTable.data(), fileTable.size()));
+}
+
+TEST(GetFileTable, BadEncodeResponse)
+{
+    uint8_t completionCode = 0;
+    uint32_t nextTransferHandle = 0;
+    uint8_t transferFlag = 0;
+    constexpr size_t responseSize = sizeof(completionCode) +
+                                    sizeof(nextTransferHandle) +
+                                    sizeof(transferFlag);
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + responseSize> responseMsg{};
+    pldm_msg* response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    // GetFileTable
+    auto rc = encode_get_file_table_resp(0, PLDM_ERROR, nextTransferHandle,
+                                         transferFlag, nullptr, 0, response);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(response->hdr.request, PLDM_RESPONSE);
+    ASSERT_EQ(response->hdr.instance_id, 0);
+    ASSERT_EQ(response->hdr.type, PLDM_OEM);
+    ASSERT_EQ(response->hdr.command, PLDM_GET_FILE_TABLE);
+    ASSERT_EQ(response->payload[0], PLDM_ERROR);
+}
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index 1338713..7c0c129 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -1,4 +1,9 @@
 #include "libpldmresponder/file_io.hpp"
+#include "libpldmresponder/file_table.hpp"
+
+#include <filesystem>
+#include <fstream>
+#include <nlohmann/json.hpp>
 
 #include "libpldm/base.h"
 #include "libpldm/file_io.h"
@@ -6,6 +11,7 @@
 #include <gmock/gmock-matchers.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+
 #define SD_JOURNAL_SUPPRESS_LOCATION
 
 #include <systemd/sd-journal.h>
@@ -28,6 +34,72 @@
 }
 }
 
+namespace fs = std::filesystem;
+using Json = nlohmann::json;
+using namespace pldm::filetable;
+
+class TestFileTable : public testing::Test
+{
+  public:
+    void SetUp() override
+    {
+        // Create a temporary directory to hold the config file and files to
+        // populate the file table.
+        char tmppldm[] = "/tmp/pldm_fileio_table.XXXXXX";
+        dir = fs::path(mkdtemp(tmppldm));
+
+        // Copy the sample image files to the directory
+        fs::copy("./files", dir);
+
+        imageFile = dir / "NVRAM-IMAGE";
+        auto jsonObjects = Json::array();
+        auto obj = Json::object();
+        obj["path"] = imageFile.c_str();
+        obj["file_traits"] = 1;
+
+        jsonObjects.push_back(obj);
+        obj.clear();
+        cksumFile = dir / "NVRAM-IMAGE-CKSUM";
+        obj["path"] = cksumFile.c_str();
+        obj["file_traits"] = 4;
+        jsonObjects.push_back(obj);
+
+        fileTableConfig = dir / "configFile.json";
+        std::ofstream file(fileTableConfig.c_str());
+        file << std::setw(4) << jsonObjects << std::endl;
+    }
+
+    void TearDown() override
+    {
+        fs::remove_all(dir);
+    }
+
+    fs::path dir;
+    fs::path imageFile;
+    fs::path cksumFile;
+    fs::path fileTableConfig;
+
+    // <4 bytes - File handle - 0 (0x00 0x00 0x00 0x00)>,
+    // <2 bytes - Filename length - 11 (0x0b 0x00>
+    // <11 bytes - Filename - ASCII for NVRAM-IMAGE>
+    // <4 bytes - File size - 1024 (0x00 0x04 0x00 0x00)>
+    // <4 bytes - File traits - 1 (0x01 0x00 0x00 0x00)>
+    // <4 bytes - File handle - 1 (0x01 0x00 0x00 0x00)>,
+    // <2 bytes - Filename length - 17 (0x11 0x00>
+    // <17 bytes - Filename - ASCII for NVRAM-IMAGE-CKSUM>
+    // <4 bytes - File size - 16 (0x0f 0x00 0x00 0x00)>
+    // <4 bytes - File traits - 4 (0x04 0x00 0x00 0x00)>
+    // No pad bytes added since the length for both the file entries in the
+    // table is 56, which is a multiple of 4.
+    // <4 bytes - Checksum - 2088303182(0x4e 0xfa 0x78 0x7c)>
+    Table attrTable = {
+        0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d, 0x2d,
+        0x49, 0x4d, 0x41, 0x47, 0x45, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d,
+        0x2d, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x2d, 0x43, 0x4b, 0x53, 0x55, 0x4d,
+        0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4e, 0xfa, 0x78, 0x7c};
+};
+
 namespace pldm
 {
 
@@ -163,6 +235,130 @@
     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
 }
 
+TEST_F(TestFileTable, ReadFileInvalidFileHandle)
+{
+    // Invalid file handle in the file table
+    uint32_t fileHandle = 2;
+    uint32_t offset = 0;
+    uint32_t length = 0;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidOffset)
+{
+    uint32_t fileHandle = 0;
+    // The file size is 1024, so the offset is invalid
+    uint32_t offset = 1024;
+    uint32_t length = 0;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidLength)
+{
+    uint32_t fileHandle = 0;
+    uint32_t offset = 100;
+    // Length should be a multiple of dma min size(16)
+    uint32_t length = 10;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidEffectiveLength)
+{
+    uint32_t fileHandle = 0;
+    // valid offset
+    uint32_t offset = 100;
+    // length + offset exceeds the size, so effective length is
+    // filesize(1024) - offset(100). The effective length is not a multiple of
+    // DMA min size(16)
+    uint32_t length = 1024;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
+    // Clear the file table contents.
+    table.clear();
+}
+
 TEST(WriteFileFromMemory, BadPath)
 {
     uint32_t fileHandle = 0;
@@ -192,3 +388,175 @@
     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_WRITE_LENGTH);
 }
+
+TEST_F(TestFileTable, WriteFileInvalidFileHandle)
+{
+    // Invalid file handle in the file table
+    uint32_t fileHandle = 2;
+    uint32_t offset = 0;
+    uint32_t length = 16;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = writeFileFromMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, WriteFileInvalidOffset)
+{
+    uint32_t fileHandle = 0;
+    // The file size is 1024, so the offset is invalid
+    uint32_t offset = 1024;
+    uint32_t length = 16;
+    uint64_t address = 0;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    memcpy(request->payload, &fileHandle, sizeof(fileHandle));
+    memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(TestFileTable::fileTableConfig.c_str());
+
+    auto response = writeFileFromMemory(request, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST(FileTable, ConfigNotExist)
+{
+    logs.clear();
+    FileTable tableObj("");
+    EXPECT_EQ(logs.size(), 1);
+}
+
+TEST_F(TestFileTable, ValidateFileEntry)
+{
+    FileTable tableObj(fileTableConfig.c_str());
+
+    // Test file handle 0, the file size is 1K bytes.
+    auto value = tableObj.at(0);
+    ASSERT_EQ(value.handle, 0);
+    ASSERT_EQ(strcmp(value.fsPath.c_str(), imageFile.c_str()), 0);
+    ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value.fsPath)), 1024);
+    ASSERT_EQ(value.traits.value, 1);
+    ASSERT_EQ(true, fs::exists(value.fsPath));
+
+    // Test file handle 1, the file size is 16 bytes
+    auto value1 = tableObj.at(1);
+    ASSERT_EQ(value1.handle, 1);
+    ASSERT_EQ(strcmp(value1.fsPath.c_str(), cksumFile.c_str()), 0);
+    ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value1.fsPath)), 16);
+    ASSERT_EQ(value1.traits.value, 4);
+    ASSERT_EQ(true, fs::exists(value1.fsPath));
+
+    // Test invalid file handle
+    ASSERT_THROW(tableObj.at(2), std::out_of_range);
+}
+
+TEST_F(TestFileTable, ValidateFileTable)
+{
+    FileTable tableObj(fileTableConfig.c_str());
+
+    // Validate file attribute table
+    auto table = tableObj();
+    ASSERT_EQ(true,
+              std::equal(attrTable.begin(), attrTable.end(), table.begin()));
+}
+
+TEST_F(TestFileTable, GetFileTableCommand)
+{
+    // Initialise the file table with a valid handle of 0 & 1
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    uint32_t transferHandle = 0;
+    uint8_t opFlag = 0;
+    uint8_t type = PLDM_FILE_ATTRIBUTE_TABLE;
+    uint32_t nextTransferHandle = 0;
+    uint8_t transferFlag = PLDM_START_AND_END;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
+        requestMsg{};
+    auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    auto request = reinterpret_cast<pldm_get_file_table_req*>(
+        requestMsg.data() + sizeof(pldm_msg_hdr));
+    request->transfer_handle = transferHandle;
+    request->operation_flag = opFlag;
+    request->table_type = type;
+
+    auto response = getFileTable(requestMsgPtr, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
+    size_t offsetSize = sizeof(responsePtr->payload[0]);
+    ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &nextTransferHandle,
+                        sizeof(nextTransferHandle)));
+    offsetSize += sizeof(nextTransferHandle);
+    ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &transferFlag,
+                        sizeof(transferFlag)));
+    offsetSize += sizeof(transferFlag);
+    ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, attrTable.data(),
+                        attrTable.size()));
+    table.clear();
+}
+
+TEST_F(TestFileTable, GetFileTableCommandReqLengthMismatch)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    // Pass invalid command payload length
+    auto response = getFileTable(request, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST_F(TestFileTable, GetFileTableCommandOEMAttrTable)
+{
+    uint32_t transferHandle = 0;
+    uint8_t opFlag = 0;
+    uint8_t type = PLDM_OEM_FILE_ATTRIBUTE_TABLE;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
+        requestMsg{};
+    auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
+    auto request = reinterpret_cast<pldm_get_file_table_req*>(
+        requestMsg.data() + sizeof(pldm_msg_hdr));
+    request->transfer_handle = transferHandle;
+    request->operation_flag = opFlag;
+    request->table_type = type;
+
+    auto response = getFileTable(requestMsgPtr, requestPayloadLength);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_TABLE_TYPE);
+}
diff --git a/test/Makefile.am b/test/Makefile.am
index d580416..fef5f96 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -113,7 +113,8 @@
         $(top_builddir)/pldmd-registration.o \
 	$(top_builddir)/libpldm/libpldm_la-base.o \
 	$(top_builddir)/oem/ibm/libpldm/libpldm_la-file_io.o \
-	$(top_builddir)/oem/ibm/libpldmresponder/libpldmresponder_la-file_io.o
+	$(top_builddir)/oem/ibm/libpldmresponder/libpldmresponder_la-file_io.o\
+	$(top_builddir)/oem/ibm/libpldmresponder/libpldmresponder_la-file_table.o
 libpldmoemresponder_fileio_test_SOURCES = $(top_builddir)/oem/ibm/test/libpldmresponder_fileio_test.cpp
 endif
 
diff --git a/test/files/NVRAM-IMAGE b/test/files/NVRAM-IMAGE
new file mode 100644
index 0000000..06d7405
--- /dev/null
+++ b/test/files/NVRAM-IMAGE
Binary files differ
diff --git a/test/files/NVRAM-IMAGE-CKSUM b/test/files/NVRAM-IMAGE-CKSUM
new file mode 100644
index 0000000..01d633b
--- /dev/null
+++ b/test/files/NVRAM-IMAGE-CKSUM
Binary files differ