Implement PDR.FindStateEffecterPDR DBus API

This commit implements a DBus API defined at
https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-dbus-interfaces/+/31774
to find the stateEffecterPDR based on the entity ID
and stateSetID.

Tested: Unit tests and in witherspoon system
busctl call :1.187 /xyz/openbmc_project/pldm xyz.openbmc_project.PLDM.PDR FindStateEffecterPDR yqq 1 33 196
aay 1 29 1 0 0 0 1 11 0 0 19 0 0 0 1 0 33 0 0 0 0 0 0 0 0 0 1 196 0 1 6

busctl call :1.187 /xyz/openbmc_project/pldm xyz.openbmc_project.PLDM.PDR FindStateEffecterPDR yqq 1 31 129
aay 1 29 3 0 0 0 1 11 0 0 19 0 0 0 3 0 31 0 0 0 0 0 0 0 0 0 1 129 0 1 64

Change-Id: I5c15be5303b511465c36914f5b60a0957cd3857e
Signed-off-by: Pavithra Barithaya <pbaritha@in.ibm.com>
diff --git a/dbus_impl_pdr.cpp b/dbus_impl_pdr.cpp
new file mode 100644
index 0000000..c35e422
--- /dev/null
+++ b/dbus_impl_pdr.cpp
@@ -0,0 +1,33 @@
+#include "dbus_impl_pdr.hpp"
+
+#include "utils.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <iostream>
+
+#include "libpldm/pdr.h"
+#include "libpldm/pldm_types.h"
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+namespace pldm
+{
+namespace dbus_api
+{
+
+std::vector<std::vector<uint8_t>> Pdr::findStateEffecterPDR(uint8_t tid,
+                                                            uint16_t entityID,
+                                                            uint16_t stateSetId)
+{
+    auto pdrs =
+        pldm::utils::findStateEffecterPDR(tid, entityID, stateSetId, pdrRepo);
+
+    if (pdrs.empty())
+    {
+        throw ResourceNotFound();
+    }
+
+    return pdrs;
+}
+} // namespace dbus_api
+} // namespace pldm
diff --git a/dbus_impl_pdr.hpp b/dbus_impl_pdr.hpp
new file mode 100644
index 0000000..7033526
--- /dev/null
+++ b/dbus_impl_pdr.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "xyz/openbmc_project/PLDM/PDR/server.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <vector>
+
+#include "libpldm/pdr.h"
+#include "libpldm/platform.h"
+
+namespace pldm
+{
+namespace dbus_api
+{
+
+using PdrIntf = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::PLDM::server::PDR>;
+
+/** @class Pdr
+ *  @brief OpenBMC PLDM.PDR Implementation
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.PLDM.PDR DBus APIs.
+ */
+class Pdr : public PdrIntf
+{
+  public:
+    Pdr() = delete;
+    Pdr(const Pdr&) = delete;
+    Pdr& operator=(const Pdr&) = delete;
+    Pdr(Pdr&&) = delete;
+    Pdr& operator=(Pdr&&) = delete;
+    virtual ~Pdr() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] path - Path to attach at.
+     *  @param[in] repo - pointer to BMC's primary PDR repo
+     */
+    Pdr(sdbusplus::bus::bus& bus, const std::string& path,
+        const pldm_pdr* repo) :
+        PdrIntf(bus, path.c_str(), repo),
+        pdrRepo(repo){};
+
+    /** @brief Implementation for PdrIntf.FindStateEffecterPDR
+     *  @param[in] tid - PLDM terminus ID.
+     *  @param[in] entityID - entity that can be associated with PLDM State set.
+     *  @param[in] stateSetId - value that identifies PLDM State set.
+     */
+    std::vector<std::vector<uint8_t>>
+        findStateEffecterPDR(uint8_t tid, uint16_t entityID,
+                             uint16_t stateSetId) override;
+
+  private:
+    /** @brief pointer to BMC's primary PDR repo */
+    const pldm_pdr* pdrRepo;
+};
+
+} // namespace dbus_api
+} // namespace pldm
diff --git a/meson.build b/meson.build
index 88fe1d2..4d2f1ec 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@
   'pldmd.cpp',
   'dbus_impl_requester.cpp',
   'instance_id.cpp',
+  'dbus_impl_pdr.cpp',
   implicit_include_directories: false,
   dependencies: deps,
   install: true,
diff --git a/pldmd.cpp b/pldmd.cpp
index d1ca6fa..4bc8943 100644
--- a/pldmd.cpp
+++ b/pldmd.cpp
@@ -1,3 +1,4 @@
+#include "dbus_impl_pdr.hpp"
 #include "dbus_impl_requester.hpp"
 #include "host_pdr_handler.hpp"
 #include "invoker.hpp"
@@ -218,6 +219,7 @@
         exit(EXIT_FAILURE);
     }
 
+    dbus_api::Pdr dbusImplPdr(bus, "/xyz/openbmc_project/pldm", pdrRepo.get());
     auto callback = [verbose, &invoker, &dbusImplReq](IO& /*io*/, int fd,
                                                       uint32_t revents) {
         if (!(revents & EPOLLIN))
diff --git a/test/pldm_utils_test.cpp b/test/pldm_utils_test.cpp
index c880c20..4af91d1 100644
--- a/test/pldm_utils_test.cpp
+++ b/test/pldm_utils_test.cpp
@@ -1,5 +1,7 @@
 #include "utils.hpp"
 
+#include "libpldm/platform.h"
+
 #include <gtest/gtest.h>
 
 using namespace pldm::utils;
@@ -77,3 +79,404 @@
 
     EXPECT_EQ(effecterField, std::nullopt);
 }
+
+TEST(FindStateEffecterPDR, testOneMatch)
+{
+
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 33;
+    uint16_t stateSetId = 196;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 33;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 196;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(pdr, record[0]);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testNoMatch)
+{
+
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 44;
+    uint16_t stateSetId = 196;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 33;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 196;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(record.empty(), true);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testEmptyRepo)
+{
+
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 33;
+    uint16_t stateSetId = 196;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(record.empty(), true);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testMoreMatch)
+{
+
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 31;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 129;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    std::vector<uint8_t> pdr_second(
+        sizeof(struct pldm_state_effecter_pdr) - sizeof(uint8_t) +
+        sizeof(struct state_effecter_possible_states));
+
+    auto rec_second =
+        reinterpret_cast<pldm_state_effecter_pdr*>(pdr_second.data());
+
+    auto state_second = reinterpret_cast<state_effecter_possible_states*>(
+        rec_second->possible_states);
+
+    rec_second->hdr.type = 11;
+    rec_second->hdr.record_handle = 2;
+    rec_second->entity_type = 31;
+    rec_second->container_id = 0;
+    rec_second->composite_effecter_count = 1;
+    state_second->state_set_id = 129;
+    state_second->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr_second.data(), pdr_second.size(), 0, false);
+
+    uint16_t entityID_ = 31;
+    uint16_t stateSetId_ = 129;
+
+    auto record = findStateEffecterPDR(tid, entityID_, stateSetId_, repo);
+
+    EXPECT_EQ(pdr, record[0]);
+    EXPECT_EQ(pdr_second, record[1]);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testManyNoMatch)
+{
+
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 33;
+    uint16_t stateSetId = 196;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 34;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 198;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    std::vector<uint8_t> pdr_second(
+        sizeof(struct pldm_state_effecter_pdr) - sizeof(uint8_t) +
+        sizeof(struct state_effecter_possible_states));
+
+    auto rec_second =
+        reinterpret_cast<pldm_state_effecter_pdr*>(pdr_second.data());
+
+    auto state_second = reinterpret_cast<state_effecter_possible_states*>(
+        rec_second->possible_states);
+
+    rec_second->hdr.type = 11;
+    rec_second->hdr.record_handle = 2;
+    rec_second->entity_type = 39;
+    rec_second->container_id = 0;
+    rec_second->composite_effecter_count = 1;
+    state_second->state_set_id = 169;
+    state_second->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr_second.data(), pdr_second.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(record.empty(), true);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testOneMatchOneNoMatch)
+{
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 67;
+    uint16_t stateSetId = 192;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 32;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 198;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    std::vector<uint8_t> pdr_second(
+        sizeof(struct pldm_state_effecter_pdr) - sizeof(uint8_t) +
+        sizeof(struct state_effecter_possible_states));
+
+    auto rec_second =
+        reinterpret_cast<pldm_state_effecter_pdr*>(pdr_second.data());
+
+    auto state_second = reinterpret_cast<state_effecter_possible_states*>(
+        rec_second->possible_states);
+
+    rec_second->hdr.type = 11;
+    rec_second->hdr.record_handle = 2;
+    rec_second->entity_type = 67;
+    rec_second->container_id = 0;
+    rec_second->composite_effecter_count = 1;
+    state_second->state_set_id = 192;
+    state_second->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr_second.data(), pdr_second.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(pdr_second, record[0]);
+    EXPECT_EQ(record.size(), 1);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testOneMatchManyNoMatch)
+{
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 67;
+    uint16_t stateSetId = 192;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 32;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 1;
+    state->state_set_id = 198;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    std::vector<uint8_t> pdr_second(
+        sizeof(struct pldm_state_effecter_pdr) - sizeof(uint8_t) +
+        sizeof(struct state_effecter_possible_states));
+
+    auto rec_second =
+        reinterpret_cast<pldm_state_effecter_pdr*>(pdr_second.data());
+
+    auto state_second = reinterpret_cast<state_effecter_possible_states*>(
+        rec_second->possible_states);
+
+    rec_second->hdr.type = 11;
+    rec_second->hdr.record_handle = 2;
+    rec_second->entity_type = 67;
+    rec_second->container_id = 0;
+    rec_second->composite_effecter_count = 1;
+    state_second->state_set_id = 192;
+    state_second->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr_second.data(), pdr_second.size(), 0, false);
+
+    std::vector<uint8_t> pdr_third(
+        sizeof(struct pldm_state_effecter_pdr) - sizeof(uint8_t) +
+        sizeof(struct state_effecter_possible_states));
+
+    auto rec_third =
+        reinterpret_cast<pldm_state_effecter_pdr*>(pdr_third.data());
+
+    auto state_third = reinterpret_cast<state_effecter_possible_states*>(
+        rec_third->possible_states);
+
+    rec_third->hdr.type = 11;
+    rec_third->hdr.record_handle = 3;
+    rec_third->entity_type = 69;
+    rec_third->container_id = 0;
+    rec_third->composite_effecter_count = 1;
+    state_third->state_set_id = 199;
+    state_third->possible_states_size = 1;
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(pdr_second, record[0]);
+    EXPECT_EQ(record.size(), 1);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testCompositeEffecter)
+{
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 67;
+    uint16_t stateSetId = 192;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 67;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 3;
+    state->state_set_id = 198;
+    state->possible_states_size = 1;
+
+    state->state_set_id = 193;
+    state->possible_states_size = 1;
+
+    state->state_set_id = 192;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(pdr, record[0]);
+
+    pldm_pdr_destroy(repo);
+}
+
+TEST(FindStateEffecterPDR, testNoMatchCompositeEffecter)
+{
+    auto repo = pldm_pdr_init();
+    uint8_t tid = 1;
+    uint16_t entityID = 67;
+    uint16_t stateSetId = 192;
+
+    std::vector<uint8_t> pdr(sizeof(struct pldm_state_effecter_pdr) -
+                             sizeof(uint8_t) +
+                             sizeof(struct state_effecter_possible_states));
+
+    auto rec = reinterpret_cast<pldm_state_effecter_pdr*>(pdr.data());
+
+    auto state =
+        reinterpret_cast<state_effecter_possible_states*>(rec->possible_states);
+
+    rec->hdr.type = 11;
+    rec->hdr.record_handle = 1;
+    rec->entity_type = 34;
+    rec->container_id = 0;
+    rec->composite_effecter_count = 3;
+    state->state_set_id = 198;
+    state->possible_states_size = 1;
+
+    state->state_set_id = 193;
+    state->possible_states_size = 1;
+
+    state->state_set_id = 123;
+    state->possible_states_size = 1;
+
+    pldm_pdr_add(repo, pdr.data(), pdr.size(), 0, false);
+
+    auto record = findStateEffecterPDR(tid, entityID, stateSetId, repo);
+
+    EXPECT_EQ(record.empty(), true);
+
+    pldm_pdr_destroy(repo);
+}
diff --git a/utils.cpp b/utils.cpp
index 6a93d99..27fda03 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -10,6 +10,9 @@
 #include <vector>
 #include <xyz/openbmc_project/Common/error.hpp>
 
+#include "libpldm/pdr.h"
+#include "libpldm/pldm_types.h"
+
 namespace pldm
 {
 namespace utils
@@ -20,6 +23,59 @@
 constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";
 constexpr auto eidPath = "/usr/share/pldm/host_eid";
 
+std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t /*tid*/,
+                                                       uint16_t entityID,
+                                                       uint16_t stateSetId,
+                                                       const pldm_pdr* repo)
+{
+    uint8_t* outData = nullptr;
+    uint32_t size{};
+    const pldm_pdr_record* record{};
+    std::vector<std::vector<uint8_t>> pdrs;
+    try
+    {
+        do
+        {
+            record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_EFFECTER_PDR,
+                                                  record, &outData, &size);
+            if (record)
+            {
+                auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(outData);
+                auto compositeEffecterCount = pdr->composite_effecter_count;
+
+                for (auto effecters = 0x00; effecters < compositeEffecterCount;
+                     effecters++)
+                {
+                    auto possibleStates =
+                        reinterpret_cast<state_effecter_possible_states*>(
+                            pdr->possible_states);
+                    auto setId = possibleStates->state_set_id;
+                    auto possibleStateSize =
+                        possibleStates->possible_states_size;
+
+                    if (pdr->entity_type == entityID && setId == stateSetId)
+                    {
+                        std::vector<uint8_t> effecter_pdr(&outData[0],
+                                                          &outData[size]);
+                        pdrs.emplace_back(std::move(effecter_pdr));
+                        break;
+                    }
+                    possibleStates += possibleStateSize + sizeof(setId) +
+                                      sizeof(possibleStateSize);
+                }
+            }
+
+        } while (record);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << " Failed to obtain a record. ERROR =" << e.what()
+                  << std::endl;
+    }
+
+    return pdrs;
+}
+
 uint8_t readHostEID()
 {
     uint8_t eid{};
diff --git a/utils.hpp b/utils.hpp
index abf525b..0814de2 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -259,5 +259,17 @@
 PropertyValue jsonEntryToDbusVal(std::string_view type,
                                  const nlohmann::json& value);
 
+/** @brief Find State Effecter PDR
+ *  @param[in] tid - PLDM terminus ID.
+ *  @param[in] entityID - entity that can be associated with PLDM State set.
+ *  @param[in] stateSetId - value that identifies PLDM State set.
+ *  @param[in] repo - pointer to BMC's primary PDR repo.
+ *  @return array[array[uint8_t]] - StateEffecterPDRs
+ */
+std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t tid,
+                                                       uint16_t entityID,
+                                                       uint16_t stateSetId,
+                                                       const pldm_pdr* repo);
+
 } // namespace utils
 } // namespace pldm