pldm: Add support for OCC state changes

When the state of the OCC changes, host sends stateSensorEvent to
indicate going active or not active which is send as a D-Bus signal
by PLDM daemon. openpower-occ-control app listens for this signal
and updates the OCC D-Bus object. The lowest entity instance number
in the PDRs corresponds to first instance of the OCC.

Tested:

1) On a simics session verified that the sensor event is handled for
OCC going active and OCCActive property is set to true.
2) A sensorEvent is generated for OCC going non active and verified
OCCActive property is set to false.
3) Powered off the host and OCCActive property is set to false.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Change-Id: I0469cc483184b66acabc5f0cea1613a7768b3393
diff --git a/pldm.cpp b/pldm.cpp
new file mode 100644
index 0000000..c520340
--- /dev/null
+++ b/pldm.cpp
@@ -0,0 +1,154 @@
+#include "pldm.hpp"

+

+#include <libpldm/entity.h>

+#include <libpldm/platform.h>

+#include <libpldm/state_set.h>

+

+#include <phosphor-logging/log.hpp>

+

+namespace pldm

+{

+

+using sdbusplus::exception::SdBusError;

+using namespace phosphor::logging;

+

+void Interface::fetchOCCSensorInfo(const PdrList& pdrs,

+                                   SensorToOCCInstance& sensorInstanceMap,

+                                   SensorOffset& sensorOffset)

+{

+    bool offsetFound = false;

+    auto pdr =

+        reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());

+    auto possibleStatesPtr = pdr->possible_states;

+    for (auto offset = 0; offset < pdr->composite_sensor_count; offset++)

+    {

+        auto possibleStates =

+            reinterpret_cast<const state_sensor_possible_states*>(

+                possibleStatesPtr);

+

+        if (possibleStates->state_set_id ==

+            PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS)

+        {

+            sensorOffset = offset;

+            offsetFound = true;

+            break;

+        }

+        possibleStatesPtr += sizeof(possibleStates->state_set_id) +

+                             sizeof(possibleStates->possible_states_size) +

+                             possibleStates->possible_states_size;

+    }

+

+    if (!offsetFound)

+    {

+        log<level::ERR>("pldm: OCC state sensor PDR with StateSetId "

+                        "PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS not found");

+        return;

+    }

+

+    // To order SensorID based on the EntityInstance

+    std::map<EntityInstance, SensorID> entityInstMap{};

+    for (auto& pdr : pdrs)

+    {

+        auto pdrPtr =

+            reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());

+        entityInstMap.emplace(

+            static_cast<EntityInstance>(pdrPtr->entity_instance),

+            static_cast<SensorID>(pdrPtr->sensor_id));

+    }

+

+    open_power::occ::instanceID count = start;

+    for (auto const& pair : entityInstMap)

+    {

+        sensorInstanceMap.emplace(pair.second, count);

+        count++;

+    }

+}

+

+void Interface::sensorEvent(sdbusplus::message::message& msg)

+{

+    if (!isOCCSensorCacheValid())

+    {

+        PdrList pdrs{};

+

+        try

+        {

+            auto method = bus.new_method_call(

+                "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",

+                "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");

+            method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE,

+                          (uint16_t)PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS);

+

+            auto responseMsg = bus.call(method);

+            responseMsg.read(pdrs);

+        }

+        catch (const SdBusError& e)

+        {

+            log<level::ERR>("pldm: Failed to fetch the OCC state sensor PDRs",

+                            entry("ERROR=%s", e.what()));

+        }

+

+        if (!pdrs.size())

+        {

+            log<level::ERR>("pldm: OCC state sensor PDRs not present");

+            return;

+        }

+

+        fetchOCCSensorInfo(pdrs, sensorToOCCInstance, sensorOffset);

+    }

+

+    TerminusID tid{};

+    SensorID sensorId{};

+    SensorOffset msgSensorOffset{};

+    EventState eventState{};

+    EventState previousEventState{};

+

+    msg.read(tid, sensorId, sensorOffset, eventState, previousEventState);

+

+    auto sensorEntry = sensorToOCCInstance.find(sensorId);

+    if (sensorEntry == sensorToOCCInstance.end() ||

+        (msgSensorOffset != sensorOffset))

+    {

+        // No action for non matching sensorEvents

+        return;

+    }

+

+    bool newState{};

+    if (eventState == static_cast<EventState>(

+                          PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))

+    {

+        newState = callBack(sensorEntry->second, true);

+    }

+    else if (eventState ==

+             static_cast<EventState>(

+                 PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))

+    {

+        newState = callBack(sensorEntry->second, false);

+    }

+    else

+    {

+        return;

+    }

+

+    log<level::INFO>("pldm: Updated OCCActive state",

+                     entry("STATE=%s", newState ? "true" : "false"));

+    return;

+}

+

+void Interface::hostStateEvent(sdbusplus::message::message& msg)

+{

+    std::map<std::string, std::variant<std::string>> properties{};

+    std::string interface;

+    msg.read(interface, properties);

+    const auto stateEntry = properties.find("CurrentHostState");

+    if (stateEntry != properties.end())

+    {

+        auto stateEntryValue = stateEntry->second;

+        auto propVal = std::get<std::string>(stateEntryValue);

+        if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")

+        {

+            sensorToOCCInstance.clear();

+        }

+    }

+}

+

+} // namespace pldm
\ No newline at end of file