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