pdr: Add pldm_pdr_delete_by_effecter_id() API
Adds a new libpldm API to delete the PDR record from the PDR repo
based on the effecter ID of the PDR. This API is used when a state
effecter PDR record needs to be deleted based on the effecter ID
and return the corresponding record handle deleted.
Change-Id: I5ff5aee0db7767e07233961afb1aad9c65320e84
Signed-off-by: Pavithra Barithaya <pavithrabarithaya07@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c11a22..ce9e628 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
- utils: Introduce `pldm_edac_crc32()`
- utils: Introduce `pldm_edac_crc8()`
+- pdr: Add pldm_pdr_delete_by_effecter_id() API
### Changed
diff --git a/include/libpldm/pdr.h b/include/libpldm/pdr.h
index af14ace..08ab38b 100644
--- a/include/libpldm/pdr.h
+++ b/include/libpldm/pdr.h
@@ -246,6 +246,18 @@
int pldm_pdr_delete_by_record_handle(pldm_pdr *repo, uint32_t record_handle,
bool is_remote);
+/** @brief delete the state effecter PDR by effecter id
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] effecter_id - effecter ID of the PDR
+ * @param[in] is_remote - if true, then the PDR is not from this terminus
+ * @param[out] record_handle - if non-NULL, then record handle of the effecter PDR deleted
+ *
+ * @return record handle of the effecter PDR deleted from the repo
+ */
+int pldm_pdr_delete_by_effecter_id(pldm_pdr *repo, uint16_t effecter_id,
+ bool is_remote, uint32_t *record_handle);
+
/* ======================= */
/* FRU Record Set PDR APIs */
/* ======================= */
diff --git a/src/dsp/pdr.c b/src/dsp/pdr.c
index 1f0d8c3..e549fbb 100644
--- a/src/dsp/pdr.c
+++ b/src/dsp/pdr.c
@@ -456,6 +456,80 @@
return -ENOENT;
}
+LIBPLDM_CC_NONNULL
+static int decode_pldm_state_effecter_pdr(uint8_t *data, uint32_t size,
+ struct pldm_state_effecter_pdr *pdr)
+{
+ PLDM_MSGBUF_DEFINE_P(buf);
+ int rc = 0;
+ rc = pldm_msgbuf_init_errno(buf, sizeof(struct pldm_state_effecter_pdr),
+ (uint8_t *)data, size);
+ if (rc) {
+ return rc;
+ }
+
+ pldm_msgbuf_extract(buf, pdr->hdr.record_handle);
+ pldm_msgbuf_extract(buf, pdr->hdr.version);
+ pldm_msgbuf_extract(buf, pdr->hdr.type);
+ pldm_msgbuf_extract(buf, pdr->hdr.record_change_num);
+ pldm_msgbuf_extract(buf, pdr->hdr.length);
+ pldm_msgbuf_extract(buf, pdr->terminus_handle);
+ pldm_msgbuf_extract(buf, pdr->effecter_id);
+ pldm_msgbuf_extract(buf, pdr->entity_type);
+ pldm_msgbuf_extract(buf, pdr->entity_instance);
+ pldm_msgbuf_extract(buf, pdr->container_id);
+ pldm_msgbuf_extract(buf, pdr->effecter_semantic_id);
+ pldm_msgbuf_extract(buf, pdr->effecter_init);
+ pldm_msgbuf_extract(buf, pdr->has_description_pdr);
+ pldm_msgbuf_extract(buf, pdr->composite_effecter_count);
+
+ return pldm_msgbuf_complete(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_pdr_delete_by_effecter_id(pldm_pdr *repo, uint16_t effecter_id,
+ bool is_remote, uint32_t *record_handle)
+{
+ pldm_pdr_record *record;
+ pldm_pdr_record *prev = NULL;
+ int rc = 0;
+ int found;
+ struct pldm_state_effecter_pdr pdr;
+
+ if (!repo) {
+ return -EINVAL;
+ }
+ record = repo->first;
+
+ while (record != NULL) {
+ if (!record->data) {
+ continue;
+ }
+
+ rc = decode_pldm_state_effecter_pdr(record->data, record->size,
+ &pdr);
+ if (rc) {
+ return rc;
+ }
+
+ if (record->is_remote != is_remote ||
+ pdr.hdr.type != PLDM_STATE_EFFECTER_PDR) {
+ record = record->next;
+ continue;
+ }
+ found = pdr.effecter_id == effecter_id;
+ if (found) {
+ if (record_handle) {
+ *record_handle = record->record_handle;
+ }
+ prev = pldm_pdr_get_prev_record(repo, record);
+ return pldm_pdr_remove_record(repo, record, prev);
+ }
+ record = record->next;
+ }
+ return rc;
+}
+
LIBPLDM_ABI_STABLE
int pldm_pdr_delete_by_record_handle(pldm_pdr *repo, uint32_t record_handle,
bool is_remote)
diff --git a/tests/dsp/pdr.cpp b/tests/dsp/pdr.cpp
index 610bed2..6fe8d9c 100644
--- a/tests/dsp/pdr.cpp
+++ b/tests/dsp/pdr.cpp
@@ -802,6 +802,98 @@
pldm_pdr_destroy(repo);
}
+#ifdef LIBPLDM_API_TESTING
+TEST(PDRAccess, testRemoveByEffecterIDDecodeFailure)
+{
+ auto repo = pldm_pdr_init();
+ ASSERT_NE(repo, nullptr);
+
+ // Create a deliberately undersized PDR record
+ size_t invalidPdrSize = sizeof(pldm_state_effecter_pdr) - 5; // Invalid size
+ std::vector<uint8_t> entry(invalidPdrSize, 0);
+ pldm_state_effecter_pdr* pdr = new (entry.data()) pldm_state_effecter_pdr;
+ pdr->hdr.type = PLDM_STATE_EFFECTER_PDR;
+ pdr->effecter_id = 99; // random ID
+
+ uint32_t record_handle = 0;
+ EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1,
+ &record_handle),
+ 0);
+
+ // Attempt to delete the malformed record by effecter_id
+ uint32_t removed_record_handle = 0;
+ int rc =
+ pldm_pdr_delete_by_effecter_id(repo, 99, false, &removed_record_handle);
+
+ // We expect a failure from decode_pldm_state_effecter_pdr
+ EXPECT_NE(rc, 0);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo),
+ 1u); // Record remains in the repo
+ pldm_pdr_destroy(repo);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PDRAccess, testRemoveByEffecterID)
+{
+ auto repo = pldm_pdr_init();
+ ASSERT_NE(repo, nullptr);
+
+ size_t pdrSize = 0;
+ pdrSize = sizeof(pldm_state_effecter_pdr) +
+ sizeof(state_effecter_possible_states);
+ std::vector<uint8_t> entry{};
+ entry.resize(pdrSize);
+
+ pldm_state_effecter_pdr* pdr = new (entry.data()) pldm_state_effecter_pdr;
+ pdr->hdr.type = PLDM_STATE_EFFECTER_PDR;
+
+ pdr->effecter_id = 1;
+ uint32_t first = 0;
+ EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &first),
+ 0);
+
+ pdr->effecter_id = 2;
+ uint32_t second = 0;
+ EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &second),
+ 0);
+
+ pdr->effecter_id = 10;
+ uint32_t third = 0;
+ EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &third),
+ 0);
+
+ pdr->effecter_id = 20;
+ uint32_t fourth = 0;
+ EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &fourth),
+ 0);
+
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 4u);
+
+ uint32_t removed_record_handle{};
+ int rc =
+ pldm_pdr_delete_by_effecter_id(repo, 1, false, &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_record_handle, 1);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 3u);
+
+ // Error case where the effceter ID is not present in the repo
+ uint32_t removed_rec_handle{};
+ rc = pldm_pdr_delete_by_effecter_id(repo, 15, false, &removed_rec_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_rec_handle, 0);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 3u);
+
+ rc =
+ pldm_pdr_delete_by_effecter_id(repo, 20, false, &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_record_handle, 4);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 2u);
+
+ pldm_pdr_destroy(repo);
+}
+#endif
+
TEST(EntityAssociationPDR, testInit)
{
auto tree = pldm_entity_association_tree_init();