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