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/Makefile.am b/Makefile.am
index aed6412..28c025f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,6 +65,8 @@
openpower_occ_control_LDFLAGS = $(generic_ld_flags)
+include pldm.mk
+
org/open_power/OCC/Device/error.hpp: ${top_srcdir}/org/open_power/OCC/Device.errors.yaml
@mkdir -p `dirname $@`
$(SDBUSPLUSPLUS) -r $(top_srcdir) error exception-header org.open_power.OCC.Device > $@
diff --git a/configure.ac b/configure.ac
index 6a3f3b0..0137b6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,19 @@
]
AC_SUBST([CPPFLAGS], [$cpp_flags])
)
+
+ AC_ARG_WITH([host-communication-protocol],
+ AS_HELP_STRING([--with-host-communication-protocol], [To specify the host communication protocol])
+ )
+ AM_CONDITIONAL([ENABLE_PLDM], [test "$with_host_communication_protocol" == "pldm"])
+ AS_IF([test "x$with_host_communication_protocol" == "xpldm"],
+ AC_MSG_NOTICE([Enabling PLDM])
+ [
+ cpp_flags="$CPPFLAGS -DPLDM"
+ AX_PKG_CHECK_MODULES([LIBPLDM], [libpldm])
+ ]
+ AC_SUBST([CPPFLAGS], [$cpp_flags])
+ )
])
AC_ARG_VAR(OCC_CONTROL_BUSNAME, [The Dbus busname to own])
diff --git a/occ_manager.cpp b/occ_manager.cpp
index d420eea..f59683d 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -111,5 +111,12 @@
}
#endif
+#ifdef PLDM
+bool Manager::updateOCCActive(instanceID instance, bool status)
+{
+ return (statusObjects[instance])->occActive(status);
+}
+#endif
+
} // namespace occ
} // namespace open_power
diff --git a/occ_manager.hpp b/occ_manager.hpp
index 20002e4..d8282b1 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -2,6 +2,9 @@
#include "occ_pass_through.hpp"
#include "occ_status.hpp"
+#ifdef PLDM
+#include "pldm.hpp"
+#endif
#include "powercap.hpp"
#include <cstring>
@@ -34,7 +37,15 @@
* @param[in] bus - handle to the bus
* @param[in] event - Unique ptr reference to sd_event
*/
- Manager(sdbusplus::bus::bus& bus, EventPtr& event) : bus(bus), event(event)
+ Manager(sdbusplus::bus::bus& bus, EventPtr& event) :
+ bus(bus), event(event)
+#ifdef PLDM
+ ,
+ pldmHandle(std::make_unique<pldm::Interface>(
+ bus, std::bind(std::mem_fn(&Manager::updateOCCActive), this,
+ std::placeholders::_1, std::placeholders::_2)))
+#endif
+
{
#ifdef I2C_OCC
// I2C OCC status objects are initialized directly
@@ -114,6 +125,23 @@
*/
void initStatusObjects();
#endif
+
+#ifdef PLDM
+ /** @brief Callback handler invoked by the PLDM event handler when state of
+ * the OCC is toggled by the host. The caller passes the instance
+ * of the OCC and state of the OCC.
+ *
+ * @param[in] instance - instance of the OCC
+ * @param[in] status - true when the OCC goes active and false when the OCC
+ * goes inactive
+ *
+ * @return true if setting the state of OCC is successful and false if it
+ * fails.
+ */
+ bool updateOCCActive(instanceID instance, bool status);
+
+ std::unique_ptr<pldm::Interface> pldmHandle = nullptr;
+#endif
};
} // namespace occ
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
diff --git a/pldm.hpp b/pldm.hpp
new file mode 100644
index 0000000..76d7e44
--- /dev/null
+++ b/pldm.hpp
@@ -0,0 +1,135 @@
+#pragma once
+
+#include "occ_status.hpp"
+
+#include <sdbusplus/bus/match.hpp>
+
+namespace pldm
+{
+
+namespace MatchRules = sdbusplus::bus::match::rules;
+
+using EntityType = uint16_t;
+using EntityInstance = uint16_t;
+using EventState = uint8_t;
+using PdrList = std::vector<std::vector<uint8_t>>;
+using SensorID = uint16_t;
+using SensorOffset = uint8_t;
+using SensorToOCCInstance = std::map<SensorID, open_power::occ::instanceID>;
+using TerminusID = uint8_t;
+
+/** @brief OCC instance starts with 0 for example "occ0" */
+constexpr open_power::occ::instanceID start = 0;
+
+/** @brief Hardcoded TID */
+constexpr TerminusID tid = 0;
+
+/** @class Interface
+ *
+ * @brief Abstracts the PLDM details related to the OCC
+ */
+class Interface
+{
+ public:
+ Interface() = delete;
+ ~Interface() = default;
+ Interface(const Interface&) = delete;
+ Interface& operator=(const Interface&) = delete;
+ Interface(Interface&&) = delete;
+ Interface& operator=(Interface&&) = delete;
+
+ /** @brief Constructs the PLDM Interface object for OCC functions
+ *
+ * @param[in] bus - reference to systemd bus
+ * @param[in] callBack - callBack handler to invoke when the OCC state
+ * changes.
+ */
+ explicit Interface(
+ sdbusplus::bus::bus& bus,
+ std::function<bool(open_power::occ::instanceID, bool)> callBack) :
+ bus(bus),
+ callBack(callBack),
+ pldmEventSignal(
+ bus,
+ MatchRules::type::signal() +
+ MatchRules::member("StateSensorEvent") +
+ MatchRules::path("/xyz/openbmc_project/pldm") +
+ MatchRules::interface("xyz.openbmc_project.PLDM.Event"),
+ std::bind(std::mem_fn(&Interface::sensorEvent), this,
+ std::placeholders::_1)),
+ hostStateSignal(
+ bus,
+ MatchRules::propertiesChanged("/xyz/openbmc_project/state/host0",
+ "xyz.openbmc_project.State.Host"),
+ std::bind(std::mem_fn(&Interface::hostStateEvent), this,
+ std::placeholders::_1))
+ {
+ }
+
+ /** @brief Fetch the OCC state sensor PDRs and populate the cache with
+ * sensorId to OCC instance mapping information and the sensor
+ * offset for Operational Running Status.
+ *
+ * @param[in] pdrs - OCC state sensor PDRs
+ * @param[out] sensorInstanceMap - map of sensorID to OCC instance
+ * @param[out] sensorOffset - sensor offset of interested state set ID
+ */
+ void fetchOCCSensorInfo(const PdrList& pdrs,
+ SensorToOCCInstance& sensorInstanceMap,
+ SensorOffset& sensorOffset);
+
+ private:
+ /** @brief reference to the systemd bus*/
+ sdbusplus::bus::bus& bus;
+
+ /** @brief Callback handler to be invoked when the state of the OCC
+ * changes
+ */
+ std::function<bool(open_power::occ::instanceID, bool)> callBack = nullptr;
+
+ /** @brief Used to subscribe to D-Bus PLDM StateSensorEvent signal and
+ * processes if the event corresponds to OCC state change.
+ */
+ sdbusplus::bus::match_t pldmEventSignal;
+
+ /** @brief Used to subscribe for host state change signal */
+ sdbusplus::bus::match_t hostStateSignal;
+
+ /** @brief PLDM Sensor ID to OCC Instance mapping
+ */
+ SensorToOCCInstance sensorToOCCInstance;
+
+ /** @brief Sensor offset of state set ID
+ * PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS in state sensor PDR.
+ */
+ SensorOffset sensorOffset;
+
+ /** @brief When the OCC state changes host sends PlatformEventMessage
+ * StateSensorEvent, this function processes the D-Bus signal
+ * with the sensor event information and invokes the callback
+ * to change the OCC state.
+ *
+ * @param[in] msg - data associated with the subscribed signal
+ */
+ void sensorEvent(sdbusplus::message::message& msg);
+
+ /** @brief When the host state changes and if the CurrentHostState is
+ * xyz.openbmc_project.State.Host.HostState.Off then
+ * the cache of OCC sensors and effecters mapping is cleared.
+ *
+ * @param[in] msg - data associated with the subscribed signal
+ */
+ void hostStateEvent(sdbusplus::message::message& msg);
+
+ /** @brief Check if the PDR cache for PLDM OCC sensors is valid
+ *
+ * @return true if cache is populated and false if the cache is not
+ * populated.
+ */
+ auto isOCCSensorCacheValid()
+ {
+ return (sensorToOCCInstance.empty() ? false : true);
+ }
+};
+
+} // namespace pldm
diff --git a/pldm.mk b/pldm.mk
new file mode 100644
index 0000000..0733508
--- /dev/null
+++ b/pldm.mk
@@ -0,0 +1,12 @@
+if ENABLE_PLDM
+
+noinst_HEADERS += \
+ pldm.hpp
+libocc_control_la_SOURCES += \
+ pldm.cpp
+openpower_occ_control_LDADD += \
+ $(LIBPLDM_LIBS)
+openpower_occ_control_CXXFLAGS += \
+ $(LIBPLDM_CFLAGS)
+
+endif