libpldm: Add APIs related to entity associations
Add APIs for:
- Finding an entity in the association tree
- Extracting entities from an entity association PDR
This is in preparation for the BMC to implement merging of entity
association PDRs received from the host firmware.
Tested: Unit tested.
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I049126570b00df5aab3cd102a54869052f2b43ff
diff --git a/libpldm/pdr.c b/libpldm/pdr.c
index a691163..b29f6f1 100644
--- a/libpldm/pdr.c
+++ b/libpldm/pdr.c
@@ -587,3 +587,76 @@
entity_association_pdr_add(tree->root, repo);
}
+
+void entity_association_tree_find(pldm_entity_node *node, pldm_entity *entity,
+ pldm_entity_node **out)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->entity.entity_type == entity->entity_type &&
+ node->entity.entity_instance_num == entity->entity_instance_num) {
+ entity->entity_container_id = node->entity.entity_container_id;
+ *out = node;
+ return;
+ }
+
+ entity_association_tree_find(node->next_sibling, entity, out);
+ entity_association_tree_find(node->first_child, entity, out);
+}
+
+pldm_entity_node *
+pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
+ pldm_entity *entity)
+{
+ assert(tree != NULL);
+
+ pldm_entity_node *node = NULL;
+ entity_association_tree_find(tree->root, entity, &node);
+ return node;
+}
+
+void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
+ size_t *num_entities,
+ pldm_entity **entities)
+{
+ assert(pdr != NULL);
+ assert(pdr_len >= sizeof(struct pldm_pdr_hdr) +
+ sizeof(struct pldm_pdr_entity_association));
+
+ struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr;
+ assert(hdr->type == PLDM_PDR_ENTITY_ASSOCIATION);
+
+ const uint8_t *start = (uint8_t *)pdr;
+ const uint8_t *end =
+ start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
+ start += sizeof(struct pldm_pdr_hdr);
+ struct pldm_pdr_entity_association *entity_association_pdr =
+ (struct pldm_pdr_entity_association *)start;
+ *num_entities = entity_association_pdr->num_children + 1;
+ assert(*num_entities >= 2);
+ *entities = malloc(sizeof(pldm_entity) * *num_entities);
+ assert(*entities != NULL);
+ assert(start + sizeof(struct pldm_pdr_entity_association) +
+ sizeof(pldm_entity) * (*num_entities - 2) ==
+ end);
+ (*entities)->entity_type =
+ le16toh(entity_association_pdr->container.entity_type);
+ (*entities)->entity_instance_num =
+ le16toh(entity_association_pdr->container.entity_instance_num);
+ (*entities)->entity_container_id =
+ le16toh(entity_association_pdr->container.entity_container_id);
+ pldm_entity *curr_entity = entity_association_pdr->children;
+ size_t i = 1;
+ while (i < *num_entities) {
+ (*entities + i)->entity_type =
+ le16toh(curr_entity->entity_type);
+ (*entities + i)->entity_instance_num =
+ le16toh(curr_entity->entity_instance_num);
+ (*entities + i)->entity_container_id =
+ le16toh(curr_entity->entity_container_id);
+ ++curr_entity;
+ ++i;
+ }
+}
diff --git a/libpldm/pdr.h b/libpldm/pdr.h
index c08dc59..c48a559 100644
--- a/libpldm/pdr.h
+++ b/libpldm/pdr.h
@@ -267,6 +267,30 @@
uint8_t pldm_entity_get_num_children(pldm_entity_node *node,
uint8_t association_type);
+/** @brief Find an entity in the entity association tree
+ *
+ * @param[in] tree - pointer to entity association tree
+ * @param[in/out] entity - entity type and instance id set on input, container
+ * id set on output
+ *
+ * @return pldm_entity_node* pointer to entity if found, NULL otherwise
+ */
+pldm_entity_node *
+pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
+ pldm_entity *entity);
+
+/** @brief Extract entities from entity association PDR
+ *
+ * @param[in] pdr - entity association PDR
+ * @param[in] pdr_len - size of entity association PDR in bytes
+ * @param[out] num_entities - number of entities found, including the container
+ * @param[out] entities - extracted entities, container is *entities[0]. Caller
+ * must free *entities
+ */
+void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
+ size_t *num_entities,
+ pldm_entity **entities);
+
#ifdef __cplusplus
}
#endif
diff --git a/libpldm/tests/libpldm_pdr_test.cpp b/libpldm/tests/libpldm_pdr_test.cpp
index 2f6b58d..b09f542 100644
--- a/libpldm/tests/libpldm_pdr_test.cpp
+++ b/libpldm/tests/libpldm_pdr_test.cpp
@@ -849,3 +849,154 @@
pldm_pdr_destroy(repo);
pldm_entity_association_tree_destroy(tree);
}
+
+TEST(EntityAssociationPDR, testFind)
+{
+ // 1
+ // |
+ // 2--3--4
+ // |
+ // 5--6--7
+ // | |
+ // 8 9
+
+ pldm_entity entities[9]{};
+
+ entities[0].entity_type = 1;
+ entities[1].entity_type = 2;
+ entities[2].entity_type = 2;
+ entities[3].entity_type = 3;
+ entities[4].entity_type = 4;
+ entities[5].entity_type = 5;
+ entities[6].entity_type = 5;
+ entities[7].entity_type = 6;
+ entities[8].entity_type = 7;
+
+ auto tree = pldm_entity_association_tree_init();
+
+ auto l1 = pldm_entity_association_tree_add(tree, &entities[0], nullptr,
+ PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l1, nullptr);
+ auto l2a = pldm_entity_association_tree_add(
+ tree, &entities[1], l1, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l2a, nullptr);
+ auto l2b = pldm_entity_association_tree_add(
+ tree, &entities[2], l1, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l2b, nullptr);
+ auto l2c = pldm_entity_association_tree_add(
+ tree, &entities[3], l1, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l2c, nullptr);
+ auto l3a = pldm_entity_association_tree_add(
+ tree, &entities[4], l2a, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l3a, nullptr);
+ auto l3b = pldm_entity_association_tree_add(
+ tree, &entities[5], l2a, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l3b, nullptr);
+ auto l3c = pldm_entity_association_tree_add(
+ tree, &entities[6], l2a, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l3c, nullptr);
+ auto l4a = pldm_entity_association_tree_add(
+ tree, &entities[7], l3a, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l4a, nullptr);
+ auto l4b = pldm_entity_association_tree_add(
+ tree, &entities[8], l3b, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
+ EXPECT_NE(l4b, nullptr);
+
+ pldm_entity entity{};
+
+ entity.entity_type = 1;
+ entity.entity_instance_num = 1;
+ auto result = pldm_entity_association_tree_find(tree, &entity);
+ EXPECT_EQ(result, l1);
+ EXPECT_EQ(entity.entity_container_id, 0);
+
+ entity.entity_type = 2;
+ entity.entity_instance_num = 1;
+ result = pldm_entity_association_tree_find(tree, &entity);
+ EXPECT_EQ(result, l2a);
+ EXPECT_EQ(entity.entity_container_id, 1);
+ entity.entity_type = 2;
+ entity.entity_instance_num = 2;
+ result = pldm_entity_association_tree_find(tree, &entity);
+ EXPECT_EQ(result, l2b);
+ EXPECT_EQ(entity.entity_container_id, 1);
+ entity.entity_type = 3;
+ entity.entity_instance_num = 1;
+ result = pldm_entity_association_tree_find(tree, &entity);
+ EXPECT_EQ(result, l2c);
+ EXPECT_EQ(entity.entity_container_id, 1);
+
+ entity.entity_type = 7;
+ entity.entity_instance_num = 1;
+ result = pldm_entity_association_tree_find(tree, &entity);
+ EXPECT_EQ(result, l4b);
+ EXPECT_EQ(entity.entity_container_id, 4);
+
+ pldm_entity_association_tree_destroy(tree);
+}
+
+TEST(EntityAssociationPDR, testExtract)
+{
+ std::vector<uint8_t> pdr{};
+ pdr.resize(sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_entity_association) +
+ sizeof(pldm_entity) * 4);
+ pldm_pdr_hdr* hdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
+ hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
+ hdr->length =
+ htole16(sizeof(pldm_pdr_entity_association) + sizeof(pldm_entity) * 4);
+
+ pldm_pdr_entity_association* e =
+ reinterpret_cast<pldm_pdr_entity_association*>(pdr.data() +
+ sizeof(pldm_pdr_hdr));
+ e->container_id = htole16(1);
+ e->num_children = 5;
+ e->container.entity_type = htole16(1);
+ e->container.entity_instance_num = htole16(1);
+ e->container.entity_container_id = htole16(0);
+
+ pldm_entity* entity = e->children;
+ entity->entity_type = htole16(2);
+ entity->entity_instance_num = htole16(1);
+ entity->entity_container_id = htole16(1);
+ ++entity;
+ entity->entity_type = htole16(3);
+ entity->entity_instance_num = htole16(1);
+ entity->entity_container_id = htole16(1);
+ ++entity;
+ entity->entity_type = htole16(4);
+ entity->entity_instance_num = htole16(1);
+ entity->entity_container_id = htole16(1);
+ ++entity;
+ entity->entity_type = htole16(5);
+ entity->entity_instance_num = htole16(1);
+ entity->entity_container_id = htole16(1);
+ ++entity;
+ entity->entity_type = htole16(6);
+ entity->entity_instance_num = htole16(1);
+ entity->entity_container_id = htole16(1);
+
+ size_t num{};
+ pldm_entity* out = nullptr;
+ pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &num, &out);
+ EXPECT_EQ(num, e->num_children + 1);
+ EXPECT_EQ(out[0].entity_type, 1);
+ EXPECT_EQ(out[0].entity_instance_num, 1);
+ EXPECT_EQ(out[0].entity_container_id, 0);
+ EXPECT_EQ(out[1].entity_type, 2);
+ EXPECT_EQ(out[1].entity_instance_num, 1);
+ EXPECT_EQ(out[1].entity_container_id, 1);
+ EXPECT_EQ(out[2].entity_type, 3);
+ EXPECT_EQ(out[2].entity_instance_num, 1);
+ EXPECT_EQ(out[2].entity_container_id, 1);
+ EXPECT_EQ(out[3].entity_type, 4);
+ EXPECT_EQ(out[3].entity_instance_num, 1);
+ EXPECT_EQ(out[3].entity_container_id, 1);
+ EXPECT_EQ(out[4].entity_type, 5);
+ EXPECT_EQ(out[4].entity_instance_num, 1);
+ EXPECT_EQ(out[4].entity_container_id, 1);
+ EXPECT_EQ(out[5].entity_type, 6);
+ EXPECT_EQ(out[5].entity_instance_num, 1);
+ EXPECT_EQ(out[5].entity_container_id, 1);
+
+ free(out);
+}