pldmd: D-Bus to host effecter translation

This commit implements a mechanism to move the Host's boot state
from 'not started' to 'boot complete' by setting the relevant
Host effecter when the associated D-Bus property is set in the BMC.

Also added an example JSON to match D-Bus to host effecters

Change-Id: I41025d99d2b4b3452d4c51b03efe3750e159328b
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/dbus_to_host_effecters.cpp b/dbus_to_host_effecters.cpp
new file mode 100644
index 0000000..b2fc092
--- /dev/null
+++ b/dbus_to_host_effecters.cpp
@@ -0,0 +1,341 @@
+#include "dbus_to_host_effecters.hpp"
+
+#include "libpldm/pdr.h"
+#include "libpldm/platform.h"
+#include "libpldm/requester/pldm.h"
+
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
+
+#include <fstream>
+#include <iostream>
+
+namespace pldm
+{
+namespace host_effecters
+{
+
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+constexpr auto hostEffecterJson = "dbus_to_host_effecter.json";
+
+void HostEffecterParser::populatePropVals(
+    const Json& dBusValues, std::vector<PropertyValue>& propertyValues,
+    const std::string& propertyType)
+
+{
+    for (const auto& elem : dBusValues)
+    {
+        auto value = jsonEntryToDbusVal(propertyType, elem);
+        propertyValues.emplace_back(value);
+    }
+}
+
+void HostEffecterParser::parseEffecterJson(const std::string& jsonPath)
+{
+    fs::path jsonDir(jsonPath);
+    if (!fs::exists(jsonDir) || fs::is_empty(jsonDir))
+    {
+        std::cerr << "Host Effecter json path does not exist, DIR=" << jsonPath
+                  << "\n";
+        return;
+    }
+
+    fs::path jsonFilePath = jsonDir / hostEffecterJson;
+    if (!fs::exists(jsonFilePath))
+    {
+        std::cerr << "json does not exist, PATH=" << jsonFilePath << "\n";
+        throw InternalFailure();
+    }
+
+    std::ifstream jsonFile(jsonFilePath);
+    auto data = Json::parse(jsonFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        std::cerr << "Parsing json file failed, FILE=" << jsonFilePath << "\n";
+        throw InternalFailure();
+    }
+    const Json empty{};
+    const std::vector<Json> emptyList{};
+
+    auto entries = data.value("entries", emptyList);
+    for (const auto& entry : entries)
+    {
+        EffecterInfo effecterInfo;
+        effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
+        auto jsonEffecterInfo = entry.value("effecter_info", empty);
+        auto effecterId =
+            jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID);
+        effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
+        effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
+        effecterInfo.entityInstance =
+            jsonEffecterInfo.value("entityInstance", 0);
+        effecterInfo.compEffecterCnt =
+            jsonEffecterInfo.value("compositeEffecterCount", 0);
+        auto effecters = entry.value("effecters", emptyList);
+        for (const auto& effecter : effecters)
+        {
+            DBusEffecterMapping dbusInfo{};
+            auto jsonDbusInfo = effecter.value("dbus_info", empty);
+            dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
+            dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
+            dbusInfo.dbusMap.propertyName =
+                jsonDbusInfo.value("property_name", "");
+            dbusInfo.dbusMap.propertyType =
+                jsonDbusInfo.value("property_type", "");
+            Json propertyValues = jsonDbusInfo["property_values"];
+
+            populatePropVals(propertyValues, dbusInfo.propertyValues,
+                             dbusInfo.dbusMap.propertyType);
+
+            const std::vector<uint8_t> emptyStates{};
+            auto state = effecter.value("state", empty);
+            dbusInfo.state.stateSetId = state.value("id", 0);
+            auto states = state.value("state_values", emptyStates);
+            if (dbusInfo.propertyValues.size() != states.size())
+            {
+                std::cerr << "Number of states do not match with"
+                          << " number of D-Bus property values in the json. "
+                          << "Object path " << dbusInfo.dbusMap.objectPath
+                          << " and property " << dbusInfo.dbusMap.propertyName
+                          << " will not be monitored \n";
+                continue;
+            }
+            for (const auto& s : states)
+            {
+                dbusInfo.state.states.emplace_back(s);
+            }
+
+            auto effecterInfoIndex = hostEffecterInfo.size();
+            auto dbusInfoIndex = effecterInfo.dbusInfo.size();
+            createHostEffecterMatch(
+                dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
+                effecterInfoIndex, dbusInfoIndex, effecterId);
+            effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
+        }
+        hostEffecterInfo.emplace_back(std::move(effecterInfo));
+    }
+}
+
+void HostEffecterParser::processHostEffecterChangeNotification(
+    const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
+    size_t dbusInfoIndex, uint16_t effecterId)
+{
+    const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
+                                   .dbusInfo[dbusInfoIndex]
+                                   .dbusMap.propertyName;
+
+    const auto& it = chProperties.find(propertyName);
+
+    if (it == chProperties.end())
+    {
+        return;
+    }
+
+    if (effecterId == PLDM_INVALID_EFFECTER_ID)
+    {
+        effecterId = findStateEffecterId(
+            pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
+            hostEffecterInfo[effecterInfoIndex].entityInstance,
+            hostEffecterInfo[effecterInfoIndex].containerId,
+            hostEffecterInfo[effecterInfoIndex]
+                .dbusInfo[dbusInfoIndex]
+                .state.stateSetId);
+        if (effecterId == PLDM_INVALID_EFFECTER_ID)
+        {
+            std::cerr << "Effecter id not found in pdr repo \n";
+            return;
+        }
+    }
+    constexpr auto hostStateInterface =
+        "xyz.openbmc_project.State.OperatingSystem.Status";
+    constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
+
+    try
+    {
+        auto propVal = dbusHandler->getDbusPropertyVariant(
+            hostStatePath, "OperatingSystemState", hostStateInterface);
+        const auto& currHostState = std::get<std::string>(propVal);
+        if ((currHostState != "xyz.openbmc_project.State.OperatingSystem."
+                              "Status.OSStatus.Standby") &&
+            (currHostState != "xyz.openbmc_project.State.OperatingSystem."
+                              "Status.OSStatus.BootComplete"))
+        {
+            std::cout << "Host is not up. Current host state: "
+                      << currHostState.c_str() << "\n";
+            return;
+        }
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        std::cerr << "Error in getting current host state. Will still "
+                     "continue to set the host effecter \n";
+    }
+    uint8_t newState{};
+    try
+    {
+        newState =
+            findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second);
+    }
+    catch (const std::out_of_range& e)
+    {
+        std::cerr << "new state not found in json"
+                  << "\n";
+        return;
+    }
+
+    std::vector<set_effecter_state_field> stateField;
+    for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
+         i++)
+    {
+        if (i == dbusInfoIndex)
+        {
+            stateField.push_back({PLDM_REQUEST_SET, newState});
+        }
+        else
+        {
+            stateField.push_back({PLDM_NO_CHANGE, 0});
+        }
+    }
+    int rc{};
+    try
+    {
+        rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
+    }
+    catch (const std::runtime_error& e)
+    {
+        std::cerr << "Could not set host state effecter \n";
+        return;
+    }
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Could not set the host state effecter, rc= " << rc
+                  << " \n";
+    }
+}
+
+uint8_t
+    HostEffecterParser::findNewStateValue(size_t effecterInfoIndex,
+                                          size_t dbusInfoIndex,
+                                          const PropertyValue& propertyValue)
+{
+    const auto& propValues = hostEffecterInfo[effecterInfoIndex]
+                                 .dbusInfo[dbusInfoIndex]
+                                 .propertyValues;
+    auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
+    uint8_t newState{};
+    if (it != propValues.end())
+    {
+        auto index = std::distance(propValues.begin(), it);
+        newState = hostEffecterInfo[effecterInfoIndex]
+                       .dbusInfo[dbusInfoIndex]
+                       .state.states[index];
+    }
+    else
+    {
+        throw std::out_of_range("new state not found in json");
+    }
+    return newState;
+}
+
+int HostEffecterParser::setHostStateEffecter(
+    size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
+    uint16_t effecterId)
+{
+    uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
+    uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
+    auto instanceId = requester->getInstanceId(mctpEid);
+
+    std::vector<uint8_t> requestMsg(
+        sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
+            sizeof(set_effecter_state_field) * compEffCnt,
+        0);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    auto rc = encode_set_state_effecter_states_req(
+        instanceId, effecterId, compEffCnt, stateField.data(), request);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Message encode failure. PLDM error code = " << std::hex
+                  << std::showbase << rc << "\n";
+        requester->markFree(mctpEid, instanceId);
+        return rc;
+    }
+
+    if (verbose)
+    {
+        if (requestMsg.size())
+        {
+            std::ostringstream tempStream;
+            for (int byte : requestMsg)
+            {
+                tempStream << std::setfill('0') << std::setw(2) << std::hex
+                           << byte << " ";
+            }
+            std::cout << tempStream.str() << std::endl;
+        }
+    }
+
+    uint8_t* responseMsg = nullptr;
+    size_t responseMsgSize{};
+
+    rc = pldm_send_recv(mctpEid, sockFd, requestMsg.data(), requestMsg.size(),
+                        &responseMsg, &responseMsgSize);
+    std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{responseMsg,
+                                                                  std::free};
+    requester->markFree(mctpEid, instanceId);
+
+    if (rc != PLDM_REQUESTER_SUCCESS)
+    {
+        std::cerr << "Failed to send message/receive response. RC = " << rc
+                  << ", errno = " << errno << " for setting host effecter "
+                  << effecterId << "\n";
+        pldm::utils::reportError(
+            "xyz.openbmc_project.bmc.pldm.InternalFailure");
+        return rc;
+    }
+    auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get());
+    uint8_t completionCode{};
+    rc = decode_set_state_effecter_states_resp(
+        responsePtr, responseMsgSize - sizeof(pldm_msg_hdr), &completionCode);
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Failed to decode setStateEffecterStates response, rc = "
+                  << rc << "\n";
+        return rc;
+    }
+    if (completionCode != PLDM_SUCCESS)
+    {
+        std::cerr << "Failed setStateEffecterStates for effecter " << effecterId
+                  << ". Response from Host " << (uint32_t)completionCode
+                  << "\n";
+        pldm::utils::reportError(
+            "xyz.openbmc_project.bmc.pldm.InternalFailure");
+    }
+    return rc;
+}
+
+void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath,
+                                                 const std::string& interface,
+                                                 size_t effecterInfoIndex,
+                                                 size_t dbusInfoIndex,
+                                                 uint16_t effecterId)
+{
+    using namespace sdbusplus::bus::match::rules;
+    effecterInfoMatch.emplace_back(
+        std::make_unique<sdbusplus::bus::match::match>(
+            pldm::utils::DBusHandler::getBus(),
+            propertiesChanged(objectPath, interface),
+            [this, effecterInfoIndex, dbusInfoIndex,
+             effecterId](sdbusplus::message::message& msg) {
+                DbusChgHostEffecterProps props;
+                std::string iface;
+                msg.read(iface, props);
+                processHostEffecterChangeNotification(
+                    props, effecterInfoIndex, dbusInfoIndex, effecterId);
+            }));
+}
+
+} // namespace host_effecters
+} // namespace pldm
diff --git a/dbus_to_host_effecters.hpp b/dbus_to_host_effecters.hpp
new file mode 100644
index 0000000..7c37c88
--- /dev/null
+++ b/dbus_to_host_effecters.hpp
@@ -0,0 +1,182 @@
+#pragma once
+
+#include "dbus_impl_requester.hpp"
+#include "types.hpp"
+#include "utils.hpp"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace pldm
+{
+
+using namespace utils;
+using namespace dbus_api;
+
+namespace host_effecters
+{
+
+using DbusChgHostEffecterProps = std::map<dbus::Property, PropertyValue>;
+
+/** @struct State
+ *  Contains the state set id and the possible states for
+ *  an effecter
+ */
+struct PossibleState
+{
+    uint16_t stateSetId;         //!< State set id
+    std::vector<uint8_t> states; //!< Possible states
+};
+
+/** @struct DBusEffecterMapping
+ *  Contains the D-Bus information for an effecter
+ */
+struct DBusEffecterMapping
+{
+    DBusMapping dbusMap;
+    std::vector<PropertyValue> propertyValues; //!< D-Bus property values
+    PossibleState state; //!< Corresponding effecter states
+};
+
+/** @struct EffecterInfo
+ *  Contains the effecter information as a whole
+ */
+struct EffecterInfo
+{
+    uint8_t mctpEid;         //!< Host mctp eid
+    uint16_t containerId;    //!< Container Id for host effecter
+    uint16_t entityType;     //!< Entity type for the host effecter
+    uint16_t entityInstance; //!< Entity instance for the host effecter
+    uint8_t compEffecterCnt; //!< Composite effecter count
+    std::vector<DBusEffecterMapping>
+        dbusInfo; //!< D-Bus information for the effecter id
+};
+
+/** @class HostEffecterParser
+ *
+ *  @brief This class parses the Host Effecter json file and monitors for the
+ *         D-Bus changes for the effecters. Upon change, calls the corresponding
+ *         setStateEffecterStates on the host
+ */
+class HostEffecterParser
+{
+  public:
+    HostEffecterParser() = delete;
+    HostEffecterParser(const HostEffecterParser&) = delete;
+    HostEffecterParser& operator=(const HostEffecterParser&) = delete;
+    HostEffecterParser(HostEffecterParser&&) = delete;
+    HostEffecterParser& operator=(HostEffecterParser&&) = delete;
+    virtual ~HostEffecterParser() = default;
+
+    /** @brief Constructor to create a HostEffecterParser object.
+     *  @param[in] requester - PLDM Requester object pointer
+     *  @param[in] fd - socket fd to communicate to host
+     *  @param[in] repo -  PLDM PDR repository
+     *  @param[in] dbusHandler - D-bus Handler
+     *  @param[in] jsonPath - path for the json file
+     *  @param[in] verbose - verbosity
+     */
+    explicit HostEffecterParser(Requester* requester, int fd,
+                                const pldm_pdr* repo,
+                                DBusHandler* const dbusHandler,
+                                const std::string& jsonPath,
+                                bool verbose = false) :
+        requester(requester),
+        sockFd(fd), pdrRepo(repo), dbusHandler(dbusHandler), verbose(verbose)
+    {
+        try
+        {
+            parseEffecterJson(jsonPath);
+        }
+        catch (const std::exception& e)
+        {
+            std::cerr << "The json file does not exist or malformed, ERROR="
+                      << e.what() << "\n";
+        }
+    }
+
+    /* @brief Parses the host effecter json
+     *
+     * @param[in] jsonPath - path for the json file
+     */
+    void parseEffecterJson(const std::string& jsonPath);
+
+    /* @brief Method to take action when the subscribed D-Bus property is
+     *        changed
+     * @param[in] chProperties - list of properties which have changed
+     * @param[in] effecterInfoIndex - index of effecterInfo pointer in
+     *                                hostEffecterInfo
+     * @param[in] dbusInfoIndex - index on dbusInfo pointer in each effecterInfo
+     * @param[in] effecterId - host effecter id
+     * @return - none
+     */
+    void processHostEffecterChangeNotification(
+        const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
+        size_t dbusInfoIndex, uint16_t effecterId);
+
+    /* @brief Populate the property values in each dbusInfo from the json
+     *
+     * @param[in] dBusValues - json values
+     * @param[out] propertyValues - dbusInfo property values
+     * @param[in] propertyType - type of the D-Bus property
+     * @return - none
+     */
+    void populatePropVals(const Json& dBusValues,
+                          std::vector<PropertyValue>& propertyValues,
+                          const std::string& propertyType);
+
+    /* @brief Set a host state effecter
+     *
+     * @param[in] effecterInfoIndex - index of effecterInfo pointer in
+     *                                hostEffecterInfo
+     * @param[in] stateField - vector of state fields equal to composite
+     *                         effecter count in number
+     * @param[in] effecterId - host effecter id
+     * @return - PLDM status code
+     */
+    virtual int
+        setHostStateEffecter(size_t effecterInfoIndex,
+                             std::vector<set_effecter_state_field>& stateField,
+                             uint16_t effecterId);
+
+    /* @brief Fetches the new state value and the index in stateField set which
+     *        needs to be set with the new value in the setStateEffecter call
+     * @param[in] effecterInfoIndex - index of effecterInfo in hostEffecterInfo
+     * @param[in] dbusInfoIndex - index of dbusInfo within effecterInfo
+     * @param[in] propertyValue - the changed D-Bus property value
+     * @return - the new state value
+     */
+    uint8_t findNewStateValue(size_t effecterInfoIndex, size_t dbusInfoIndex,
+                              const PropertyValue& propertyValue);
+
+    /* @brief Subscribes for D-Bus property change signal on the specified
+     * object
+     *
+     * @param[in] objectPath - D-Bus object path to look for
+     * @param[in] interface - D-Bus interface
+     * @param[in] effecterInfoIndex - index of effecterInfo pointer in
+     *                                hostEffecterInfo
+     * @param[in] dbusInfoIndex - index of dbusInfo within effecterInfo
+     * @param[in] effecterId - host effecter id
+     */
+    virtual void createHostEffecterMatch(const std::string& objectPath,
+                                         const std::string& interface,
+                                         size_t effecterInfoIndex,
+                                         size_t dbusInfoIndex,
+                                         uint16_t effecterId);
+
+  protected:
+    Requester* requester;    //!< Reference to Requester to obtain instance id
+    int sockFd;              //!< Socket fd to send message to host
+    const pldm_pdr* pdrRepo; //!< Reference to PDR repo
+    std::vector<EffecterInfo> hostEffecterInfo; //!< Parsed effecter information
+    std::vector<std::unique_ptr<sdbusplus::bus::match::match>>
+        effecterInfoMatch; //!< vector to catch the D-Bus property change
+                           //!< signals for the effecters
+    const DBusHandler* dbusHandler; //!< D-bus Handler
+    bool verbose;                   //!< verbose flag
+};
+
+} // namespace host_effecters
+} // namespace pldm
diff --git a/libpldm/platform.c b/libpldm/platform.c
index 3f91ee9..9b0403f 100644
--- a/libpldm/platform.c
+++ b/libpldm/platform.c
@@ -40,7 +40,8 @@
 		return rc;
 	}
 
-	if (comp_effecter_count < 0x1 || comp_effecter_count > 0x8) {
+	if (comp_effecter_count < 0x1 || comp_effecter_count > 0x8 ||
+	    field == NULL) {
 		return PLDM_ERROR_INVALID_DATA;
 	}
 
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 649189d..5d80511 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -48,6 +48,8 @@
 #define PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH 2
 #define PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH 2
 
+#define PLDM_INVALID_EFFECTER_ID 0xFFFF
+
 enum pldm_effecter_data_size {
 	PLDM_EFFECTER_DATA_SIZE_UINT8,
 	PLDM_EFFECTER_DATA_SIZE_SINT8,
diff --git a/libpldmresponder/examples/effecter/dbus_to_host_effecter.json b/libpldmresponder/examples/effecter/dbus_to_host_effecter.json
new file mode 100644
index 0000000..945557b
--- /dev/null
+++ b/libpldmresponder/examples/effecter/dbus_to_host_effecter.json
@@ -0,0 +1,115 @@
+{

+    "entries": [

+        {

+            #mctp eid is needed for communicating with the host

+            "mctp_eid": 9,

+            "effecter_info": {

+                #a 0xFFFF means effecter id is not hard coded and will be

+                #fetched from the PDR

+                "effecterID": 0xFFFF,

+                "containerID": 1,

+                "entityType": 67,

+                "entityInstance": 1,

+                "compositeEffecterCount": 3

+            },

+            "effecters": [

+                {

+                # Following are the D-Bus information under each effecter 

+                #which are monitored for a property change signal

+

+                #if there is a property change, the corresponding

+                #state value is picked up to set the host effecter

+

+                    "dbus_info": {

+                        "object_path": "/xyz/abc/def",

+                        "interface": "xyz.openbmc_project.example1.value",

+                        "property_name": "value1",

+                        "property_type": "string",

+                        "property_values": [

+                            "xyz.openbmc_project.State.On",

+                            "xyz.openbmc_project.State.Off"

+                        ]

+                    },

+                    "state" : {

+                        "id" : 197,

+                        # This should be of same size and order as

+                        # property_values

+                        "state_values": [

+                            0,

+                            1

+                        ]

+                    }

+                },

+                {

+                    "dbus_info": {

+                        "object_path": "/xyz/abc/def",

+                        "interface": "xyz.openbmc_project.example1.value",

+                        "property_name": "value2",

+                        "property_type": "uint8_t",

+                        "property_values": [

+                            9,

+                            10

+                        ]

+                    },

+                    "state" : {

+                        "id" : 12,

+                        "state_values": [

+                            2,

+                            3

+                        ]

+                    }

+                },

+                {

+                    "dbus_info": {

+                        "object_path": "/abc/def/xyz",

+                        "interface": "xyz.openbmc_project.example3.value",

+                        "property_name": "value3",

+                        "property_type": "bool",

+                        "property_values": [

+                            false,

+                            true

+                        ]

+                    },

+                    "state" : {

+                        "id" : 12,

+                        "state_values": [

+                            0,

+                            1

+                        ]

+                    }

+                }

+            ]

+        },

+        {

+            "mctp_eid": 10,

+            "effecter_info": {

+                "effecterID": 10,

+                "containerID": 0,

+                "entityType": 33,

+                "entityInstance": 0,

+                "compositeEffecterCount": 1

+            },

+            "effecters": [

+                {

+                    "dbus_info": {

+                        "object_path": "/def/abc/xyz",

+                        "interface": "xyz.openbmc_project.example4.value",

+                        "property_name": "value4",

+                        "property_type": "string",

+                        "property_values": [

+                            "xyz.openbmc_project.State.Enabled",

+                            "xyz.openbmc_project.State.Disabled"

+                        ]

+                    },

+                    "state" : {

+                        "id" : 15,

+                        "state_values": [

+                            3,

+                            4

+                        ]

+                    }

+                }

+            ]

+        }

+    ]

+}

diff --git a/meson.build b/meson.build
index 4d2f1ec..6691345 100644
--- a/meson.build
+++ b/meson.build
@@ -57,6 +57,7 @@
 
 deps = [
   libpldm,
+  libpldmutils,
   libpldmresponder,
   libpldmutils,
   dependency('sdbusplus'),
@@ -70,6 +71,7 @@
   'dbus_impl_requester.cpp',
   'instance_id.cpp',
   'dbus_impl_pdr.cpp',
+  'dbus_to_host_effecters.cpp',
   implicit_include_directories: false,
   dependencies: deps,
   install: true,
diff --git a/pldmd.cpp b/pldmd.cpp
index 1361429..0102047 100644
--- a/pldmd.cpp
+++ b/pldmd.cpp
@@ -5,6 +5,7 @@
 
 #include "dbus_impl_pdr.hpp"
 #include "dbus_impl_requester.hpp"
+#include "dbus_to_host_effecters.hpp"
 #include "host_pdr_handler.hpp"
 #include "invoker.hpp"
 #include "libpldmresponder/base.hpp"
@@ -176,6 +177,10 @@
         hostPDRHandler = std::make_unique<HostPDRHandler>(
             sockfd, hostEID, event, pdrRepo.get(), entityTree.get(),
             dbusImplReq);
+        DBusHandler dbusHandler;
+        pldm::host_effecters::HostEffecterParser hostEffecterParser(
+            &dbusImplReq, sockfd, pdrRepo.get(), &dbusHandler, HOST_JSONS_DIR,
+            verbose);
     }
 
     Invoker invoker{};
diff --git a/test/host_effecter_jsons/good/dbus_to_host_effecter.json b/test/host_effecter_jsons/good/dbus_to_host_effecter.json
new file mode 100644
index 0000000..dd95b17
--- /dev/null
+++ b/test/host_effecter_jsons/good/dbus_to_host_effecter.json
@@ -0,0 +1,37 @@
+{
+    "entries": [
+        {
+            "mctp_eid": 9,
+            "effecter_info": {
+                "effecterID": 4,
+                "containerID": 0,
+                "entityType": 33,
+                "entityInstance": 0,
+                "compositeEffecterCount": 1
+            },
+            "effecters": [
+                {
+                    "dbus_info": {
+                        "object_path": "/xyz/openbmc_project/control/host0/boot",
+                        "interface": "xyz.openbmc_project.Control.Boot.Mode",
+                        "property_name": "BootMode",
+                        "property_type": "string",
+                        "property_values": [
+                            "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"
+                        ]
+                    },
+                    "state" : {
+                        "id" : 196,
+                        "state_values": [
+                            2
+                            ]
+                    }
+                }
+
+            ]
+
+        }
+
+    ]
+
+}
diff --git a/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json b/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json
new file mode 100644
index 0000000..9db2505
--- /dev/null
+++ b/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json
@@ -0,0 +1,10 @@
+"effecters": [
+    {
+        "dbus_info": {
+            "object_path": "/xyz/openbmc_project/control/host0/boot",
+            "interface": "xyz.openbmc_project.Control.Boot.Mode",
+            "property_values": [
+                "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"
+                ]
+        }
+        ]
diff --git a/test/host_effecter_jsons/no_json/dummy.json b/test/host_effecter_jsons/no_json/dummy.json
new file mode 100644
index 0000000..401832d
--- /dev/null
+++ b/test/host_effecter_jsons/no_json/dummy.json
@@ -0,0 +1,5 @@
+"record_details":
+   {
+       "fru_record_type" : 1,
+       "encoding_type": 1
+    }
diff --git a/test/libpldmresponder_dbus_to_host_effecter_test.cpp b/test/libpldmresponder_dbus_to_host_effecter_test.cpp
new file mode 100644
index 0000000..e9ca8cc
--- /dev/null
+++ b/test/libpldmresponder_dbus_to_host_effecter_test.cpp
@@ -0,0 +1,92 @@
+#include "dbus_to_host_effecters.hpp"
+#include "mocked_utils.hpp"
+#include "utils.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+using namespace pldm::host_effecters;
+using namespace pldm::utils;
+using namespace pldm::dbus_api;
+
+class MockHostEffecterParser : public HostEffecterParser
+{
+  public:
+    MockHostEffecterParser(int fd, const pldm_pdr* repo,
+                           DBusHandler* const dbusHandler,
+                           const std::string& jsonPath) :
+        HostEffecterParser(nullptr, fd, repo, dbusHandler, jsonPath)
+    {}
+
+    MOCK_METHOD(void, setHostStateEffecter,
+                (size_t, std::vector<set_effecter_state_field>&, uint16_t),
+                (const override));
+
+    MOCK_METHOD(void, createHostEffecterMatch,
+                (const std::string&, const std::string&, size_t, size_t,
+                 uint16_t),
+                (const override));
+
+    const std::vector<EffecterInfo>& gethostEffecterInfo()
+    {
+        return hostEffecterInfo;
+    }
+};
+
+TEST(HostEffecterParser, parseEffecterJsonGoodPath)
+{
+    MockdBusHandler dbusHandler;
+    int sockfd{};
+    MockHostEffecterParser hostEffecterParserGood(sockfd, nullptr, &dbusHandler,
+                                                  "./host_effecter_jsons/good");
+    auto hostEffecterInfo = hostEffecterParserGood.gethostEffecterInfo();
+    ASSERT_EQ(hostEffecterInfo.size(), 1);
+    ASSERT_EQ(hostEffecterInfo[0].entityInstance, 0);
+    ASSERT_EQ(hostEffecterInfo[0].entityType, 33);
+    ASSERT_EQ(hostEffecterInfo[0].dbusInfo.size(), 1);
+    DBusEffecterMapping dbusInfo{
+        {"/xyz/openbmc_project/control/host0/boot",
+         "xyz.openbmc_project.Control.Boot.Mode", "BootMode", "string"},
+        {"xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"},
+        {196, {2}}};
+    auto& temp = hostEffecterInfo[0].dbusInfo[0];
+    ASSERT_EQ(temp.dbusMap.objectPath == dbusInfo.dbusMap.objectPath, true);
+    ASSERT_EQ(temp.dbusMap.interface == dbusInfo.dbusMap.interface, true);
+    ASSERT_EQ(temp.dbusMap.propertyName == dbusInfo.dbusMap.propertyName, true);
+    ASSERT_EQ(temp.dbusMap.propertyType == dbusInfo.dbusMap.propertyType, true);
+}
+
+TEST(HostEffecterParser, parseEffecterJsonBadPath)
+{
+    MockdBusHandler dbusHandler;
+    int sockfd{};
+    MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
+                                              "./host_effecter_jsons/no_json");
+    ASSERT_THROW(
+        hostEffecterParser.parseEffecterJson("./host_effecter_jsons/no_json"),
+        std::exception);
+    ASSERT_THROW(
+        hostEffecterParser.parseEffecterJson("./host_effecter_jsons/malformed"),
+        std::exception);
+}
+
+TEST(HostEffecterParser, findNewStateValue)
+{
+    MockdBusHandler dbusHandler;
+    int sockfd{};
+    MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
+                                              "./host_effecter_jsons/good");
+
+    PropertyValue val1{std::in_place_type<std::string>,
+                       "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"};
+    PropertyValue val2{std::in_place_type<std::string>,
+                       "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"};
+    auto newState = hostEffecterParser.findNewStateValue(0, 0, val1);
+    ASSERT_EQ(newState, 2);
+
+    ASSERT_THROW(hostEffecterParser.findNewStateValue(0, 0, val2),
+                 std::exception);
+}
diff --git a/test/libpldmresponder_pdr_effecter_test.cpp b/test/libpldmresponder_pdr_effecter_test.cpp
index 98b31ea..9d98c1f 100644
--- a/test/libpldmresponder_pdr_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_effecter_test.cpp
@@ -172,3 +172,21 @@
     pldm_pdr_destroy(inPDRRepo);
     pldm_pdr_destroy(outPDRRepo);
 }
+
+TEST(findStateEffecterId, goodJson)
+{
+    auto inPDRRepo = pldm_pdr_init();
+    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr);
+    uint16_t entityType = 33;
+    uint16_t entityInstance = 0;
+    uint16_t containerId = 0;
+    uint16_t stateSetId = 196;
+    auto effecterId = findStateEffecterId(inPDRRepo, entityType, entityInstance,
+                                          containerId, stateSetId);
+    ASSERT_EQ(effecterId, 1);
+    stateSetId = 300;
+    effecterId = findStateEffecterId(inPDRRepo, entityType, entityInstance,
+                                     containerId, stateSetId);
+    ASSERT_EQ(effecterId, PLDM_INVALID_EFFECTER_ID);
+    pldm_pdr_destroy(inPDRRepo);
+}
diff --git a/test/meson.build b/test/meson.build
index 4bbdbc5..9bef526 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -18,7 +18,8 @@
           sources: [
             '../instance_id.cpp',
             '../dbus_impl_requester.cpp',
-            '../host_pdr_handler.cpp'])
+            '../host_pdr_handler.cpp',
+            '../dbus_to_host_effecters.cpp'])
 
 tests = [
   'libpldmresponder_base_test',
@@ -35,6 +36,7 @@
   'pldmd_registration_test',
   'pldm_utils_test',
   'libpldmresponder_fru_test',
+  'libpldmresponder_dbus_to_host_effecter_test',
 ]
 
 if get_option('oem-ibm').enabled()
diff --git a/utils.cpp b/utils.cpp
index 10feeab..f659c07 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -393,5 +393,46 @@
     return propValue;
 }
 
+uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
+                             uint16_t entityInstance, uint16_t containerId,
+                             uint16_t stateSetId)
+{
+    uint8_t* pdrData = nullptr;
+    uint32_t pdrSize{};
+    const pldm_pdr_record* record{};
+    do
+    {
+        record = pldm_pdr_find_record_by_type(pdrRepo, PLDM_STATE_EFFECTER_PDR,
+                                              record, &pdrData, &pdrSize);
+        if (record)
+        {
+            auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrData);
+            auto compositeEffecterCount = pdr->composite_effecter_count;
+            auto possible_states_start = pdr->possible_states;
+
+            for (auto effecters = 0x00; effecters < compositeEffecterCount;
+                 effecters++)
+            {
+                auto possibleStates =
+                    reinterpret_cast<state_effecter_possible_states*>(
+                        possible_states_start);
+                auto setId = possibleStates->state_set_id;
+                auto possibleStateSize = possibleStates->possible_states_size;
+
+                if (entityType == pdr->entity_type &&
+                    entityInstance == pdr->entity_instance &&
+                    containerId == pdr->container_id && stateSetId == setId)
+                {
+                    return pdr->effecter_id;
+                }
+                possible_states_start += possibleStateSize + sizeof(setId) +
+                                         sizeof(possibleStateSize);
+            }
+        }
+    } while (record);
+
+    return PLDM_INVALID_EFFECTER_ID;
+}
+
 } // namespace utils
 } // namespace pldm
diff --git a/utils.hpp b/utils.hpp
index d93a807..0eb2b2d 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -25,6 +25,7 @@
 {
 
 namespace fs = std::filesystem;
+using Json = nlohmann::json;
 
 /** @struct CustomFD
  *
@@ -249,6 +250,7 @@
  *  @return uint8_t - MCTP EID
  */
 uint8_t readHostEID();
+
 /** @brief Convert a value in the JSON to a D-Bus property value
  *
  *  @param[in] type - type of the D-Bus property
@@ -282,5 +284,18 @@
                                                      uint16_t stateSetId,
                                                      const pldm_pdr* repo);
 
+/** @brief Find effecter id from a state effecter pdr
+ *  @param[in] pdrRepo - PDR repository
+ *  @param[in] entityType - entity type
+ *  @param[in] entityInstance - entity instance number
+ *  @param[in] containerId - container id
+ *  @param[in] stateSetId - state set id
+ *
+ *  @return uint16_t - the effecter id
+ */
+uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType,
+                             uint16_t entityInstance, uint16_t containerId,
+                             uint16_t stateSetId);
+
 } // namespace utils
 } // namespace pldm