pdr: Implement terminus locator PDR for BMC

This commit creates the TL PDR for the BMC. The TID and MCTP EID for
BMC is static, 1 and 8 respectively. BMC PDRs are assigned the value
of 1 for PLDMTerminusHandle. Added the parser for terminus locator
PDR in pldmtool.

Tested:

/tmp/pldmtool platform GetPdr -d 0

nextRecordHandle: 2
responseCount: 19
recordHandle: 1
PDRHeaderVersion: 1
PDRType: 1
recordChangeNumber: 0
dataLength: 9

PLDMTerminusHandle: 1
validity: valid
TID: 1
containerID: 0
terminusLocatorType: MCTP_EID
terminusLocatorValueSize: 1
EID: 8

Change-Id: I596301d6c676b450ae1f2cef872966b4c40d8bae
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/libpldm/platform.h b/libpldm/platform.h
index ed175a9..91128b7 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -241,6 +241,22 @@
 	PLDM_EVENT_LOGGING_REJECTED = 0x05
 };
 
+/** @brief PLDM Terminus Locator PDR validity
+ */
+enum pldm_terminus_locator_pdr_validity {
+	PLDM_TL_PDR_NOT_VALID,
+	PLDM_TL_PDR_VALID
+};
+
+/** @brief PLDM Terminus Locator type
+ */
+enum pldm_terminus_locator_type {
+	PLDM_TERMINUS_LOCATOR_TYPE_UID,
+	PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID,
+	PLDM_TERMINUS_LOCATOR_TYPE_SMBUS_RELATIVE,
+	PLDM_TERMINUS_LOCATOR_TYPE_SYS_SW
+};
+
 /** @struct pldm_pdr_hdr
  *
  *  Structure representing PLDM common PDR header
@@ -253,6 +269,30 @@
 	uint16_t length;
 } __attribute__((packed));
 
+/** @struct pldm_terminus_locator_pdr
+ *
+ *  Structure representing PLDM terminus locator PDR
+ */
+struct pldm_terminus_locator_pdr {
+	struct pldm_pdr_hdr hdr;
+	uint16_t terminus_handle;
+	uint8_t validity;
+	uint8_t tid;
+	uint16_t container_id;
+	uint8_t terminus_locator_type;
+	uint8_t terminus_locator_value_size;
+	uint8_t terminus_locator_value[1];
+} __attribute__((packed));
+
+/** @struct pldm_terminus_locator_type_mctp_eid
+ *
+ *  Structure representing terminus locator value for
+ *  terminus locator type MCTP_EID
+ */
+struct pldm_terminus_locator_type_mctp_eid {
+	uint8_t eid;
+} __attribute__((packed));
+
 /** @struct pldm_pdr_entity_association
  *
  *  Structure representing PLDM Entity Association PDR
diff --git a/libpldmresponder/pdr.hpp b/libpldmresponder/pdr.hpp
index c6f05a2..0745384 100644
--- a/libpldmresponder/pdr.hpp
+++ b/libpldmresponder/pdr.hpp
@@ -16,6 +16,10 @@
 namespace pdr
 {
 
+constexpr uint8_t BmcMctpEid = 8;
+constexpr uint8_t BmcPldmTerminusHandle = 1;
+constexpr uint8_t BmcTerminusId = 1;
+
 /** @brief Build (if not built already) and retrieve PDR by the PDR types
  *
  *  @param[in] dir - directory housing platform specific PDR JSON files
diff --git a/libpldmresponder/pdr_state_effecter.hpp b/libpldmresponder/pdr_state_effecter.hpp
index 1391def..173d328 100644
--- a/libpldmresponder/pdr_state_effecter.hpp
+++ b/libpldmresponder/pdr_state_effecter.hpp
@@ -2,7 +2,8 @@
 
 #include "libpldm/platform.h"
 
-#include "libpldmresponder/pdr_utils.hpp"
+#include "pdr.hpp"
+#include "pdr_utils.hpp"
 
 namespace pldm
 {
@@ -62,7 +63,7 @@
         pdr->hdr.record_change_num = 0;
         pdr->hdr.length = pdrSize - sizeof(pldm_pdr_hdr);
 
-        pdr->terminus_handle = 0;
+        pdr->terminus_handle = pdr::BmcPldmTerminusHandle;
         pdr->effecter_id = handler.getNextEffecterId();
         pdr->entity_type = e.value("type", 0);
         pdr->entity_instance = e.value("instance", 0);
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index 211ff65..3a9266e 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -3,6 +3,7 @@
 
 #include "common/utils.hpp"
 #include "event_parser.hpp"
+#include "pdr.hpp"
 #include "pdr_numeric_effecter.hpp"
 #include "pdr_state_effecter.hpp"
 #include "platform_numeric_effecter.hpp"
@@ -137,6 +138,7 @@
 {
     if (!pdrCreated)
     {
+        generateTerminusLocatorPDR(pdrRepo);
         generate(*dBusIntf, pdrJsonsDir, pdrRepo);
         pdrCreated = true;
     }
@@ -556,6 +558,33 @@
     return ccOnlyResponse(request, rc);
 }
 
+void Handler::generateTerminusLocatorPDR(Repo& repo)
+{
+    std::vector<uint8_t> pdrBuffer(sizeof(pldm_terminus_locator_pdr));
+
+    auto pdr = reinterpret_cast<pldm_terminus_locator_pdr*>(pdrBuffer.data());
+
+    pdr->hdr.record_handle = 0;
+    pdr->hdr.version = 1;
+    pdr->hdr.type = PLDM_TERMINUS_LOCATOR_PDR;
+    pdr->hdr.record_change_num = 0;
+    pdr->hdr.length = sizeof(pldm_terminus_locator_pdr) - sizeof(pldm_pdr_hdr);
+    pdr->terminus_handle = BmcPldmTerminusHandle;
+    pdr->validity = PLDM_TL_PDR_VALID;
+    pdr->tid = BmcTerminusId;
+    pdr->container_id = 0x0;
+    pdr->terminus_locator_type = PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID;
+    pdr->terminus_locator_value_size =
+        sizeof(pldm_terminus_locator_type_mctp_eid);
+    auto locatorValue = reinterpret_cast<pldm_terminus_locator_type_mctp_eid*>(
+        pdr->terminus_locator_value);
+    locatorValue->eid = BmcMctpEid;
+
+    PdrEntry pdrEntry{};
+    pdrEntry.data = pdrBuffer.data();
+    pdrEntry.size = pdrBuffer.size();
+    repo.addRecord(pdrEntry);
+}
 } // namespace platform
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 1ff6bc4..5cf2534 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -68,6 +68,7 @@
     {
         if (!buildPDRLazily)
         {
+            generateTerminusLocatorPDR(pdrRepo);
             generate(*dBusIntf, pdrJsonsDir, pdrRepo);
             pdrCreated = true;
         }
@@ -402,6 +403,12 @@
         return rc;
     }
 
+    /** @brief Build BMC Terminus Locator PDR
+     *
+     *  @param[in] repo - instance of concrete implementation of Repo
+     */
+    void generateTerminusLocatorPDR(Repo& repo);
+
   private:
     pdr_utils::Repo pdrRepo;
     uint16_t nextEffecterId{};
diff --git a/pldmtool/pldm_platform_cmd.cpp b/pldmtool/pldm_platform_cmd.cpp
index 0e3cc58..d54ec20 100644
--- a/pldmtool/pldm_platform_cmd.cpp
+++ b/pldmtool/pldm_platform_cmd.cpp
@@ -553,6 +553,34 @@
         }
     }
 
+    void printTerminusLocatorPDR(const uint8_t* data)
+    {
+        const std::array<std::string_view, 4> terminusLocatorType = {
+            "UID", "MCTP_EID", "SMBusRelative", "systemSoftware"};
+
+        auto pdr = reinterpret_cast<const pldm_terminus_locator_pdr*>(data);
+
+        std::cout << "PLDMTerminusHandle: " << pdr->terminus_handle
+                  << std::endl;
+        std::cout << "validity: " << (pdr->validity ? "valid" : "notValid")
+                  << std::endl;
+        std::cout << "TID: " << unsigned(pdr->tid) << std::endl;
+        std::cout << "containerID: " << pdr->container_id << std::endl;
+        std::cout << "terminusLocatorType: "
+                  << terminusLocatorType[pdr->terminus_locator_type]
+                  << std::endl;
+        std::cout << "terminusLocatorValueSize: "
+                  << unsigned(pdr->terminus_locator_value_size) << std::endl;
+
+        if (pdr->terminus_locator_type == PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
+        {
+            auto locatorValue =
+                reinterpret_cast<const pldm_terminus_locator_type_mctp_eid*>(
+                    pdr->terminus_locator_value);
+            std::cout << "EID: " << unsigned(locatorValue->eid) << std::endl;
+        }
+    }
+
     void printPDRMsg(const uint32_t nextRecordHndl, const uint16_t respCnt,
                      uint8_t* data)
     {
@@ -568,6 +596,9 @@
         printCommonPDRHeader(pdr);
         switch (pdr->type)
         {
+            case PLDM_TERMINUS_LOCATOR_PDR:
+                printTerminusLocatorPDR(data);
+                break;
             case PLDM_STATE_SENSOR_PDR:
                 printStateSensorPDR(data);
                 break;
diff --git a/test/libpldmresponder_pdr_effecter_test.cpp b/test/libpldmresponder_pdr_effecter_test.cpp
index a2a7288..390db58 100644
--- a/test/libpldmresponder_pdr_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_effecter_test.cpp
@@ -37,18 +37,18 @@
 
     // Check first PDR
     pdr_utils::PdrEntry e;
-    auto record1 = pdr::getRecordByHandle(outRepo, 1, e);
-    ASSERT_NE(record1, nullptr);
+    auto record2 = pdr::getRecordByHandle(outRepo, 2, e);
+    ASSERT_NE(record2, nullptr);
     pldm_state_effecter_pdr* pdr =
         reinterpret_cast<pldm_state_effecter_pdr*>(e.data);
 
-    ASSERT_EQ(pdr->hdr.record_handle, 1);
+    ASSERT_EQ(pdr->hdr.record_handle, 2);
     ASSERT_EQ(pdr->hdr.version, 1);
     ASSERT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
     ASSERT_EQ(pdr->hdr.record_change_num, 0);
     ASSERT_EQ(pdr->hdr.length, 23);
 
-    ASSERT_EQ(pdr->terminus_handle, 0);
+    ASSERT_EQ(pdr->terminus_handle, BmcPldmTerminusHandle);
     ASSERT_EQ(pdr->effecter_id, 1);
     ASSERT_EQ(pdr->entity_type, 33);
     ASSERT_EQ(pdr->entity_instance, 0);
@@ -70,17 +70,17 @@
     ASSERT_EQ(dbusMappings1[0].objectPath, "/foo/bar");
 
     // Check second PDR
-    auto record2 = pdr::getRecordByHandle(outRepo, 2, e);
-    ASSERT_NE(record2, nullptr);
+    auto record3 = pdr::getRecordByHandle(outRepo, 3, e);
+    ASSERT_NE(record3, nullptr);
     pdr = reinterpret_cast<pldm_state_effecter_pdr*>(e.data);
 
-    ASSERT_EQ(pdr->hdr.record_handle, 2);
+    ASSERT_EQ(pdr->hdr.record_handle, 3);
     ASSERT_EQ(pdr->hdr.version, 1);
     ASSERT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
     ASSERT_EQ(pdr->hdr.record_change_num, 0);
     ASSERT_EQ(pdr->hdr.length, 24);
 
-    ASSERT_EQ(pdr->terminus_handle, 0);
+    ASSERT_EQ(pdr->terminus_handle, BmcPldmTerminusHandle);
     ASSERT_EQ(pdr->effecter_id, 2);
     ASSERT_EQ(pdr->entity_type, 100);
     ASSERT_EQ(pdr->entity_instance, 0);
@@ -136,12 +136,12 @@
 
     // Check first PDR
     pdr_utils::PdrEntry e;
-    auto record = pdr::getRecordByHandle(outRepo, 3, e);
+    auto record = pdr::getRecordByHandle(outRepo, 4, e);
     ASSERT_NE(record, nullptr);
 
     pldm_numeric_effecter_value_pdr* pdr =
         reinterpret_cast<pldm_numeric_effecter_value_pdr*>(e.data);
-    EXPECT_EQ(pdr->hdr.record_handle, 3);
+    EXPECT_EQ(pdr->hdr.record_handle, 4);
     EXPECT_EQ(pdr->hdr.version, 1);
     EXPECT_EQ(pdr->hdr.type, PLDM_NUMERIC_EFFECTER_PDR);
     EXPECT_EQ(pdr->hdr.record_change_num, 0);
@@ -207,4 +207,4 @@
                                      containerId, stateSetId, true);
     ASSERT_EQ(effecterId, PLDM_INVALID_EFFECTER_ID);
     pldm_pdr_destroy(inPDRRepo);
-}
+}
\ No newline at end of file
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index 4183a0a..424aa08 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -228,7 +228,7 @@
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
-    auto record1 = pdr::getRecordByHandle(outRepo, 1, e);
+    auto record1 = pdr::getRecordByHandle(outRepo, 2, e);
     ASSERT_NE(record1, nullptr);
     pldm_state_effecter_pdr* pdr =
         reinterpret_cast<pldm_state_effecter_pdr*>(e.data);
@@ -268,7 +268,7 @@
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
-    auto record1 = pdr::getRecordByHandle(outRepo, 1, e);
+    auto record1 = pdr::getRecordByHandle(outRepo, 2, e);
     ASSERT_NE(record1, nullptr);
     pldm_state_effecter_pdr* pdr =
         reinterpret_cast<pldm_state_effecter_pdr*>(e.data);
@@ -313,8 +313,8 @@
     getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);
 
     pdr_utils::PdrEntry e;
-    auto record3 = pdr::getRecordByHandle(numericEffecterPDRs, 3, e);
-    ASSERT_NE(record3, nullptr);
+    auto record4 = pdr::getRecordByHandle(numericEffecterPDRs, 4, e);
+    ASSERT_NE(record4, nullptr);
 
     pldm_numeric_effecter_value_pdr* pdr =
         reinterpret_cast<pldm_numeric_effecter_value_pdr*>(e.data);
@@ -355,8 +355,8 @@
     getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);
 
     pdr_utils::PdrEntry e;
-    auto record3 = pdr::getRecordByHandle(numericEffecterPDRs, 3, e);
-    ASSERT_NE(record3, nullptr);
+    auto record4 = pdr::getRecordByHandle(numericEffecterPDRs, 4, e);
+    ASSERT_NE(record4, nullptr);
 
     pldm_numeric_effecter_value_pdr* pdr =
         reinterpret_cast<pldm_numeric_effecter_value_pdr*>(e.data);
@@ -520,3 +520,43 @@
         ASSERT_THROW(handler.getEventInfo(entry), std::out_of_range);
     }
 }
+
+TEST(TerminusLocatorPDR, BMCTerminusLocatorPDR)
+{
+    auto inPDRRepo = pldm_pdr_init();
+    auto outPDRRepo = pldm_pdr_init();
+    Repo outRepo(outPDRRepo);
+    MockdBusHandler mockedUtils;
+    Handler handler(&mockedUtils, "", "", inPDRRepo, nullptr, nullptr);
+    Repo inRepo(inPDRRepo);
+    getRepoByType(inRepo, outRepo, PLDM_TERMINUS_LOCATOR_PDR);
+
+    // 1 BMC terminus locator PDR in the PDR repository
+    ASSERT_EQ(outRepo.getRecordCount(), 1);
+
+    pdr_utils::PdrEntry entry;
+    auto record = pdr::getRecordByHandle(outRepo, 1, entry);
+    ASSERT_NE(record, nullptr);
+
+    auto pdr = reinterpret_cast<const pldm_terminus_locator_pdr*>(entry.data);
+    EXPECT_EQ(pdr->hdr.record_handle, 1);
+    EXPECT_EQ(pdr->hdr.version, 1);
+    EXPECT_EQ(pdr->hdr.type, PLDM_TERMINUS_LOCATOR_PDR);
+    EXPECT_EQ(pdr->hdr.record_change_num, 0);
+    EXPECT_EQ(pdr->hdr.length,
+              sizeof(pldm_terminus_locator_pdr) - sizeof(pldm_pdr_hdr));
+    EXPECT_EQ(pdr->terminus_handle, BmcPldmTerminusHandle);
+    EXPECT_EQ(pdr->validity, PLDM_TL_PDR_VALID);
+    EXPECT_EQ(pdr->tid, BmcTerminusId);
+    EXPECT_EQ(pdr->container_id, 0);
+    EXPECT_EQ(pdr->terminus_locator_type, PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID);
+    EXPECT_EQ(pdr->terminus_locator_value_size,
+              sizeof(pldm_terminus_locator_type_mctp_eid));
+    auto locatorValue =
+        reinterpret_cast<const pldm_terminus_locator_type_mctp_eid*>(
+            pdr->terminus_locator_value);
+    EXPECT_EQ(locatorValue->eid, BmcMctpEid);
+
+    pldm_pdr_destroy(inPDRRepo);
+    pldm_pdr_destroy(outPDRRepo);
+}