pdr: Add related decode_entity_auxiliary_names_pdr*() APIs

Add `decode_entity_auxiliary_names_pdr()` to decode the entity auxiliary
names PDR raw data to the data fields as table 95 in DSP0248_1.2.2.
The API will not decode the entity auxiliary names directly - to
expose the language tags and names fields the caller has to
subsequently call `decode_pldm_entity_auxiliary_names_pdr_index()`.
Between the API calls the caller must allocate memory for the
`names` field as an array of `struct pldm_entity_auxiliary_name` with
`name_string_count` elements.

Change-Id: I5fc3989c4c4595546a70c01eb2b6dadcf8c14303
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 776b2d0..a7f88a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 3. pdr: Add pldm_entity_association_pdr_create_new()
 4. platform: Define macros for the responded transferflags
 5. pdr: Add pldm_pdr_get_terminus_handle() API
+6. pdr: Add related decode_entity_auxiliary_names_pdr() APIs
 
 ### Deprecated
 
diff --git a/include/libpldm/platform.h b/include/libpldm/platform.h
index 2384de3..3bff9e6 100644
--- a/include/libpldm/platform.h
+++ b/include/libpldm/platform.h
@@ -6,8 +6,11 @@
 extern "C" {
 #endif
 
+#include <assert.h>
+#include <stdalign.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <uchar.h>
 
 #include <libpldm/base.h>
 #include <libpldm/pdr.h>
@@ -104,6 +107,14 @@
 	 PLDM_PDR_NUMERIC_EFFECTER_PDR_VARIED_EFFECTER_DATA_SIZE_MIN_LENGTH +  \
 	 PLDM_PDR_NUMERIC_EFFECTER_PDR_VARIED_RANGE_FIELD_MIN_LENGTH)
 
+/**
+ * Minimum length of entity auxiliary name effecter PDR includes size of hdr,
+ * entityType, entityInstanceNumber, entityContainerID, sharedNameCount and
+ * nameStringCount in `Table 95 - Entity Auxiliary Names PDR format` of DSP0248
+ * v1.2.2
+ */
+#define PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH 8
+
 #define PLDM_INVALID_EFFECTER_ID 0xffff
 
 /* DSP0248 Table1 PLDM monitoring and control data types */
@@ -117,6 +128,13 @@
 #define PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MIN 1
 #define PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MAX 8
 
+/* Container ID */
+/** @brief Table 2 - Parts of the Entity Identification Information format in
+ *         PLDM Platform and Control spec, DSP0248 v1.2.2. "If this value is
+ *         0x0000, the containing entity is considered to be the overall system"
+ */
+#define PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID 0
+
 enum pldm_effecter_data_size {
 	PLDM_EFFECTER_DATA_SIZE_UINT8,
 	PLDM_EFFECTER_DATA_SIZE_SINT8,
@@ -807,6 +825,44 @@
 	union_range_field_format fatal_low;
 };
 
+typedef char16_t pldm_utf16be;
+
+struct pldm_entity_auxiliary_name {
+	/* name_language_tag type is char which terminator is 0x00*/
+	char *tag;
+	/**
+	 * entity_aux_name type is str_utf16be which terminator is 0x00 0x00.
+	 * The two bytes of one characters is in BE order.
+	 */
+	pldm_utf16be *name;
+};
+
+struct pldm_entity_auxiliary_names_pdr {
+	struct pldm_value_pdr_hdr hdr;
+	pldm_entity container;
+	uint8_t shared_name_count;
+	uint8_t name_string_count;
+	struct pldm_entity_auxiliary_name *names;
+	size_t auxiliary_name_data_size;
+#ifndef __cplusplus
+#if defined __has_attribute
+	/*
+	 * auxiliary_name_data is organised in the fashion of struct-of-arrays, by
+	 * contrast to the approach of an array-of-structs. By Table 95 the entity
+	 * name data is provided in (ASCII, UTF16-BE) pairs, but we rearrange that
+	 * to be an array of UTF16-BE strings followed by an array of ASCII strings,
+	 * with the pairs associated by index, to maintain alignment.
+	 */
+	static_assert(__has_attribute(aligned),
+		      "auxiliary_name_data risks undefined behaviour");
+	char auxiliary_name_data[]
+		__attribute__((aligned(alignof(pldm_utf16be))));
+#else
+#error("__has_attribute() support is required to uphold runtime safety")
+#endif
+#endif
+};
+
 /** @struct state_effecter_possible_states
  *
  *  Structure representing state enums for state effecter
@@ -2322,6 +2378,55 @@
 	const void *pdr_data, size_t pdr_data_length,
 	struct pldm_numeric_effecter_value_pdr *pdr_value);
 
+/** @brief Decode date fields from Entity Auxiliary name PDR
+ *
+ *  @note Use case:
+ *        1. Call `decode_entity_auxiliary_names_pdr()` to decode the Entity
+ *           Auxiliary names PDR raw data to the PDR data fields in
+ *           `struct pldm_entity_auxiliary_names_pdr` equivalent the fields in
+ *           `table 95` of DSP0248_1.2.2. Excepts the entity language tags and
+ *           names.
+ *        2. Use the decoded `name_string_count` and size of
+ *           `struct pldm_entity_auxiliary_name` to allocate memory for the
+ *           `struct pldm_entity_auxiliary_name *names` field in
+ *           `struct pldm_entity_auxiliary_names_pdr`.
+ *        3. Call `decode_pldm_entity_auxiliary_names_pdr_index()` to decode
+ *           `auxiliary_name_data[]` in `struct pldm_entity_auxiliary_names_pdr`
+ *           to the entity language tags and names in
+ *           `struct pldm_entity_auxiliary_name`.
+ *
+ *  @param[in] data - PLDM response message which includes the entity
+ *                        auxiliary name PDRs in DSP0248_1.2.2 table 95.
+ *  @param[in] data_length - Length of response message payload
+ *  @param[out] pdr - Entity auxiliary names pdr struct
+ *  @param[out] pdr_length - Entity auxiliary names pdr struct
+ *
+ *  @return error code
+ */
+int decode_entity_auxiliary_names_pdr(
+	const void *data, size_t data_length,
+	struct pldm_entity_auxiliary_names_pdr *pdr, size_t pdr_length);
+
+/** @brief Decode Entity Auxiliary name data. The API will update the name
+ *         directly to names field in the pdr struct.
+ *
+ *  @pre The API will decode `auxiliary_name_data[]` array in
+ *       `struct pldm_entity_auxiliary_names_pdr pdr` to
+ *       the entity auxiliary language tags and names in
+ *       `struct pldm_entity_auxiliary_name *names` of the same pdr struct.
+ *       Before call the API, the caller has to allocate memory for the `names`
+ *       struct with the number of name(`name_string_count`) in PDR and size of
+ *       `struct pldm_entity_auxiliary_name`.
+ *       The value of `auxiliary_name_data` and `name_string_count` are decoded
+ *       by `decode_entity_auxiliary_names_pdr()` method so the caller has to
+ *       call that API first.
+ *
+ *  @param[out] pdr_value - Entity auxiliary names pdr struct
+ *  @param[in] names_size - Size of names data
+ *  @return error code
+ */
+int decode_pldm_entity_auxiliary_names_pdr_index(
+	struct pldm_entity_auxiliary_names_pdr *pdr_value);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/dsp/platform.c b/src/dsp/platform.c
index c9251dc..3d27802 100644
--- a/src/dsp/platform.c
+++ b/src/dsp/platform.c
@@ -11,6 +11,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <uchar.h>
 
 static int pldm_platform_pdr_hdr_validate(struct pldm_value_pdr_hdr *ctx,
 					  size_t lower, size_t upper)
@@ -2691,3 +2692,176 @@
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_TESTING
+int decode_entity_auxiliary_names_pdr(
+	const void *data, size_t data_length,
+	struct pldm_entity_auxiliary_names_pdr *pdr, size_t pdr_length)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	struct pldm_msgbuf _src;
+	struct pldm_msgbuf *src = &_src;
+	struct pldm_msgbuf _dst;
+	struct pldm_msgbuf *dst = &_dst;
+	size_t names_len = 0;
+	void *names = NULL;
+	int rc;
+	int i;
+
+	/*
+	 * Alignment of auxiliary_name_data is an invariant as we statically assert
+	 * its behaviour in the header.
+	 */
+	assert(!((uintptr_t)pdr->auxiliary_name_data &
+		 (alignof(pldm_utf16be) - 1)));
+
+	/* Reject any lengths that are obviously invalid */
+	if (pdr_length < data_length || pdr_length < sizeof(*pdr)) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH, data,
+		data_length);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract_value_pdr_hdr(buf, &pdr->hdr);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_platform_pdr_hdr_validate(
+		&pdr->hdr, PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH,
+		data_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, pdr->container.entity_type);
+	pldm_msgbuf_extract(buf, pdr->container.entity_instance_num);
+	pldm_msgbuf_extract(buf, pdr->container.entity_container_id);
+	pldm_msgbuf_extract(buf, pdr->shared_name_count);
+	rc = pldm_msgbuf_extract(buf, pdr->name_string_count);
+	if (rc < 0) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_span_remaining(buf, &names, &names_len);
+	if (rc < 0) {
+		return rc;
+	}
+
+	pdr->auxiliary_name_data_size = pdr_length - sizeof(*pdr);
+
+	rc = pldm_msgbuf_init_errno(dst, pdr->auxiliary_name_data_size,
+				    pdr->auxiliary_name_data,
+				    pdr->auxiliary_name_data_size);
+	if (rc < 0) {
+		return rc;
+	}
+
+	/*
+	 * Below we do two passes over the same region. This is to first pack the
+	 * UTF16-BE strings into auxiliary_name_data, followed by the ASCII strings,
+	 * to maintain appropriate alignment.
+	 */
+
+	/* Initialise for the first pass to extract the UTF16-BE name strings */
+	rc = pldm_msgbuf_init_errno(src, names_len, names, names_len);
+	if (rc < 0) {
+		return rc;
+	}
+
+	for (i = 0; i < pdr->name_string_count; i++) {
+		pldm_msgbuf_span_string_ascii(src, NULL, NULL);
+		pldm_msgbuf_copy_string_utf16(dst, src);
+	}
+
+	rc = pldm_msgbuf_destroy_consumed(src);
+	if (rc < 0) {
+		return rc;
+	}
+
+	/* Reinitialise for the second pass to extract the ASCII tag strings */
+	rc = pldm_msgbuf_init_errno(src, names_len, names, names_len);
+	if (rc < 0) {
+		return rc;
+	}
+
+	for (i = 0; i < pdr->name_string_count; i++) {
+		pldm_msgbuf_copy_string_ascii(dst, src);
+		pldm_msgbuf_span_string_utf16(src, NULL, NULL);
+	}
+
+	if ((rc = pldm_msgbuf_destroy(dst)) ||
+	    (rc = pldm_msgbuf_destroy(src)) ||
+	    (rc = pldm_msgbuf_destroy(buf))) {
+		return rc;
+	}
+
+	return 0;
+}
+
+LIBPLDM_ABI_TESTING
+int decode_pldm_entity_auxiliary_names_pdr_index(
+	struct pldm_entity_auxiliary_names_pdr *pdr)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+	int i;
+
+	if (!pdr) {
+		return -EINVAL;
+	}
+
+	if (pdr->name_string_count == 0 && pdr->names) {
+		return -EINVAL;
+	}
+
+	if (pdr->name_string_count > 0 && !pdr->names) {
+		return -EINVAL;
+	}
+
+	if (pdr->name_string_count == 0) {
+		return 0;
+	}
+
+	/*
+	 * Minimum size is one NUL for each member of each entry
+	 *
+	 * Note that the definition of nameLanguageTag in DSP0248 v1.2.2
+	 * states the following:
+	 *
+	 * > A null-terminated ISO646 ASCII string ...
+	 * >
+	 * > special value: null string = 0x0000 = unspecified.
+	 *
+	 * Until proven otherwise we will assume the "0x0000" is a
+	 * misrepresentation of an ASCII NUL, and that ASCII NUL is
+	 * represented by a single byte.
+	 */
+	rc = pldm_msgbuf_init_errno(
+		buf, pdr->name_string_count * (sizeof(char) + sizeof(char16_t)),
+		pdr->auxiliary_name_data, pdr->auxiliary_name_data_size);
+	if (rc) {
+		return rc;
+	}
+
+	for (i = 0; i < pdr->name_string_count; i++) {
+		void *loc = NULL;
+		pldm_msgbuf_span_string_utf16(buf, &loc, NULL);
+		pdr->names[i].name = loc;
+	}
+
+	for (i = 0; i < pdr->name_string_count; i++) {
+		void *loc = NULL;
+		pldm_msgbuf_span_string_ascii(buf, &loc, NULL);
+		pdr->names[i].tag = loc;
+	}
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
diff --git a/tests/dsp/platform.cpp b/tests/dsp/platform.cpp
index 51b68a2..0cf7c39 100644
--- a/tests/dsp/platform.cpp
+++ b/tests/dsp/platform.cpp
@@ -5020,3 +5020,180 @@
 
     EXPECT_EQ(rc, -EINVAL);
 }
+
+[[maybe_unused]] static size_t str16len(char16_t* startptr)
+{
+    char16_t* endptr = startptr;
+    while (*endptr)
+    {
+        endptr++;
+    }
+    return endptr - startptr;
+}
+
+#ifdef LIBPLDM_API_TESTING
+TEST(decodeEntityAuxNamePdrData, GoodTest)
+{
+    std::vector<uint8_t> pdr1{
+        // Common PDR Header
+        0x1, 0x0, 0x0, 0x0,              // record handle
+        0x1,                             // PDRHeaderVersion
+        PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+        0x1,
+        0x0, // recordChangeNumber
+        0x27,
+        0, // dataLength
+        /* Entity Auxiliary Names PDR Data*/
+        3,
+        0, // entityType system software
+        0x1,
+        0x0, // Entity instance number =1
+        PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID,
+        0,                // Overal system
+        0,                // shared Name Count one name only
+        03,               // nameStringCount
+        0x65, 0x6e, 0x00, // Language Tag "en"
+        0x00, 0x53, 0x00, 0x30, 0x00, 0x53, 0x00, 0x00, // Entity Name "S0S"
+        0x66, 0x6e, 0x00,                               // Language Tag "en"
+        0x00, 0x53, 0x00, 0x31, 0x00, 0x00,             // Entity Name "S1"
+        0x67, 0x6e, 0x00,                               // Language Tag "en"
+        0x00, 0x52, 0x00, 0x52, 0x00, 0x33, 0x00, 0x00  // Entity Name "RR3"
+    };
+
+    const char expectTag0[] = {0x65, 0x6e, 0x00};
+    const char expectTag1[] = {0x66, 0x6e, 0x00};
+    const char expectTag2[] = {0x67, 0x6e, 0x00};
+    const char expectName0[] = {0x00, 0x53, 0x00, 0x30, 0x00, 0x53, 0x00, 0x00};
+    const char expectName1[] = {0x00, 0x53, 0x00, 0x31, 0x00, 0x00};
+    const char expectName2[] = {0x00, 0x52, 0x00, 0x52, 0x00, 0x33, 0x00, 0x00};
+    auto names_offset = sizeof(struct pldm_pdr_hdr) +
+                        PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
+    auto names_size = pdr1.size() - names_offset;
+    size_t length = 0;
+
+    size_t decodedPdrSize =
+        sizeof(struct pldm_entity_auxiliary_names_pdr) + names_size;
+    auto decodedPdr =
+        (struct pldm_entity_auxiliary_names_pdr*)malloc(decodedPdrSize);
+    EXPECT_NE(nullptr, decodedPdr);
+
+    auto rc = decode_entity_auxiliary_names_pdr(pdr1.data(), pdr1.size(),
+                                                decodedPdr, decodedPdrSize);
+
+    EXPECT_EQ(0, rc);
+    EXPECT_EQ(1, decodedPdr->hdr.record_handle);
+    EXPECT_EQ(1, decodedPdr->hdr.version);
+    EXPECT_EQ(PLDM_ENTITY_AUXILIARY_NAMES_PDR, decodedPdr->hdr.type);
+    EXPECT_EQ(1, decodedPdr->hdr.record_change_num);
+    EXPECT_EQ(pdr1.size() - sizeof(struct pldm_pdr_hdr),
+              decodedPdr->hdr.length);
+    EXPECT_EQ(3, decodedPdr->container.entity_type);
+    EXPECT_EQ(1, decodedPdr->container.entity_instance_num);
+    EXPECT_EQ(PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID,
+              decodedPdr->container.entity_container_id);
+    EXPECT_EQ(0, decodedPdr->shared_name_count);
+    EXPECT_EQ(3, decodedPdr->name_string_count);
+
+    decodedPdr->names = (struct pldm_entity_auxiliary_name*)calloc(
+        decodedPdr->name_string_count,
+        sizeof(struct pldm_entity_auxiliary_name));
+    EXPECT_NE(nullptr, decodedPdr->names);
+
+    rc = decode_pldm_entity_auxiliary_names_pdr_index(decodedPdr);
+    EXPECT_EQ(0, rc);
+
+    length = strlen(decodedPdr->names[0].tag);
+    EXPECT_EQ(strlen(expectTag0), length);
+    EXPECT_EQ(strncmp(expectTag0, decodedPdr->names[0].tag, length + 1), 0);
+
+    // NOLINTBEGIN(clang-analyzer-unix.Malloc)
+    ASSERT_EQ(0,
+              (uintptr_t)decodedPdr->names[0].name & (alignof(char16_t) - 1));
+    // NOLINTEND(clang-analyzer-unix.Malloc)
+    length = str16len((char16_t*)decodedPdr->names[0].name);
+    EXPECT_EQ(str16len((char16_t*)expectName0), length);
+    EXPECT_EQ(3, str16len((char16_t*)expectName0));
+    EXPECT_EQ(memcmp(expectName0, decodedPdr->names[0].name,
+                     sizeof(char16_t) * (length + 1)),
+              0);
+
+    length = strlen(decodedPdr->names[1].tag);
+    EXPECT_EQ(strlen(expectTag1), length);
+    EXPECT_EQ(strncmp(expectTag1, decodedPdr->names[1].tag, length + 1), 0);
+
+    // NOLINTBEGIN(clang-analyzer-unix.Malloc)
+    ASSERT_EQ(0,
+              (uintptr_t)decodedPdr->names[1].name & (alignof(char16_t) - 1));
+    // NOLINTEND(clang-analyzer-unix.Malloc)
+    length = str16len((char16_t*)decodedPdr->names[1].name);
+    EXPECT_EQ(str16len((char16_t*)expectName1), length);
+    EXPECT_EQ(2, str16len((char16_t*)expectName1));
+    EXPECT_EQ(memcmp(expectName1, decodedPdr->names[1].name,
+                     sizeof(char16_t) * (length + 1)),
+              0);
+
+    length = strlen(decodedPdr->names[2].tag);
+    EXPECT_EQ(strlen(expectTag2), length);
+    EXPECT_EQ(strncmp(expectTag2, decodedPdr->names[2].tag, length + 1), 0);
+
+    // NOLINTBEGIN(clang-analyzer-unix.Malloc)
+    ASSERT_EQ(0,
+              (uintptr_t)decodedPdr->names[2].name & (alignof(char16_t) - 1));
+    // NOLINTEND(clang-analyzer-unix.Malloc)
+    length = str16len((char16_t*)decodedPdr->names[2].name);
+    EXPECT_EQ(str16len((char16_t*)expectName2), length);
+    EXPECT_EQ(3, str16len((char16_t*)expectName2));
+    EXPECT_EQ(memcmp(expectName2, decodedPdr->names[2].name,
+                     sizeof(char16_t) * (length + 1)),
+              0);
+
+    free(decodedPdr->names);
+    free(decodedPdr);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(decodeEntityAuxNamePdrData, BadTest)
+{
+    std::vector<uint8_t> pdr1{
+        // Common PDR Header
+        0x1, 0x0, 0x0, 0x0,              // record handle
+        0x1,                             // PDRHeaderVersion
+        PLDM_ENTITY_AUXILIARY_NAMES_PDR, // PDRType
+        0x1,
+        0x0,  // recordChangeNumber
+        0x25, // correct size is 0x27, input invalid size
+        0,    // dataLength
+        /* Entity Auxiliary Names PDR Data*/
+        3,
+        0, // entityType system software
+        0x1,
+        0x0, // Entity instance number =1
+        PLDM_PLATFORM_ENTITY_SYSTEM_CONTAINER_ID,
+        0,                // Overal system
+        0,                // shared Name Count one name only
+        0,                // Invalid nameStringCount
+        0x65, 0x6e, 0x00, // Language Tag "en"
+        0x00, 0x53, 0x00, 0x30, 0x00, 0x53, 0x00, 0x00, // Entity Name "S0S"
+        0x66, 0x6e, 0x00,                               // Language Tag "en"
+        0x00, 0x53, 0x00, 0x31, 0x00, 0x00,             // Entity Name "S1"
+        0x67, 0x6e, 0x00,                               // Language Tag "en"
+        0x00, 0x52, 0x00, 0x52, 0x00, 0x33, 0x00, 0x00  // Entity Name "RR3"
+    };
+
+    auto names_offset = sizeof(struct pldm_pdr_hdr) +
+                        PLDM_PDR_ENTITY_AUXILIARY_NAME_PDR_MIN_LENGTH;
+    auto names_size = pdr1.size() - names_offset;
+
+    size_t decodedPdrSize =
+        sizeof(struct pldm_entity_auxiliary_names_pdr) + names_size;
+    auto decodedPdr =
+        (struct pldm_entity_auxiliary_names_pdr*)malloc(decodedPdrSize);
+
+    auto rc = decode_entity_auxiliary_names_pdr(pdr1.data(), pdr1.size(),
+                                                decodedPdr, decodedPdrSize);
+
+    EXPECT_EQ(-EBADMSG, rc);
+    free(decodedPdr);
+}
+#endif