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/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 116bfa5..770ef8c 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -20,7 +20,9 @@
if get_option('oem-ibm').enabled()
sources += [
'../oem/ibm/libpldmresponder/file_io.cpp',
- '../oem/ibm/libpldmresponder/file_table.cpp'
+ '../oem/ibm/libpldmresponder/file_table.cpp',
+ '../oem/ibm/libpldmresponder/file_io_by_type.cpp',
+ '../oem/ibm/libpldmresponder/file_io_type_pel.cpp'
]
endif
diff --git a/meson.build b/meson.build
index 547466a..13a1899 100644
--- a/meson.build
+++ b/meson.build
@@ -19,6 +19,7 @@
conf_data.set_quoted('PDR_JSONS_DIR', '/usr/share/pldm/pdr')
if get_option('oem-ibm').enabled()
conf_data.set_quoted('FILE_TABLE_JSON', '/usr/share/pldm/fileTable.json')
+ conf_data.set_quoted('PEL_TEMP_DIR', '/tmp/pel')
add_global_arguments('-DOEM_IBM', language : 'c')
add_global_arguments('-DOEM_IBM', language : 'cpp')
endif
diff --git a/oem/ibm/libpldm/file_io.h b/oem/ibm/libpldm/file_io.h
index 15b568b..c7b4a7c 100644
--- a/oem/ibm/libpldm/file_io.h
+++ b/oem/ibm/libpldm/file_io.h
@@ -31,6 +31,7 @@
PLDM_INVALID_WRITE_LENGTH = 0x83,
PLDM_FILE_TABLE_UNAVAILABLE = 0x84,
PLDM_INVALID_FILE_TABLE_TYPE = 0x85,
+ PLDM_INVALID_FILE_TYPE = 0x86,
};
/** @brief PLDM File I/O table types
@@ -40,6 +41,12 @@
PLDM_OEM_FILE_ATTRIBUTE_TABLE = 1,
};
+/** @brief PLDM File I/O table types
+ */
+enum pldm_fileio_file_type {
+ PLDM_FILE_TYPE_PEL = 0,
+};
+
#define PLDM_RW_FILE_MEM_REQ_BYTES 20
#define PLDM_RW_FILE_MEM_RESP_BYTES 5
#define PLDM_GET_FILE_TABLE_REQ_BYTES 6
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
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index 0ee4ed6..2086fc1 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -139,6 +139,17 @@
*/
Response writeFileFromMemory(const pldm_msg* request, size_t payloadLength);
+/** @brief Handler for writeFileByTypeFromMemory command
+ *
+ * @param[in] request - pointer to PLDM request payload
+ * @param[in] payloadLength - length of the message
+ *
+ * @return PLDM response message
+ */
+
+Response writeFileByTypeFromMemory(const pldm_msg* request,
+ size_t payloadLength);
+
/** @brief Handler for GetFileTable command
*
* @param[in] request - pointer to PLDM request payload
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
new file mode 100644
index 0000000..d8a40a5
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -0,0 +1,73 @@
+#include "config.h"
+
+#include "file_io_by_type.hpp"
+
+#include "file_io_type_pel.hpp"
+#include "libpldmresponder/utils.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <exception>
+#include <filesystem>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <vector>
+#include <xyz/openbmc_project/Logging/Entry/server.hpp>
+
+#include "libpldm/base.h"
+#include "oem/ibm/libpldm/file_io.h"
+
+namespace pldm
+{
+namespace responder
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+int FileHandler::transferFileData(const fs::path& path, bool upstream,
+ uint32_t offset, uint32_t length,
+ uint64_t address)
+{
+ dma::DMA xdmaInterface;
+
+ while (length > dma::maxSize)
+ {
+ auto rc = xdmaInterface.transferDataHost(path, offset, dma::maxSize,
+ address, upstream);
+ if (rc < 0)
+ {
+ return PLDM_ERROR;
+ }
+ offset += dma::maxSize;
+ length -= dma::maxSize;
+ address += dma::maxSize;
+ }
+ auto rc =
+ xdmaInterface.transferDataHost(path, offset, length, address, upstream);
+ return rc < 0 ? PLDM_ERROR : PLDM_SUCCESS;
+}
+
+std::unique_ptr<FileHandler> getHandlerByType(uint16_t fileType,
+ uint32_t fileHandle)
+{
+ switch (fileType)
+ {
+ case PLDM_FILE_TYPE_PEL:
+ {
+ return std::make_unique<PelHandler>(fileHandle);
+ break;
+ }
+ default:
+ {
+ elog<InternalFailure>();
+ break;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.hpp b/oem/ibm/libpldmresponder/file_io_by_type.hpp
new file mode 100644
index 0000000..6c31252
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_by_type.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "file_io.hpp"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace fs = std::filesystem;
+
+/**
+ * @class FileHandler
+ *
+ * Base class to handle read/write of all oem file types
+ */
+class FileHandler
+{
+ public:
+ /** @brief Method to write an oem file type from host memory. Individual
+ * file types need to override this method to do the file specific
+ * processing
+ * @param[in] offset - offset to read/write
+ * @param[in] length - length to be read/write mentioned by Host
+ * @param[in] address - DMA address
+ * @return PLDM status code
+ */
+ virtual int writeFromMemory(uint32_t offset, uint32_t length,
+ uint64_t address) = 0;
+
+ /** @brief Method to do the file content transfer ove DMA between host and
+ * bmc. This method is made virtual to be overridden in test case. And need
+ * not be defined in other child classes
+ *
+ * @param[in] path - file system path where read/write will be done
+ * @param[in] upstream - direction of DMA transfer. "false" means a
+ * transfer from host to BMC
+ * @param[in] offset - offset to read/write
+ * @param[in] length - length to be read/write mentioned by Host
+ * @param[in] address - DMA address
+ *
+ * @return PLDM status code
+ */
+ virtual int transferFileData(const fs::path& path, bool upstream,
+ uint32_t offset, uint32_t length,
+ uint64_t address);
+
+ /** @brief Constructor to create a FileHandler object
+ */
+ FileHandler(uint32_t fileHandle) : fileHandle(fileHandle)
+ {
+ }
+
+ /** FileHandler destructor
+ */
+ virtual ~FileHandler()
+ {
+ }
+
+ protected:
+ uint32_t fileHandle; //!< file handle indicating name of file or invalid
+};
+
+/** @brief Method to create individual file handler objects based on file type
+ *
+ * @param[in] fileType - type of file
+ * @param[in] fileHandle - file handle
+ */
+
+std::unique_ptr<FileHandler> getHandlerByType(uint16_t fileType,
+ uint32_t fileHandle);
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_type_pel.cpp b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
new file mode 100644
index 0000000..400d336
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
@@ -0,0 +1,85 @@
+#include "config.h"
+
+#include "file_io_type_pel.hpp"
+
+#include "libpldmresponder/utils.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <stdint.h>
+#include <systemd/sd-bus.h>
+#include <unistd.h>
+
+#include <exception>
+#include <filesystem>
+#include <sdbusplus/server.hpp>
+#include <vector>
+#include <xyz/openbmc_project/Logging/Entry/server.hpp>
+
+#include "libpldm/base.h"
+#include "oem/ibm/libpldm/file_io.h"
+
+namespace pldm
+{
+namespace responder
+{
+
+using namespace phosphor::logging;
+
+int PelHandler::writeFromMemory(uint32_t offset, uint32_t length,
+ uint64_t address)
+{
+ fs::create_directories(PEL_TEMP_DIR);
+
+ auto timeMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::high_resolution_clock::now().time_since_epoch())
+ .count();
+ std::string fileName(PEL_TEMP_DIR);
+ fileName += "/pel." + std::to_string(timeMs);
+ fs::path path(std::move(fileName));
+
+ auto rc = transferFileData(path, false, offset, length, address);
+ if (rc == PLDM_SUCCESS)
+ {
+ rc = storePel(path.string());
+ }
+ fs::remove(path);
+ return rc;
+}
+
+int PelHandler::storePel(std::string&& pelFileName)
+{
+ static constexpr auto logObjPath = "/xyz/openbmc_project/logging";
+ static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create";
+
+ static sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+
+ try
+ {
+ auto service = getService(bus, logObjPath, logInterface);
+ using namespace sdbusplus::xyz::openbmc_project::Logging::server;
+ std::map<std::string, std::string> addlData{};
+ addlData.emplace("RAWPEL", std::move(pelFileName));
+ auto severity =
+ sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
+ sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
+ Error);
+
+ auto method = bus.new_method_call(service.c_str(), logObjPath,
+ logInterface, "Create");
+ method.append("xyz.openbmc_project.Host.Error.Event", severity,
+ addlData);
+ bus.call_noreply(method);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("failed to make a d-bus call to PEL daemon",
+ entry("ERROR=%s", e.what()));
+ return PLDM_ERROR;
+ }
+
+ return PLDM_SUCCESS;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_type_pel.hpp b/oem/ibm/libpldmresponder/file_io_type_pel.hpp
new file mode 100644
index 0000000..92bcbf6
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_pel.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "file_io_by_type.hpp"
+
+namespace pldm
+{
+namespace responder
+{
+
+using namespace pldm::responder::dma;
+
+/** @class PelHandler
+ *
+ * @brief Inherits and implements FileHandler. This class is used
+ * to read/write pels.
+ */
+class PelHandler : public FileHandler
+{
+ public:
+ /** @brief PelHandler constructor
+ */
+ PelHandler(uint32_t fileHandle) : FileHandler(fileHandle)
+ {
+ }
+
+ virtual int writeFromMemory(uint32_t offset, uint32_t length,
+ uint64_t address);
+
+ /** @brief method to store a pel file in tempfs and send
+ * d-bus notification to pel daemon that it is ready for consumption
+ *
+ * @param[in] pelFileName - the pel file path
+ */
+ virtual int storePel(std::string&& pelFileName);
+
+ /** @brief PelHandler destructor
+ */
+ ~PelHandler()
+ {
+ }
+};
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index 3ebf96a..603c0c6 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -1,9 +1,13 @@
#include "libpldmresponder/file_io.hpp"
+#include "libpldmresponder/file_io_by_type.hpp"
+#include "libpldmresponder/file_io_type_pel.hpp"
#include "libpldmresponder/file_table.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
+#include <phosphor-logging/elog-errors.hpp>
#include "libpldm/base.h"
#include "libpldm/file_io.h"
@@ -719,3 +723,46 @@
table.clear();
}
+
+TEST(writeFileByTypeFromMemory, testBadPath)
+{
+ const auto hdr_size = sizeof(pldm_msg_hdr);
+ std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
+ requestMsg{};
+ auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ size_t requestPayloadLength = requestMsg.size() - hdr_size;
+ struct pldm_read_write_file_by_type_memory_req* request =
+ reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
+ req->payload);
+ request->file_type = PLDM_FILE_TYPE_PEL;
+ request->file_handle = 0xFFFFFFFF;
+ request->offset = 0;
+ request->length = 17;
+ request->address = 0;
+
+ auto response = writeFileByTypeFromMemory(req, 0);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+ struct pldm_read_write_file_by_type_memory_resp* resp =
+ reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
+ responsePtr->payload);
+ ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
+
+ response = writeFileByTypeFromMemory(req, requestPayloadLength);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+ resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
+ responsePtr->payload);
+ ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code);
+}
+
+TEST(getHandlerByType, allPaths)
+{
+ uint32_t fileHandle{};
+ auto handler = getHandlerByType(PLDM_FILE_TYPE_PEL, fileHandle);
+ auto pelType = dynamic_cast<PelHandler*>(handler.get());
+ ASSERT_TRUE(pelType != nullptr);
+
+ using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+ ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure);
+}
diff --git a/test/meson.build b/test/meson.build
index db250a7..1749605 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -41,6 +41,6 @@
implicit_include_directories: false,
link_args: dynamic_linker,
build_rpath: get_option('oe-sdk').enabled() ? rpath : '',
- dependencies: [libpldm, libpldmresponder, gtest, gmock, dependency('sdbusplus')]),
+ dependencies: [libpldm, libpldmresponder, gtest, gmock, dependency('sdbusplus'),dependency('phosphor-logging')]),
workdir: meson.current_source_dir())
endforeach