platform: Add decode_pldm_cper_event_data() API

Support decoder for `cperEvent` event class as table `Table 27 -
CPEREvent class eventData format` in DSP0248 V1.3.0.

Change-Id: I6165980e0570bbb21158af9e6adee15894b3bf3a
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 172f604..9665996 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,7 @@
 5. pdr: Add pldm_pdr_get_terminus_handle() API
 6. pdr: Add related decode_entity_auxiliary_names_pdr() APIs
 7. fw_update: Add encode req & decode resp for get_downstream_fw_params
+8. platform: Add decode_pldm_platform_cper_event_data() API
 
 ### Changed
 
diff --git a/include/libpldm/platform.h b/include/libpldm/platform.h
index 159e514..589970f 100644
--- a/include/libpldm/platform.h
+++ b/include/libpldm/platform.h
@@ -13,6 +13,7 @@
 #include <uchar.h>
 
 #include <libpldm/base.h>
+#include <libpldm/compiler.h>
 #include <libpldm/pdr.h>
 #include <libpldm/pldm_types.h>
 
@@ -74,6 +75,8 @@
 
 /* Minimum length of sensor event data */
 #define PLDM_MSG_POLL_EVENT_LENGTH 7
+/* Minimum data length of CPER event type */
+#define PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH 4
 
 /* Minimum length of sensor event data */
 #define PLDM_SENSOR_EVENT_DATA_MIN_LENGTH			 5
@@ -280,7 +283,8 @@
 	PLDM_REDFISH_MESSAGE_EVENT = 0x03,
 	PLDM_PDR_REPOSITORY_CHG_EVENT = 0x04,
 	PLDM_MESSAGE_POLL_EVENT = 0x05,
-	PLDM_HEARTBEAT_TIMER_ELAPSED_EVENT = 0x06
+	PLDM_HEARTBEAT_TIMER_ELAPSED_EVENT = 0x06,
+	PLDM_CPER_EVENT = 0x07
 };
 
 /** @brief PLDM sensorEventClass states
@@ -1143,6 +1147,25 @@
 	uint32_t data_transfer_handle;
 };
 
+/** @struct pldm_platform_cper_event
+ *
+ *  structure representing cperEvent fields
+ */
+struct pldm_platform_cper_event {
+	uint8_t format_version;
+	uint8_t format_type;
+	uint16_t event_data_length;
+#ifndef __cplusplus
+	uint8_t event_data[] LIBPLDM_CC_COUNTED_BY(event_data_length);
+#endif
+};
+
+/** @brief PLDM CPER event format type */
+enum pldm_platform_cper_event_format {
+	PLDM_PLATFORM_CPER_EVENT_WITH_HEADER = 0x00,
+	PLDM_PLATFORM_CPER_EVENT_WITHOUT_HEADER = 0x01
+};
+
 /** @struct pldm_platform_event_message_req
  *
  *  structure representing PlatformEventMessage command request data
@@ -2428,6 +2451,26 @@
  */
 int decode_pldm_entity_auxiliary_names_pdr_index(
 	struct pldm_entity_auxiliary_names_pdr *pdr_value);
+
+/** @brief Decode PLDM Platform CPER event data type
+ *
+ *  @param[in] event_data - event data from the response message
+ *  @param[in] event_data_length - length of the event data
+ *  @param[out] cper_event - the decoded pldm_platform_cper_event struct
+ *  @param[in] cper_event_length - the length of cper event
+ *  @return error code
+ */
+int decode_pldm_platform_cper_event_data(
+	const void *event_data, size_t event_data_length,
+	struct pldm_platform_cper_event *cper_event, size_t cper_event_length);
+
+/** @brief Helper function to response CPER event event data
+ *
+ *  @param[in] cper_event - the decoded pldm_platform_cper_event struct
+ *  @return cper event event data array pointer
+ */
+uint8_t *
+pldm_platform_cper_event_event_data(struct pldm_platform_cper_event *event);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/dsp/platform.c b/src/dsp/platform.c
index f826f3c..9439e12 100644
--- a/src/dsp/platform.c
+++ b/src/dsp/platform.c
@@ -2856,3 +2856,60 @@
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_TESTING
+int decode_pldm_platform_cper_event_data(
+	const void *event_data, size_t event_data_length,
+	struct pldm_platform_cper_event *cper_event, size_t cper_event_length)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (!cper_event || !event_data) {
+		return -EINVAL;
+	}
+
+	if (cper_event_length < sizeof(*cper_event)) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf, PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH,
+				    event_data, event_data_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, cper_event->format_version);
+	rc = pldm_msgbuf_extract(buf, cper_event->format_type);
+	if (rc) {
+		return rc;
+	}
+	if (cper_event->format_type != PLDM_PLATFORM_CPER_EVENT_WITH_HEADER &&
+	    cper_event->format_type !=
+		    PLDM_PLATFORM_CPER_EVENT_WITHOUT_HEADER) {
+		return -EPROTO;
+	}
+
+	rc = pldm_msgbuf_extract(buf, cper_event->event_data_length);
+	if (rc) {
+		return rc;
+	}
+
+	if (cper_event->event_data_length >
+	    (cper_event_length - sizeof(*cper_event))) {
+		return -EOVERFLOW;
+	}
+
+	pldm_msgbuf_extract_array_uint8(buf, cper_event->event_data,
+					cper_event->event_data_length);
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+uint8_t *
+pldm_platform_cper_event_event_data(struct pldm_platform_cper_event *event)
+{
+	return event->event_data;
+}
diff --git a/tests/dsp/platform.cpp b/tests/dsp/platform.cpp
index f5ae4fd..dfc5506 100644
--- a/tests/dsp/platform.cpp
+++ b/tests/dsp/platform.cpp
@@ -5190,3 +5190,125 @@
     EXPECT_EQ(-EBADMSG, rc);
     free(decodedPdr);
 }
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PlatformEventMessage, testGoodCperEventDataDecodeRequest)
+{
+    constexpr const size_t eventDataSize = 4;
+    constexpr const size_t eventSize =
+        PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH + eventDataSize;
+    std::array<uint8_t, eventSize> eventData{
+        0x1,                   // format version
+        0x0,                   // format type
+        0x4,  0x0,             // event data length
+        0x44, 0x33, 0x22, 0x11 // data
+    };
+
+    uint8_t expectedFormatVersion = 1;
+    uint8_t expectedFormatType = 0;
+    uint16_t expectedEventDataLength = 4;
+    uint8_t expectCperEventData[] = {0x44, 0x33, 0x22, 0x11};
+
+    size_t cperEventSize =
+        sizeof(struct pldm_platform_cper_event) + eventDataSize;
+    auto cper_event = reinterpret_cast<struct pldm_platform_cper_event*>(
+        malloc(cperEventSize));
+
+    auto rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<const void*>(eventData.data()), eventData.size(),
+        cper_event, cperEventSize);
+
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(cper_event->format_version, expectedFormatVersion);
+    EXPECT_EQ(cper_event->format_type, expectedFormatType);
+    EXPECT_EQ(cper_event->event_data_length, expectedEventDataLength);
+
+    auto cperEventData = pldm_platform_cper_event_event_data(cper_event);
+    EXPECT_NE(cperEventData, nullptr);
+    if (cperEventData)
+    {
+        EXPECT_EQ(0, memcmp(expectCperEventData, cperEventData,
+                            expectedEventDataLength));
+    }
+
+    free(cper_event);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PlatformEventMessage, testBadCperEventDataDecodeRequest)
+{
+
+    constexpr const size_t eventDataSize = 4;
+    constexpr const size_t eventSize =
+        PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH + eventDataSize;
+    std::array<uint8_t, eventSize> eventData{
+        0x1,                   // format version
+        0x0,                   // format type
+        0x4,  0x0,             // event data length
+        0x44, 0x33, 0x22, 0x11 // data
+    };
+
+    size_t cperEventSize =
+        sizeof(struct pldm_platform_cper_event) + eventDataSize;
+    auto cperEvent = reinterpret_cast<struct pldm_platform_cper_event*>(
+        malloc(cperEventSize));
+
+    auto rc = decode_pldm_platform_cper_event_data(NULL, eventData.size(),
+                                                   cperEvent, cperEventSize);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<const void*>(eventData.data()), eventData.size(), NULL,
+        cperEventSize);
+    EXPECT_EQ(rc, -EINVAL);
+
+#ifdef NDEBUG
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<uint8_t*>(eventData.data()), eventData.size() - 1,
+        cperEvent, cperEventSize);
+    EXPECT_EQ(rc, -EBADMSG);
+#else
+    EXPECT_DEATH(decode_pldm_platform_cper_event_data(
+                     reinterpret_cast<uint8_t*>(eventData.data()),
+                     eventData.size() - 1, cperEvent, cperEventSize),
+                 "ctx->remaining >= 0");
+#endif
+
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<uint8_t*>(eventData.data()), eventData.size(),
+        cperEvent, cperEventSize - 1);
+    EXPECT_EQ(rc, -EOVERFLOW);
+
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<uint8_t*>(eventData.data()), eventData.size(),
+        cperEvent, cperEventSize + 1);
+    EXPECT_EQ(rc, 0);
+
+    // Invalid CPER Event Format Type
+    eventData[1] = 0x2;
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<const void*>(eventData.data()), eventData.size(),
+        cperEvent, cperEventSize);
+
+    EXPECT_EQ(rc, -EPROTO);
+
+    // Invalid cper event data size
+    eventData[1] = 0x1;
+    eventData[2] = 3;
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<const void*>(eventData.data()), eventData.size(),
+        cperEvent, cperEventSize);
+
+    EXPECT_EQ(rc, -EBADMSG);
+
+    eventData[2] = 5;
+    rc = decode_pldm_platform_cper_event_data(
+        reinterpret_cast<const void*>(eventData.data()), eventData.size(),
+        cperEvent, cperEventSize);
+
+    EXPECT_EQ(rc, -EOVERFLOW);
+
+    free(cperEvent);
+}
+#endif