From e75e89239f44ab58a62d2841d6bc56f9a69f8d19 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Fri, 27 Mar 2026 17:54:58 -0500 Subject: [PATCH] pldmd: Signal and log OOB error event data Generate a PldmMessagePollEvent D-Bus signal when an Out-Of-Band (OOB) error event is received. In verbose mode only, also generate a log file with the data received as part of OOB PLDM error event. Signed-off-by: Shirish Pargaonkar --- common/utils.cpp | 89 ++++++++++++++++++++++++++ common/utils.hpp | 27 ++++++++ platform-mc/event_manager.cpp | 114 +++++++++++++++++++++++++++++++++- platform-mc/event_manager.hpp | 35 ++++++++++- platform-mc/manager.hpp | 8 ++- pldmd/pldmd.cpp | 2 +- 6 files changed, 266 insertions(+), 9 deletions(-) diff --git a/common/utils.cpp b/common/utils.cpp index cf52f86ac1..b181ba52a2 100644 --- a/common/utils.cpp +++ b/common/utils.cpp @@ -20,6 +20,13 @@ #include #include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + PHOSPHOR_LOG2_USING; namespace pldm @@ -610,6 +617,88 @@ int emitRDEDeviceDetectedSignal( return PLDM_SUCCESS; } +void emitPldmMessagePollEventSignal( + uint8_t formatVersion, uint8_t tid, const std::string& tName, + uint8_t eventClass, uint32_t dataTransferHandle, uint16_t eventId, + uint16_t eventDataSize, int fd) { + + try + { + auto& bus = DBusHandler::getBus(); + auto msg = bus.new_signal("/xyz/openbmc_project/pldm", + "xyz.openbmc_project.PLDM.Event", + "PldmMessagePollEvent"); + + // Generate the current timestamp in microseconds + uint64_t timestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + // Please follow/comply_with the signature in yaml file + msg.append(timestamp, + tid, + tName, + formatVersion, + eventClass, + eventId, + dataTransferHandle, + eventDataSize, + sdbusplus::message::unix_fd(fd)); + msg.signal_send(); + } + catch (const std::exception& e) + { + lg2::error("Failed to emit PLDM pldmMessagePollEvent signal: {ERROR}", + "ERROR", e.what()); + return; + } + + lg2::info("Emitting PLDM Message Poll Event signal: FormatVersion={FV}, TID={TID}, " + "TerminusName={NAME}, EventClass={CLASS}, EventId={ID}, " + "DataTransferHandlee={DTH}, " + "Size={SIZE}, FD={FD}", + "FV", formatVersion, + "TID", tid, + "NAME", tName, + "CLASS", lg2::hex, eventClass, + "ID", lg2::hex, eventId, + "DTH", lg2::hex, dataTransferHandle, + "SIZE", lg2::hex, eventDataSize, + "FD", fd); +} + +int create_mem_fd(const std::vector& data) +{ + // Create the anonymous file in RAM + int fd = memfd_create("event_data", MFD_CLOEXEC); + if (fd == -1) { + lg2::error("Failed to create memfd, errno: {ERRNO}", + "ERRNO", errno); + return -1; + } + + size_t dataSize = data.size(); + lg2::info("truncate memfd to size {SIZE}", "SIZE", dataSize); + + if (ftruncate(fd, dataSize) == -1) { + lg2::error("Failed to truncate memfd to size {SIZE}, errno: {ERRNO}", + "SIZE", dataSize, "ERRNO", errno); + close(fd); + return -1; + } + + if (write(fd, data.data(), dataSize) == -1) { + lg2::error("Failed to write to memfd, errno: {ERRNO}", "ERRNO", errno); + close(fd); + return -1; + } + + // Reset the file offset to the beginning for the receiver + lseek(fd, 0, SEEK_SET); + + return fd; +} + void recoverMctpEndpoint(const std::string& endpointObjPath) { auto& bus = DBusHandler::getBus(); diff --git a/common/utils.hpp b/common/utils.hpp index 3f00a9b374..6b74445373 100644 --- a/common/utils.hpp +++ b/common/utils.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -580,6 +581,32 @@ int emitRDEDeviceDetectedSignal( uint8_t tid, eid mctpEid, pldm::UUID devUUID, const std::vector>& pdrPayloads); +/** @brief Emit a D-Bus signal for a PLDM Message Poll event. + * + * This signal notifies subscribers that a message poll event has been + * received from a specific terminal, providing the necessary metadata + * to retrieve the event data. + * + * @param[in] formatVersion - The version of the event format (from PLDM spec) + * @param[in] tid - Terminal ID of the source + * @param[in] tName - Terminal name or identifier string + * @param[in] eventClass - The class of the event being polled + * @param[in] dataTransferHandle - Handle used to identify the data transfer + * @param[in] eventId - The unique identifier for the event + * @param[in] eventDataSize - The size of the event data in bytes + * @param[in] fd - File descriptor for the data transfer, if applicable + */ +void emitPldmMessagePollEventSignal( + uint8_t formatVersion, uint8_t tid, const std::string& tName, + uint8_t eventClass, uint32_t dataTransferHandle, + uint16_t eventId, uint16_t eventDataSize, int fd); + +/** @brief Create a memfd and write data to it + * @param[in] data - data to write to the memfd + * @return fd - the file descriptor of the memfd + */ +int create_mem_fd(const std::vector& data); + /** * @brief call Recover() method to recover an MCTP Endpoint * @param[in] MCTP Endpoint's object path diff --git a/platform-mc/event_manager.cpp b/platform-mc/event_manager.cpp index 8388836c6f..d0b476cf2c 100644 --- a/platform-mc/event_manager.cpp +++ b/platform-mc/event_manager.cpp @@ -19,6 +19,84 @@ namespace platform_mc { namespace fs = std::filesystem; +void EventManager::logPldmErrorEvent( + pldm_tid_t tid, uint16_t eventId, uint8_t eventClass, + const uint8_t* eventData, size_t eventDataSize) +{ + fs::path dir{"/tmp/pldmeventlog"}; + fs::path filePath{""}; + + try + { + // Ensure directory exists (safe even if already exists) + fs::create_directories(dir); + + // Epoch timestamp (seconds) + auto now = std::chrono::system_clock::now(); + auto epoch = + std::chrono::duration_cast( + now.time_since_epoch()) + .count(); + + static std::atomic counter{0}; + + // Build filename with hex fields + std::ostringstream filename; + filename << "pldm_oob_err_" << epoch + << "_" << counter++ + << "_ec0x" << std::hex << std::setw(2) << std::setfill('0') + << static_cast(eventClass) + << "_tid0x" << std::setw(2) + << static_cast(tid) + << "_eventid0x" << std::setw(4) + << eventId + << ".bin"; + + filePath = dir / filename.str(); + + // Open file + std::ofstream ofs(filePath, std::ios::binary); + if (!ofs) + { + lg2::error( + "Failed to open PLDM event file. PATH={PATH} ERRNO={ERRNO} ERRMSG={ERRMSG}", + "PATH", filePath.string(), + "ERRNO", errno, + "ERRMSG", std::strerror(errno)); + + return; + } + + // Write raw event data + ofs.write(reinterpret_cast(eventData), + eventDataSize); + + if (!ofs) + { + lg2::error( + "Failed to write PLDM event data. PATH={PATH}", + "PATH", filePath.string()); + return; + } + } + catch (const std::exception& e) + { + lg2::error( + "Exception writing PLDM event file. PATH={PATH} ERROR={ERROR}", + "PATH", filePath.string(), + "ERROR", e.what()); + return; + } + + // Use debug to avoid log flooding in production + lg2::debug( + "PLDM event file created. PATH={PATH} TID={TID} EVENTID={EVID} EVENTCLASS={EVCLASS}", + "PATH", filePath.string(), + "TID", static_cast(tid), + "EVID", lg2::hex, static_cast(eventId), + "EVCLASS", lg2::hex, static_cast(eventClass)); +} + int EventManager::handlePlatformEvent( pldm_tid_t tid, uint16_t eventId, uint8_t eventClass, const uint8_t* eventData, size_t eventDataSize) @@ -103,6 +181,9 @@ int EventManager::handlePlatformEvent( terminus->pollEvent = true; terminus->pollEventId = poll_event.event_id; terminus->pollDataTransferHandle = poll_event.data_transfer_handle; + + if (eventId != PLDM_PLATFORM_EVENT_ID_NULL && verbose) + logPldmErrorEvent(tid, eventId, eventClass, eventData, eventDataSize); } return PLDM_SUCCESS; @@ -525,9 +606,25 @@ int EventManager::getNextPartParameters( return PLDM_SUCCESS; } +std::string EventManager::getTerminusName(pldm_tid_t tid) const +{ + auto it = termini.find(tid); + if (it != termini.end()) + { + auto& terminus = it->second; + auto tNameOpt = terminus->getTerminusName(); + if (tNameOpt) + { + return std::string(*tNameOpt); + } + } + // Fallback: Return TID as a string if name isn't available + return std::to_string(tid); +} + void EventManager::callPolledEventHandlers(pldm_tid_t tid, uint8_t eventClass, - uint16_t eventId, - std::vector& eventMessage) + uint8_t formatVersion, uint32_t dataTransferHandle, + uint16_t eventId, std::vector& eventMessage) { try { @@ -541,6 +638,18 @@ void EventManager::callPolledEventHandlers(pldm_tid_t tid, uint8_t eventClass, lg2::error( "Failed to handle platform event msg for terminus {TID}, event {EVENTID} return {RET}", "TID", tid, "EVENTID", eventId, "RET", rc); + } else { + lg2::info("Handle platform event msg for terminus {TID}, event {EVENTID}, size {EVSIZE}", + "TID", tid, "EVENTID", lg2::hex, eventId, "EVSIZE", lg2::hex, eventMessage.size()); + + int fd = pldm::utils::create_mem_fd(eventMessage); + if (fd != -1) { + std::string tName = getTerminusName(tid); + pldm::utils::emitPldmMessagePollEventSignal( + formatVersion, tid, tName, eventClass, dataTransferHandle, + eventId, eventMessage.size(), fd); + close(fd); + } } } } @@ -617,6 +726,7 @@ exec::task EventManager::pollForPlatformEventTask( if (eventHandlers.contains(polledEventClass)) { callPolledEventHandlers(polledEventTid, polledEventClass, + formatVersion, pollDataTransferHandle, polledEventId, eventMessage); } eventMessage.clear(); diff --git a/platform-mc/event_manager.hpp b/platform-mc/event_manager.hpp index 083725d57f..776a06d52f 100644 --- a/platform-mc/event_manager.hpp +++ b/platform-mc/event_manager.hpp @@ -40,7 +40,7 @@ class EventManager virtual ~EventManager() = default; explicit EventManager(TerminusManager& terminusManager, - TerminiMapper& termini) : + TerminiMapper& termini, const bool verbose) : terminusManager(terminusManager), termini(termini) { // Default response handler for PollForPlatFormEventMessage @@ -59,6 +59,7 @@ class EventManager return this->handlePlatformEvent(tid, eventId, PLDM_CPER_EVENT, eventData, eventDataSize); }}); + this->verbose = verbose; }; /** @brief Handle platform event @@ -212,13 +213,39 @@ class EventManager * * @param[in] tid - terminus ID * @param[out] eventClass - Event class + * @param[in] formatVersion - The PLDM version/format of the event data + * @param[in] dataTransferHandle - Handle identifying the specific data block + * in the event transfer * @param[out] eventId - Event ID * @param[in] eventMessage - event data of response message * */ void callPolledEventHandlers(pldm_tid_t tid, uint8_t eventClass, - uint16_t eventId, - std::vector& eventMessage); + uint8_t formatVersion, uint32_t dataTransferHandle, + uint16_t eventId, std::vector& eventMessage); + + /** @brief Log a PLDM error event to the BMC journal or event log. + * + * This function processes incoming PLDM events and creates a corresponding + * error entry. It ensures the event data is parsed according to the specified + * event class. + * + * @param[in] tid - Terminal ID of the source that generated the event + * @param[in] eventId - The unique identifier for the specific event + * @param[in] eventClass - The class of the event (e.g., sensor, effecter) + * @param[in] eventData - Pointer to the raw event data buffer + * @param[in] eventDataSize - Size of the event data buffer in bytes + */ + void logPldmErrorEvent( + pldm_tid_t tid, uint16_t eventId, uint8_t eventClass, + const uint8_t* eventData, size_t eventDataSize); + + /** @brief Get the terminus name for a given TID + * + * @param[in] tid - Terminal ID + * @return The terminus name as a string, or a fallback string if not found + */ + std::string getTerminusName(pldm_tid_t tid) const; /** @brief Reference of terminusManager */ TerminusManager& terminusManager; @@ -231,6 +258,8 @@ class EventManager /** @brief map of PLDM event type of polled event to EventHandlers */ pldm::platform_mc::EventMap eventHandlers; + + bool verbose{false}; }; } // namespace platform_mc } // namespace pldm diff --git a/platform-mc/manager.hpp b/platform-mc/manager.hpp index 01db9a6fd9..0320762172 100644 --- a/platform-mc/manager.hpp +++ b/platform-mc/manager.hpp @@ -36,13 +36,13 @@ class Manager : public pldm::MctpDiscoveryHandlerIntf ~Manager() = default; explicit Manager(sdeventplus::Event& event, RequesterHandler& handler, - pldm::InstanceIdDb& instanceIdDb) : + pldm::InstanceIdDb& instanceIdDb, const bool verbose) : terminusManager(event, handler, instanceIdDb, termini, this, pldm::BmcMctpEid), platformManager(terminusManager, termini, this), sensorManager(event, terminusManager, termini, this), - eventManager(terminusManager, termini) - {} + eventManager(terminusManager, termini, verbose) + {this->verbose = verbose;} /** @brief Helper function to do the actions before discovering terminus * @@ -287,6 +287,8 @@ class Manager : public pldm::MctpDiscoveryHandlerIntf /** @brief map of PLDM event type to EventHandlers */ PollHandlers pollHandlers; + + bool verbose{false}; }; } // namespace platform_mc } // namespace pldm diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp index 9fa1ce3f28..d0092ab180 100644 --- a/pldmd/pldmd.cpp +++ b/pldmd/pldmd.cpp @@ -220,7 +220,7 @@ int main(int argc, char** argv) DBusHandler dbusHandler; std::unique_ptr platformManager = - std::make_unique(event, reqHandler, instanceIdDb); + std::make_unique(event, reqHandler, instanceIdDb, verbose); #ifdef LIBPLDMRESPONDER using namespace pldm::state_sensor;