libpldm: add APIs for FRU record set PDR ops

Add APIs for:
- adding FRU record set PDRs to a PDR repository
- finding FRU record set PDRs in a PDR repository based on the FRU
  record set identifier.

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I2b233af1325849cfc5d7de6a9a69af6771b222b2
diff --git a/libpldm/pdr.c b/libpldm/pdr.c
index 692a035..5e792a5 100644
--- a/libpldm/pdr.c
+++ b/libpldm/pdr.c
@@ -236,3 +236,68 @@
 
 	return record->record_handle;
 }
+
+uint32_t pldm_pdr_add_fru_record_set(pldm_pdr *repo, uint16_t terminus_handle,
+				     uint16_t fru_rsi, uint16_t entity_type,
+				     uint16_t entity_instance_num,
+				     uint16_t container_id)
+{
+	uint32_t size = sizeof(struct pldm_pdr_hdr) +
+			sizeof(struct pldm_pdr_fru_record_set);
+	uint8_t data[size];
+
+	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)&data;
+	hdr->version = 1;
+	hdr->record_handle = 0;
+	hdr->type = PLDM_PDR_FRU_RECORD_SET;
+	hdr->record_change_num = 0;
+	hdr->length = sizeof(struct pldm_pdr_fru_record_set);
+	struct pldm_pdr_fru_record_set *fru =
+	    (struct pldm_pdr_fru_record_set *)((uint8_t *)hdr +
+					       sizeof(struct pldm_pdr_hdr));
+	fru->terminus_handle = terminus_handle;
+	fru->fru_rsi = fru_rsi;
+	fru->entity_type = entity_type;
+	fru->entity_instance_num = entity_instance_num;
+	fru->container_id = container_id;
+
+	return pldm_pdr_add(repo, data, size, 0);
+}
+
+const pldm_pdr_record *pldm_pdr_fru_record_set_find_by_rsi(
+    const pldm_pdr *repo, uint16_t fru_rsi, uint16_t *terminus_handle,
+    uint16_t *entity_type, uint16_t *entity_instance_num,
+    uint16_t *container_id)
+{
+	assert(terminus_handle != NULL);
+	assert(entity_type != NULL);
+	assert(entity_instance_num != NULL);
+	assert(container_id != NULL);
+
+	uint8_t *data = NULL;
+	uint32_t size = 0;
+	const pldm_pdr_record *curr_record = pldm_pdr_find_record_by_type(
+	    repo, PLDM_PDR_FRU_RECORD_SET, NULL, &data, &size);
+	while (curr_record != NULL) {
+		struct pldm_pdr_fru_record_set *fru =
+		    (struct pldm_pdr_fru_record_set
+			 *)(data + sizeof(struct pldm_pdr_hdr));
+		if (fru->fru_rsi == fru_rsi) {
+			*terminus_handle = fru->terminus_handle;
+			*entity_type = fru->entity_type;
+			*entity_instance_num = fru->entity_instance_num;
+			*container_id = fru->container_id;
+			return curr_record;
+		}
+		data = NULL;
+		curr_record = pldm_pdr_find_record_by_type(
+		    repo, PLDM_PDR_FRU_RECORD_SET, curr_record, &data, &size);
+	}
+
+	*terminus_handle = 0;
+	*entity_type = 0;
+	*entity_instance_num = 0;
+	*container_id = 0;
+
+	return NULL;
+}
diff --git a/libpldm/pdr.h b/libpldm/pdr.h
index eb4a3bb..7fe0f23 100644
--- a/libpldm/pdr.h
+++ b/libpldm/pdr.h
@@ -133,6 +133,47 @@
 			     const pldm_pdr_record *curr_record, uint8_t **data,
 			     uint32_t *size);
 
+/* ======================= */
+/* FRU Record Set PDR APIs */
+/* ======================= */
+
+/** @brief Add a FRU record set PDR record to a PDR repository
+ *
+ *  @param[in/out] repo - opaque pointer acting as a PDR repo handle
+ *  @param[in] terminus_handle - PLDM terminus handle of terminus owning the PDR
+ *  record
+ *  @param[in] fru_rsi - FRU record set identifier
+ *  @param[in] entity_type - entity type of FRU
+ *  @param[in] entity_instance_num - entity instance number of FRU
+ *  @param[in] container_id - container id of FRU
+ *
+ *  @return uint32_t - record handle assigned to PDR record
+ */
+uint32_t pldm_pdr_add_fru_record_set(pldm_pdr *repo, uint16_t terminus_handle,
+				     uint16_t fru_rsi, uint16_t entity_type,
+				     uint16_t entity_instance_num,
+				     uint16_t container_id);
+
+/** @brief Find a FRU record set PDR by FRU record set identifier
+ *
+ *  @param[in] repo - opaque pointer acting as a PDR repo handle
+ *  @param[in] fru_rsi - FRU record set identifier
+ *  @param[in] terminus_handle - *terminus_handle will be FRU terminus handle of
+ *  found PDR, or 0 if not found
+ *  @param[in] entity_type - *entity_type will be FRU entity type of found PDR,
+ *  or 0 if not found
+ *  @param[in] entity_instance_num - *entity_instance_num will be FRU entity
+ *  instance number of found PDR, or 0 if not found
+ *  @param[in] container_id - *cintainer_id will be FRU container id of found
+ *  PDR, or 0 if not found
+ *
+ *  @return uint32_t - record handle assigned to PDR record
+ */
+const pldm_pdr_record *pldm_pdr_fru_record_set_find_by_rsi(
+    const pldm_pdr *repo, uint16_t fru_rsi, uint16_t *terminus_handle,
+    uint16_t *entity_type, uint16_t *entity_instance_num,
+    uint16_t *container_id);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 5341f6f..0cce1b4 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -45,6 +45,7 @@
  */
 enum pldm_pdr_types {
 	PLDM_STATE_EFFECTER_PDR = 11,
+	PLDM_PDR_FRU_RECORD_SET = 20,
 };
 
 /** @brief PLDM effecter initialization schemes
@@ -77,6 +78,18 @@
 	uint16_t length;
 } __attribute__((packed));
 
+/** @struct pldm_pdr_fru_record_set
+ *
+ *  Structure representing PLDM FRU record set PDR
+ */
+struct pldm_pdr_fru_record_set {
+	uint16_t terminus_handle;
+	uint16_t fru_rsi;
+	uint16_t entity_type;
+	uint16_t entity_instance_num;
+	uint16_t container_id;
+} __attribute__((packed));
+
 /** @struct pldm_state_effecter_pdr
  *
  *  Structure representing PLDM state effecter PDR
diff --git a/test/libpldm_pdr_test.cpp b/test/libpldm_pdr_test.cpp
index fbae94a..c1caf20 100644
--- a/test/libpldm_pdr_test.cpp
+++ b/test/libpldm_pdr_test.cpp
@@ -209,3 +209,111 @@
 
     pldm_pdr_destroy(repo);
 }
+
+TEST(PDRUpdate, testAddFruRecordSet)
+{
+    auto repo = pldm_pdr_init();
+
+    auto handle = pldm_pdr_add_fru_record_set(repo, 1, 10, 1, 0, 100);
+    EXPECT_EQ(handle, 1);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 1);
+    EXPECT_EQ(pldm_pdr_get_repo_size(repo),
+              sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_fru_record_set));
+    uint32_t size{};
+    uint32_t nextRecHdl{};
+    uint8_t* outData = nullptr;
+    auto hdl = pldm_pdr_find_record(repo, 0, &outData, &size, &nextRecHdl);
+    EXPECT_NE(hdl, nullptr);
+    EXPECT_EQ(size, sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(nextRecHdl, 0);
+    pldm_pdr_hdr* hdr = reinterpret_cast<pldm_pdr_hdr*>(outData);
+    EXPECT_EQ(hdr->version, 1);
+    EXPECT_EQ(hdr->type, PLDM_PDR_FRU_RECORD_SET);
+    EXPECT_EQ(hdr->length, sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(hdr->record_handle, 1);
+    pldm_pdr_fru_record_set* fru = reinterpret_cast<pldm_pdr_fru_record_set*>(
+        outData + sizeof(pldm_pdr_hdr));
+    EXPECT_EQ(fru->terminus_handle, 1);
+    EXPECT_EQ(fru->fru_rsi, 10);
+    EXPECT_EQ(fru->entity_type, 1);
+    EXPECT_EQ(fru->entity_instance_num, 0);
+    EXPECT_EQ(fru->container_id, 100);
+    outData = nullptr;
+
+    handle = pldm_pdr_add_fru_record_set(repo, 2, 11, 2, 1, 101);
+    EXPECT_EQ(handle, 2);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 2);
+    EXPECT_EQ(pldm_pdr_get_repo_size(repo),
+              2 * (sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_fru_record_set)));
+    hdl = pldm_pdr_find_record(repo, 2, &outData, &size, &nextRecHdl);
+    EXPECT_NE(hdl, nullptr);
+    EXPECT_EQ(size, sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(nextRecHdl, 0);
+    hdr = reinterpret_cast<pldm_pdr_hdr*>(outData);
+    EXPECT_EQ(hdr->version, 1);
+    EXPECT_EQ(hdr->type, PLDM_PDR_FRU_RECORD_SET);
+    EXPECT_EQ(hdr->length, sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(hdr->record_handle, 2);
+    fru = reinterpret_cast<pldm_pdr_fru_record_set*>(outData +
+                                                     sizeof(pldm_pdr_hdr));
+    EXPECT_EQ(fru->terminus_handle, 2);
+    EXPECT_EQ(fru->fru_rsi, 11);
+    EXPECT_EQ(fru->entity_type, 2);
+    EXPECT_EQ(fru->entity_instance_num, 1);
+    EXPECT_EQ(fru->container_id, 101);
+    outData = nullptr;
+
+    hdl = pldm_pdr_find_record(repo, 1, &outData, &size, &nextRecHdl);
+    EXPECT_NE(hdl, nullptr);
+    EXPECT_EQ(size, sizeof(pldm_pdr_hdr) + sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(nextRecHdl, 2);
+    hdr = reinterpret_cast<pldm_pdr_hdr*>(outData);
+    EXPECT_EQ(hdr->version, 1);
+    EXPECT_EQ(hdr->type, PLDM_PDR_FRU_RECORD_SET);
+    EXPECT_EQ(hdr->length, sizeof(pldm_pdr_fru_record_set));
+    EXPECT_EQ(hdr->record_handle, 1);
+    fru = reinterpret_cast<pldm_pdr_fru_record_set*>(outData +
+                                                     sizeof(pldm_pdr_hdr));
+    EXPECT_EQ(fru->terminus_handle, 1);
+    EXPECT_EQ(fru->fru_rsi, 10);
+    EXPECT_EQ(fru->entity_type, 1);
+    EXPECT_EQ(fru->entity_instance_num, 0);
+    EXPECT_EQ(fru->container_id, 100);
+    outData = nullptr;
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(PDRUpdate, tesFindtFruRecordSet)
+{
+    auto repo = pldm_pdr_init();
+
+    uint16_t terminusHdl{};
+    uint16_t entityType{};
+    uint16_t entityInstanceNum{};
+    uint16_t containerId{};
+    auto first = pldm_pdr_add_fru_record_set(repo, 1, 1, 1, 0, 100);
+    auto second = pldm_pdr_add_fru_record_set(repo, 1, 2, 1, 1, 100);
+    auto third = pldm_pdr_add_fru_record_set(repo, 1, 3, 1, 2, 100);
+    EXPECT_EQ(first, pldm_pdr_get_record_handle(
+                         repo, pldm_pdr_fru_record_set_find_by_rsi(
+                                   repo, 1, &terminusHdl, &entityType,
+                                   &entityInstanceNum, &containerId)));
+    EXPECT_EQ(second, pldm_pdr_get_record_handle(
+                          repo, pldm_pdr_fru_record_set_find_by_rsi(
+                                    repo, 2, &terminusHdl, &entityType,
+                                    &entityInstanceNum, &containerId)));
+    EXPECT_EQ(third, pldm_pdr_get_record_handle(
+                         repo, pldm_pdr_fru_record_set_find_by_rsi(
+                                   repo, 3, &terminusHdl, &entityType,
+                                   &entityInstanceNum, &containerId)));
+    EXPECT_EQ(terminusHdl, 1);
+    EXPECT_EQ(entityType, 1);
+    EXPECT_EQ(entityInstanceNum, 2);
+    EXPECT_EQ(containerId, 100);
+    EXPECT_EQ(nullptr, pldm_pdr_fru_record_set_find_by_rsi(
+                           repo, 4, &terminusHdl, &entityType,
+                           &entityInstanceNum, &containerId));
+
+    pldm_pdr_destroy(repo);
+}