platform-mc: Support pollForPlatFormEventMessage

Supports polling all events synchronously when the terminus sends
`pldmMessagePollEvent` with the event id. BMC will use the received
event id as input for `pollForPlatformEventMessage` command to retrieve
the event data.

Change-Id: If01f63f30d3f57f8423c863ec776e83dda8e3042
Signed-off-by: Dung Cao <dung@os.amperecomputing.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
diff --git a/platform-mc/event_manager.cpp b/platform-mc/event_manager.cpp
index 8212598..a784cdd 100644
--- a/platform-mc/event_manager.cpp
+++ b/platform-mc/event_manager.cpp
@@ -1,5 +1,6 @@
 #include "event_manager.hpp"
 
+#include "libpldm/platform.h"
 #include "libpldm/utils.h"
 
 #include "terminus_manager.hpp"
@@ -72,6 +73,34 @@
         return processCperEvent(tid, eventId, eventData, eventDataSize);
     }
 
+    /* EventClass pldmMessagePollEvent `Table 11 - PLDM Event Types` DSP0248 */
+    if (eventClass == PLDM_MESSAGE_POLL_EVENT)
+    {
+        lg2::info("Received pldmMessagePollEvent for terminus {TID}", "TID",
+                  tid);
+        pldm_message_poll_event poll_event{};
+        auto rc = decode_pldm_message_poll_event_data(eventData, eventDataSize,
+                                                      &poll_event);
+        if (rc)
+        {
+            lg2::error(
+                "Failed to decode PldmMessagePollEvent event, error {RC} ",
+                "RC", rc);
+            return rc;
+        }
+
+        auto it = termini.find(tid);
+        if (it != termini.end())
+        {
+            auto& terminus = it->second; // Reference for clarity
+            terminus->pollEvent = true;
+            terminus->pollEventId = poll_event.event_id;
+            terminus->pollDataTransferHandle = poll_event.data_transfer_handle;
+        }
+
+        return PLDM_SUCCESS;
+    }
+
     lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE", eventClass);
 
     return PLDM_ERROR;
@@ -456,5 +485,208 @@
     return PLDM_SUCCESS;
 }
 
+int EventManager::getNextPartParameters(
+    uint16_t eventId, std::vector<uint8_t> eventMessage, uint8_t transferFlag,
+    uint32_t eventDataIntegrityChecksum, uint32_t nextDataTransferHandle,
+    uint8_t* transferOperationFlag, uint32_t* dataTransferHandle,
+    uint32_t* eventIdToAcknowledge)
+{
+    if (transferFlag != PLDM_PLATFORM_TRANSFER_START_AND_END &&
+        transferFlag != PLDM_PLATFORM_TRANSFER_END)
+    {
+        *transferOperationFlag = PLDM_GET_NEXTPART;
+        *dataTransferHandle = nextDataTransferHandle;
+        *eventIdToAcknowledge = PLDM_PLATFORM_EVENT_ID_FRAGMENT;
+        return PLDM_SUCCESS;
+    }
+
+    if (transferFlag == PLDM_PLATFORM_TRANSFER_END)
+    {
+        if (eventDataIntegrityChecksum !=
+            crc32(eventMessage.data(), eventMessage.size()))
+        {
+            lg2::error("pollForPlatformEventMessage invalid checksum.");
+            return PLDM_ERROR_INVALID_DATA;
+        }
+    }
+
+    /* End of one event. Set request transfer flag to ACK */
+    *transferOperationFlag = PLDM_ACKNOWLEDGEMENT_ONLY;
+    *dataTransferHandle = 0;
+    *eventIdToAcknowledge = eventId;
+
+    return PLDM_SUCCESS;
+}
+
+exec::task<int> EventManager::pollForPlatformEventTask(
+    pldm_tid_t tid, uint32_t pollDataTransferHandle)
+{
+    uint8_t rc = 0;
+    // Set once, doesn't need resetting
+    uint8_t transferOperationFlag = PLDM_GET_FIRSTPART;
+    uint32_t dataTransferHandle = pollDataTransferHandle;
+    uint32_t eventIdToAcknowledge = PLDM_PLATFORM_EVENT_ID_NULL;
+    uint8_t formatVersion = 0x1; // Constant, no need to reset
+    uint16_t eventId = PLDM_PLATFORM_EVENT_ID_ACK;
+    uint16_t polledEventId = PLDM_PLATFORM_EVENT_ID_NONE;
+    pldm_tid_t polledEventTid = 0;
+    uint8_t polledEventClass = 0;
+
+    std::vector<uint8_t> eventMessage{};
+
+    // Reset and mark terminus as available
+    updateAvailableState(tid, true);
+
+    while (eventId != PLDM_PLATFORM_EVENT_ID_NONE)
+    {
+        uint8_t completionCode = 0;
+        pldm_tid_t eventTid = PLDM_PLATFORM_EVENT_ID_NONE;
+        eventId = PLDM_PLATFORM_EVENT_ID_NONE;
+        uint32_t nextDataTransferHandle = 0;
+        uint8_t transferFlag = 0;
+        uint8_t eventClass = 0;
+        uint32_t eventDataSize = 0;
+        uint8_t* eventData = nullptr;
+        uint32_t eventDataIntegrityChecksum = 0;
+
+        /* Stop event polling */
+        if (!getAvailableState(tid))
+        {
+            lg2::info(
+                "Terminus ID {TID} is not available for PLDM request from {NOW}.",
+                "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
+            co_await stdexec::just_stopped();
+        }
+
+        rc = co_await pollForPlatformEventMessage(
+            tid, formatVersion, transferOperationFlag, dataTransferHandle,
+            eventIdToAcknowledge, completionCode, eventTid, eventId,
+            nextDataTransferHandle, transferFlag, eventClass, eventDataSize,
+            eventData, eventDataIntegrityChecksum);
+        if (rc || completionCode != PLDM_SUCCESS)
+        {
+            lg2::error(
+                "Failed to pollForPlatformEventMessage for terminus {TID}, event {EVENTID}, error {RC}, complete code {CC}",
+                "TID", tid, "EVENTID", eventId, "RC", rc, "CC", completionCode);
+            co_return rc;
+        }
+
+        if (eventDataSize > 0)
+        {
+            eventMessage.insert(eventMessage.end(), eventData,
+                                eventData + eventDataSize);
+        }
+
+        if (transferOperationFlag == PLDM_ACKNOWLEDGEMENT_ONLY)
+        {
+            /* Handle the polled event after finish ACK it */
+            if (eventHandlers.contains(polledEventClass))
+            {
+                eventHandlers.at(
+                    polledEventClass)(polledEventTid, polledEventId,
+                                      eventMessage.data(), eventMessage.size());
+            }
+            eventMessage.clear();
+
+            if (eventId == PLDM_PLATFORM_EVENT_ID_ACK)
+            {
+                transferOperationFlag = PLDM_GET_FIRSTPART;
+                dataTransferHandle = 0;
+                eventIdToAcknowledge = PLDM_PLATFORM_EVENT_ID_NULL;
+            }
+        }
+        else
+        {
+            auto ret = getNextPartParameters(
+                eventId, eventMessage, transferFlag, eventDataIntegrityChecksum,
+                nextDataTransferHandle, &transferOperationFlag,
+                &dataTransferHandle, &eventIdToAcknowledge);
+            if (ret)
+            {
+                lg2::error(
+                    "Failed to process data of pollForPlatformEventMessage for terminus {TID}, event {EVENTID} return {RET}",
+                    "TID", tid, "EVENTID", eventId, "RET", ret);
+                co_return PLDM_ERROR_INVALID_DATA;
+            }
+
+            /* Store the polled event INFO to handle after ACK */
+            if ((transferFlag == PLDM_PLATFORM_TRANSFER_START_AND_END) ||
+                (transferFlag == PLDM_PLATFORM_TRANSFER_END))
+            {
+                polledEventTid = eventTid;
+                polledEventId = eventId;
+                polledEventClass = eventClass;
+            }
+        }
+    }
+
+    co_return PLDM_SUCCESS;
+}
+
+exec::task<int> EventManager::pollForPlatformEventMessage(
+    pldm_tid_t tid, uint8_t formatVersion, uint8_t transferOperationFlag,
+    uint32_t dataTransferHandle, uint16_t eventIdToAcknowledge,
+    uint8_t& completionCode, uint8_t& eventTid, uint16_t& eventId,
+    uint32_t& nextDataTransferHandle, uint8_t& transferFlag,
+    uint8_t& eventClass, uint32_t& eventDataSize, uint8_t*& eventData,
+    uint32_t& eventDataIntegrityChecksum)
+{
+    Request request(
+        sizeof(pldm_msg_hdr) + PLDM_POLL_FOR_PLATFORM_EVENT_MESSAGE_REQ_BYTES);
+    auto requestMsg = new (request.data()) pldm_msg;
+    auto rc = encode_poll_for_platform_event_message_req(
+        0, formatVersion, transferOperationFlag, dataTransferHandle,
+        eventIdToAcknowledge, requestMsg, request.size());
+    if (rc)
+    {
+        lg2::error(
+            "Failed to encode request PollForPlatformEventMessage for terminus ID {TID}, error {RC} ",
+            "TID", tid, "RC", rc);
+        co_return rc;
+    }
+
+    /* Stop event polling */
+    if (!getAvailableState(tid))
+    {
+        lg2::info(
+            "Terminus ID {TID} is not available for PLDM request from {NOW}.",
+            "TID", tid, "NOW", pldm::utils::getCurrentSystemTime());
+        co_await stdexec::just_stopped();
+    }
+
+    const pldm_msg* responseMsg = nullptr;
+    size_t responseLen = 0;
+    rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
+                                                  &responseLen);
+    if (rc)
+    {
+        lg2::error(
+            "Failed to send PollForPlatformEventMessage message for terminus {TID}, error {RC}",
+            "TID", tid, "RC", rc);
+        co_return rc;
+    }
+
+    rc = decode_poll_for_platform_event_message_resp(
+        responseMsg, responseLen, &completionCode, &eventTid, &eventId,
+        &nextDataTransferHandle, &transferFlag, &eventClass, &eventDataSize,
+        (void**)&eventData, &eventDataIntegrityChecksum);
+    if (rc)
+    {
+        lg2::error(
+            "Failed to decode response PollForPlatformEventMessage for terminus ID {TID}, error {RC} ",
+            "TID", tid, "RC", rc);
+        co_return rc;
+    }
+    if (completionCode != PLDM_SUCCESS)
+    {
+        lg2::error(
+            "Error : PollForPlatformEventMessage for terminus ID {TID}, complete code {CC}.",
+            "TID", tid, "CC", completionCode);
+        co_return rc;
+    }
+
+    co_return completionCode;
+}
+
 } // namespace platform_mc
 } // namespace pldm
diff --git a/platform-mc/event_manager.hpp b/platform-mc/event_manager.hpp
index c654eb7..14b6c8e 100644
--- a/platform-mc/event_manager.hpp
+++ b/platform-mc/event_manager.hpp
@@ -15,6 +15,12 @@
 namespace platform_mc
 {
 
+using EventType = uint8_t;
+using HandlerFunc =
+    std::function<int(pldm_tid_t tid, uint16_t eventId,
+                      const uint8_t* eventData, size_t eventDataSize)>;
+using EventMap = std::map<EventType, HandlerFunc>;
+
 /**
  * @brief EventManager
  *
@@ -35,7 +41,25 @@
 
     explicit EventManager(TerminusManager& terminusManager,
                           TerminiMapper& termini) :
-        terminusManager(terminusManager), termini(termini) {};
+        terminusManager(terminusManager), termini(termini)
+    {
+        // Default response handler for PollForPlatFormEventMessage
+        registerPolledEventHandler(
+            PLDM_MESSAGE_POLL_EVENT,
+            [this](pldm_tid_t tid, uint16_t eventId, const uint8_t* eventData,
+                   size_t eventDataSize) {
+                return this->handlePlatformEvent(tid, eventId,
+                                                 PLDM_MESSAGE_POLL_EVENT,
+                                                 eventData, eventDataSize);
+            });
+        registerPolledEventHandler(
+            PLDM_CPER_EVENT,
+            [this](pldm_tid_t tid, uint16_t eventId, const uint8_t* eventData,
+                   size_t eventDataSize) {
+                return this->handlePlatformEvent(tid, eventId, PLDM_CPER_EVENT,
+                                                 eventData, eventDataSize);
+            });
+    };
 
     /** @brief Handle platform event
      *
@@ -73,6 +97,24 @@
         return availableState[tid];
     };
 
+    /** @brief A Coroutine to poll all events from terminus
+     *
+     *  @param[in] tid - the destination TID
+     *  @param[in] pollDataTransferHandle - the dataTransferHandle from
+     *             pldmMessagePollEvent event
+     *  @return coroutine return_value - PLDM completion code
+     */
+    exec::task<int> pollForPlatformEventTask(pldm_tid_t tid,
+                                             uint32_t pollDataTransferHandle);
+
+    /** @brief Register response handler for the polled events from
+     *         PollForPlatFormEventMessage
+     */
+    void registerPolledEventHandler(uint8_t eventClass, HandlerFunc function)
+    {
+        eventHandlers.insert_or_assign(eventClass, std::move(function));
+    }
+
   protected:
     /** @brief Helper method to process the PLDM Numeric sensor event class
      *
@@ -96,8 +138,9 @@
      *
      *  @return PLDM completion code
      */
-    int processCperEvent(pldm_tid_t tid, uint16_t eventId,
-                         const uint8_t* eventData, const size_t eventDataSize);
+    virtual int processCperEvent(pldm_tid_t tid, uint16_t eventId,
+                                 const uint8_t* eventData,
+                                 const size_t eventDataSize);
 
     /** @brief Helper method to create CPER dump log
      *
@@ -111,6 +154,53 @@
                             const std::string& dataPath,
                             const std::string& typeName);
 
+    /** @brief Send pollForPlatformEventMessage and return response
+     *
+     *  @param[in] tid - Destination TID
+     *  @param[in] formatVersion - format Version
+     *  @param[in] transferOpFlag - Transfer Operation Flag
+     *  @param[in] dataTransferHandle - Data transfer handle
+     *  @param[in] eventIdToAcknowledge - Event ID
+     *  @param[out] completionCode - the complete code of response message
+     *  @param[out] eventTid - Event terminus ID
+     *  @param[out] eventId - Event ID
+     *  @param[out] nextDataTransferHandle - Next handle to get next data part
+     *  @param[out] transferFlag - transfer Flag of response data
+     *  @param[out] eventClass - event class
+     *  @param[out] eventDataSize - data size of event response message
+     *  @param[out] eventData - event data of response message
+     *  @param[out] eventDataIntegrityChecksum - check sum of final event
+     *
+     *  @return coroutine return_value - PLDM completion code
+     */
+    exec::task<int> pollForPlatformEventMessage(
+        pldm_tid_t tid, uint8_t formatVersion, uint8_t transferOperationFlag,
+        uint32_t dataTransferHandle, uint16_t eventIdToAcknowledge,
+        uint8_t& completionCode, uint8_t& eventTid, uint16_t& eventId,
+        uint32_t& nextDataTransferHandle, uint8_t& transferFlag,
+        uint8_t& eventClass, uint32_t& eventDataSize, uint8_t*& eventData,
+        uint32_t& eventDataIntegrityChecksum);
+
+    /** @brief Get the parameters for next pollForPlatformEventMessage to get
+     *         the remaining part of event if has
+     *
+     *  @param[in] eventId - Event ID
+     *  @param[in] eventMessage - event data of response message
+     *  @param[in] transferFlag - transfer Flag of response data
+     *  @param[in] eventDataIntegrityChecksum - check sum of final event
+     *  @param[in] nextDataTransferHandle - Next handle to get next data part
+     *  @param[out] transferOperationFlag - transfer Flag of next request data
+     *  @param[out] dataTransferHandle - Data transfer handle
+     *  @param[out] eventIdToAcknowledge - Event ID
+     *
+     *  @return return_value - PLDM completion code
+     */
+    int getNextPartParameters(
+        uint16_t eventId, std::vector<uint8_t> eventMessage,
+        uint8_t transferFlag, uint32_t eventDataIntegrityChecksum,
+        uint32_t nextDataTransferHandle, uint8_t* transferOperationFlag,
+        uint32_t* dataTransferHandle, uint32_t* eventIdToAcknowledge);
+
     /** @brief Reference of terminusManager */
     TerminusManager& terminusManager;
 
@@ -119,6 +209,9 @@
 
     /** @brief Available state for pldm request of terminus */
     std::unordered_map<pldm_tid_t, Availability> availableState;
+
+    /** @brief map of PLDM event type of polled event to EventHandlers */
+    pldm::platform_mc::EventMap eventHandlers;
 };
 } // namespace platform_mc
 } // namespace pldm
diff --git a/platform-mc/manager.cpp b/platform-mc/manager.cpp
index 7e79a29..db5175a 100644
--- a/platform-mc/manager.cpp
+++ b/platform-mc/manager.cpp
@@ -31,5 +31,19 @@
     co_return rc;
 }
 
+exec::task<int> Manager::pollForPlatformEvent(
+    pldm_tid_t tid, uint16_t /* pollEventId */, uint32_t pollDataTransferHandle)
+{
+    auto it = termini.find(tid);
+    if (it != termini.end())
+    {
+        auto& terminus = it->second;
+        co_await eventManager.pollForPlatformEventTask(tid,
+                                                       pollDataTransferHandle);
+        terminus->pollEvent = false;
+    }
+    co_return PLDM_SUCCESS;
+}
+
 } // namespace platform_mc
 } // namespace pldm
diff --git a/platform-mc/manager.hpp b/platform-mc/manager.hpp
index c499f88..0cb0178 100644
--- a/platform-mc/manager.hpp
+++ b/platform-mc/manager.hpp
@@ -147,6 +147,40 @@
         return PLDM_SUCCESS;
     }
 
+    /** @brief PLDM POLL event handler function
+     *
+     *  @param[in] request - Event message
+     *  @param[in] payloadLength - Event message payload size
+     *  @param[in] tid - Terminus ID
+     *  @param[in] eventDataOffset - Event data offset
+     *
+     *  @return PLDM error code: PLDM_SUCCESS when there is no error in handling
+     *          the event
+     */
+    int handlePldmMessagePollEvent(
+        const pldm_msg* request, size_t payloadLength,
+        uint8_t /* formatVersion */, uint8_t tid, size_t eventDataOffset)
+    {
+        auto eventData = reinterpret_cast<const uint8_t*>(request->payload) +
+                         eventDataOffset;
+        auto eventDataSize = payloadLength - eventDataOffset;
+        eventManager.handlePlatformEvent(tid, PLDM_PLATFORM_EVENT_ID_NULL,
+                                         PLDM_MESSAGE_POLL_EVENT, eventData,
+                                         eventDataSize);
+        return PLDM_SUCCESS;
+    }
+
+    /** @brief The function to trigger the event polling
+     *
+     *  @param[in] tid - Terminus ID
+     *  @param[in] pollEventId - The source eventID from pldmMessagePollEvent
+     *  @param[in] pollDataTransferHandle - The dataTransferHandle from
+     *             pldmMessagePollEvent event
+     *  @return coroutine return_value - PLDM completion code
+     */
+    exec::task<int> pollForPlatformEvent(pldm_tid_t tid, uint16_t pollEventId,
+                                         uint32_t pollDataTransferHandle);
+
   private:
     /** @brief List of discovered termini */
     TerminiMapper termini{};
diff --git a/platform-mc/sensor_manager.cpp b/platform-mc/sensor_manager.cpp
index da829b0..2ffc679 100644
--- a/platform-mc/sensor_manager.cpp
+++ b/platform-mc/sensor_manager.cpp
@@ -181,6 +181,14 @@
             co_return PLDM_SUCCESS;
         }
 
+        auto& terminus = termini[tid];
+
+        if (manager && terminus->pollEvent)
+        {
+            co_await manager->pollForPlatformEvent(
+                tid, terminus->pollEventId, terminus->pollDataTransferHandle);
+        }
+
         sd_event_now(event.get(), CLOCK_MONOTONIC, &t1);
         auto toBeUpdated = roundRobinSensors[tid].size();
         while (((t1 - t0) < pollingTimeInUsec) && (toBeUpdated > 0))
diff --git a/platform-mc/terminus.cpp b/platform-mc/terminus.cpp
index b5a1e8c..70d1112 100644
--- a/platform-mc/terminus.cpp
+++ b/platform-mc/terminus.cpp
@@ -15,7 +15,8 @@
 
 Terminus::Terminus(pldm_tid_t tid, uint64_t supportedTypes) :
     initialized(false), maxBufferSize(PLDM_PLATFORM_EVENT_MSG_MAX_BUFFER_SIZE),
-    synchronyConfigurationSupported(0), tid(tid), supportedTypes(supportedTypes)
+    synchronyConfigurationSupported(0), pollEvent(false), tid(tid),
+    supportedTypes(supportedTypes)
 {}
 
 bool Terminus::doesSupportType(uint8_t type)
diff --git a/platform-mc/terminus.hpp b/platform-mc/terminus.hpp
index 464897f..1986da0 100644
--- a/platform-mc/terminus.hpp
+++ b/platform-mc/terminus.hpp
@@ -148,6 +148,21 @@
     /** @brief A list of numericSensors */
     std::vector<std::shared_ptr<NumericSensor>> numericSensors{};
 
+    /** @brief The flag indicates that the terminus FIFO contains a large
+     *         message that will require a multipart transfer via the
+     *         PollForPlatformEvent command
+     */
+    bool pollEvent;
+
+    /** @brief The sensor id is used in pollForPlatformMessage command */
+    uint16_t pollEventId;
+
+    /** @brief The dataTransferHandle from `pldmMessagePollEvent` event and will
+     *         be used as `dataTransferHandle` for pollForPlatformMessage
+     *         command.
+     */
+    uint32_t pollDataTransferHandle;
+
     /** @brief Get Sensor Auxiliary Names by sensorID
      *
      *  @param[in] id - sensor ID
diff --git a/platform-mc/test/event_manager_test.cpp b/platform-mc/test/event_manager_test.cpp
index 2a976e0..5fb53e5 100644
--- a/platform-mc/test/event_manager_test.cpp
+++ b/platform-mc/test/event_manager_test.cpp
@@ -388,3 +388,106 @@
     eventManager.updateAvailableState(2, false);
     EXPECT_EQ(false, eventManager.getAvailableState(tid));
 }
+
+TEST_F(EventManagerTest, pollForPlatformEventTaskMultipartTransferTest)
+{
+    // Add terminus
+    auto mappedTid = terminusManager.mapTid(pldm::MctpInfo(10, "", "", 1));
+    auto tid = mappedTid.value();
+    termini[tid] = std::make_shared<pldm::platform_mc::Terminus>(
+        tid, 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+    auto terminus = termini[tid];
+
+    // queue pollForPlatformEventMessage first part response
+    const size_t pollForPlatformEventMessage1Len = 22;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + pollForPlatformEventMessage1Len>
+        pollForPlatformEventMessage1Resp{
+            0x0,
+            0x02,
+            0x0d,
+            PLDM_SUCCESS,
+            tid, // TID
+            0x1,
+            0x0, // eventID
+            0x1,
+            0x0,
+            0x0,
+            0x0,                          // nextDataTransferHandle
+            PLDM_PLATFORM_TRANSFER_START, // transferFlag = start
+            PLDM_CPER_EVENT,              // eventClass
+            8,
+            0,
+            0,
+            0,    // eventDataSize
+            0x01, // CPER event formatVersion= 0x01
+            1,    // formatType = single CPER section(0x01)
+            10,
+            0,    // eventDataLength = 10
+            1,
+            2,
+            3,
+            4 // eventData first part
+        };
+    auto rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(pollForPlatformEventMessage1Resp.data()),
+        sizeof(pollForPlatformEventMessage1Resp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue pollForPlatformEventMessage last part response
+    const size_t pollForPlatformEventMessage2Len = 24;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + pollForPlatformEventMessage2Len>
+        pollForPlatformEventMessage2Resp{
+            0x0,
+            0x02,
+            0x0d,
+            PLDM_SUCCESS,
+            tid, // TID
+            0x1,
+            0x0, // eventID
+            0x2,
+            0x0,
+            0x0,
+            0x0,                        // nextDataTransferHandle
+            PLDM_PLATFORM_TRANSFER_END, // transferFlag = end
+            PLDM_CPER_EVENT,            // eventClass
+            6,
+            0,
+            0,
+            0, // eventDataSize
+            5,
+            6,
+            7,
+            8,
+            9,
+            0, // eventData last part
+            0x46,
+            0x7f,
+            0x6a,
+            0x5d // crc32
+        };
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(pollForPlatformEventMessage2Resp.data()),
+        sizeof(pollForPlatformEventMessage2Resp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    // queue pollForPlatformEventMessage Ack response
+    const size_t pollForPlatformEventMessage3Len = 4;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + pollForPlatformEventMessage3Len>
+        pollForPlatformEventMessage3Resp{
+            0x0, 0x02, 0x0d, PLDM_SUCCESS,
+            tid,     // TID
+            0x0, 0x0 // eventID
+        };
+    rc = terminusManager.enqueueResponse(
+        reinterpret_cast<pldm_msg*>(pollForPlatformEventMessage3Resp.data()),
+        sizeof(pollForPlatformEventMessage3Resp));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    EXPECT_CALL(eventManager, processCperEvent(_, _, _, _))
+        .Times(1)
+        .WillRepeatedly(Return(1));
+
+    // start task to poll event from terminus
+    // should finish immediately
+    stdexec::sync_wait(eventManager.pollForPlatformEventTask(tid, 0x0000));
+}
diff --git a/platform-mc/test/mock_event_manager.hpp b/platform-mc/test/mock_event_manager.hpp
index 116e7f1..8e2349e 100644
--- a/platform-mc/test/mock_event_manager.hpp
+++ b/platform-mc/test/mock_event_manager.hpp
@@ -14,6 +14,11 @@
   public:
     MockEventManager(TerminusManager& terminusManager, TerminiMapper& termini) :
         EventManager(terminusManager, termini) {};
+
+    MOCK_METHOD(int, processCperEvent,
+                (pldm_tid_t tid, uint16_t eventId, const uint8_t* eventData,
+                 size_t eventDataSize),
+                (override));
 };
 
 } // namespace platform_mc
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index fc2b08d..e9ff90e 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -282,6 +282,13 @@
              return platformManager->handleCperEvent(
                  request, payloadLength, formatVersion, tid, eventDataOffset);
          }}},
+        {PLDM_MESSAGE_POLL_EVENT,
+         {[&platformManager](const pldm_msg* request, size_t payloadLength,
+                             uint8_t formatVersion, uint8_t tid,
+                             size_t eventDataOffset) {
+             return platformManager->handlePldmMessagePollEvent(
+                 request, payloadLength, formatVersion, tid, eventDataOffset);
+         }}},
         {PLDM_SENSOR_EVENT,
          {[&platformManager](const pldm_msg* request, size_t payloadLength,
                              uint8_t formatVersion, uint8_t tid,