pldm_events: Parse state sensor PDRs from the Host PDR

Parse the State Sensor PDRs from the host firmware and build
the HostStateSensorMap data structure which will be used to
lookup the sensor info for the sensorEventType in the
PlatformEventMessage command. Also clear the HostStateSensorMap
when the host powers off.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Change-Id: Id85a73f1a0a1caf4b4155a8d235b3e807383e53a
diff --git a/host_pdr_handler.cpp b/host_pdr_handler.cpp
index dabb401..db845fc 100644
--- a/host_pdr_handler.cpp
+++ b/host_pdr_handler.cpp
@@ -66,7 +66,7 @@
         pldm::utils::DBusHandler::getBus(),
         propertiesChanged("/xyz/openbmc_project/state/host0",
                           "xyz.openbmc_project.State.Host"),
-        [repo](sdbusplus::message::message& msg) {
+        [this, repo](sdbusplus::message::message& msg) {
             DbusChangedProps props{};
             std::string intf;
             msg.read(intf, props);
@@ -78,6 +78,7 @@
                 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
                 {
                     pldm_pdr_remove_remote_pdrs(repo);
+                    this->sensorMap.clear();
                 }
             }
         });
@@ -178,6 +179,17 @@
                 }
                 else
                 {
+                    if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
+                    {
+                        const auto& [terminusHandle, sensorID, sensorInfo] =
+                            responder::pdr_utils::parseStateSensorPDR(pdr);
+                        SensorEntry sensorEntry{};
+                        // TODO: Lookup Terminus ID from the Terminus Locator
+                        // PDR with terminusHandle
+                        sensorEntry.terminusID = 0;
+                        sensorEntry.sensorID = sensorID;
+                        sensorMap.emplace(sensorEntry, std::move(sensorInfo));
+                    }
                     pldm_pdr_add(repo, pdr.data(), respCount, 0, true);
                 }
             }
diff --git a/host_pdr_handler.hpp b/host_pdr_handler.hpp
index 850afdb..83abe5f 100644
--- a/host_pdr_handler.hpp
+++ b/host_pdr_handler.hpp
@@ -1,6 +1,8 @@
 #pragma once
 
 #include "dbus_impl_requester.hpp"
+#include "libpldmresponder/pdr_utils.hpp"
+#include "types.hpp"
 #include "utils.hpp"
 
 #include <map>
@@ -23,6 +25,31 @@
 using ChangeEntry = uint32_t;
 using PDRRecordHandles = std::vector<ChangeEntry>;
 
+/** @struct SensorEntry
+ *
+ *  SensorEntry is a unique key which maps a sensorEventType request in the
+ *  PlatformEventMessage command to a host sensor PDR. This struct is a key
+ *  in a std::map, so implemented operator==and operator<.
+ */
+struct SensorEntry
+{
+    pdr::TerminusID terminusID;
+    pdr::SensorID sensorID;
+
+    bool operator==(const SensorEntry& e) const
+    {
+        return ((terminusID == e.terminusID) && (sensorID == e.sensorID));
+    }
+
+    bool operator<(const SensorEntry& e) const
+    {
+        return ((terminusID < e.terminusID) ||
+                ((terminusID == e.terminusID) && (sensorID < e.sensorID)));
+    }
+};
+
+using HostStateSensorMap = std::map<SensorEntry, pdr::SensorInfo>;
+
 /** @class HostPDRHandler
  *  @brief This class can fetch and process PDRs from host firmware
  *  @details Provides an API to fetch PDRs from the host firmware. Upon
@@ -71,6 +98,18 @@
     void sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
                                    uint8_t eventDataFormat);
 
+    /** @brief Lookup host sensor info corresponding to requested SensorEntry
+     *
+     *  @param[in] entry - TerminusID and SensorID
+     *
+     *  @return SensorInfo corresponding to the input paramter SensorEntry
+     *          throw std::out_of_range exception if not found
+     */
+    const pdr::SensorInfo& lookupSensorInfo(const SensorEntry& entry) const
+    {
+        return sensorMap.at(entry);
+    }
+
   private:
     /** @brief fetchPDR schedules work on the event loop, this method does the
      *  actual work. This is so that the PDR exchg with the host is async.
@@ -119,6 +158,12 @@
     std::map<EntityType, pldm_entity> parents;
     /** @brief D-Bus property changed signal match */
     std::unique_ptr<sdbusplus::bus::match::match> hostOffMatch;
+
+    /** @brief sensorMap is a lookup data structure that is build from the
+     *         hostPDR that speeds up the lookup of <TerminusID, SensorID> in
+     *         PlatformEventMessage command request.
+     */
+    HostStateSensorMap sensorMap;
 };
 
 } // namespace pldm
diff --git a/libpldm/platform.h b/libpldm/platform.h
index cafd47c..f2ee670 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -129,6 +129,7 @@
 /** @brief PLDM PDR types
  */
 enum pldm_pdr_types {
+	PLDM_STATE_SENSOR_PDR = 4,
 	PLDM_NUMERIC_EFFECTER_PDR = 9,
 	PLDM_STATE_EFFECTER_PDR = 11,
 	PLDM_PDR_ENTITY_ASSOCIATION = 15,
@@ -268,6 +269,33 @@
 	uint16_t container_id;
 } __attribute__((packed));
 
+/** @struct pldm_state_sensor_pdr
+ *
+ *  Structure representing PLDM state sensor PDR
+ */
+struct pldm_state_sensor_pdr {
+	struct pldm_pdr_hdr hdr;
+	uint16_t terminus_handle;
+	uint16_t sensor_id;
+	uint16_t entity_type;
+	uint16_t entity_instance;
+	uint16_t container_id;
+	uint8_t sensor_init;
+	bool8_t sensor_auxiliary_names_pdr;
+	uint8_t composite_sensor_count;
+	uint8_t possible_states[1];
+} __attribute__((packed));
+
+/** @struct state_sensor_possible_states
+ *
+ *  Structure representing state enums for state sensor
+ */
+struct state_sensor_possible_states {
+	uint16_t state_set_id;
+	uint8_t possible_states_size;
+	bitfield8_t states[1];
+} __attribute__((packed));
+
 /** @struct pldm_state_effecter_pdr
  *
  *  Structure representing PLDM state effecter PDR
diff --git a/libpldmresponder/pdr_utils.cpp b/libpldmresponder/pdr_utils.cpp
index 85af37f..8819e67 100644
--- a/libpldmresponder/pdr_utils.cpp
+++ b/libpldmresponder/pdr_utils.cpp
@@ -1,5 +1,9 @@
 #include "pdr.hpp"
 
+#include <climits>
+
+#include "libpldm/platform.h"
+
 namespace pldm
 {
 
@@ -135,6 +139,54 @@
     return valueMap;
 }
 
+std::tuple<TerminusHandle, SensorID, SensorInfo>
+    parseStateSensorPDR(const std::vector<uint8_t>& stateSensorPdr)
+{
+    auto pdr =
+        reinterpret_cast<const pldm_state_sensor_pdr*>(stateSensorPdr.data());
+    CompositeSensorStates sensors{};
+    auto statesPtr = pdr->possible_states;
+    auto compositeSensorCount = pdr->composite_sensor_count;
+
+    while (compositeSensorCount--)
+    {
+        auto state =
+            reinterpret_cast<const state_sensor_possible_states*>(statesPtr);
+        PossibleStates possibleStates{};
+        uint8_t possibleStatesPos{};
+        auto updateStates = [&possibleStates,
+                             &possibleStatesPos](const bitfield8_t& val) {
+            for (int i = 0; i < CHAR_BIT; i++)
+            {
+                if (val.byte & (1 << i))
+                {
+                    possibleStates.insert(possibleStatesPos * CHAR_BIT + i);
+                }
+            }
+            possibleStatesPos++;
+        };
+        std::for_each(&state->states[0],
+                      &state->states[state->possible_states_size],
+                      updateStates);
+
+        sensors.emplace_back(std::move(possibleStates));
+        if (compositeSensorCount)
+        {
+            statesPtr += sizeof(state_sensor_possible_states) +
+                         state->possible_states_size - 1;
+        }
+    }
+
+    auto entityInfo =
+        std::make_tuple(static_cast<ContainerID>(pdr->container_id),
+                        static_cast<EntityType>(pdr->entity_type),
+                        static_cast<EntityInstance>(pdr->entity_instance));
+    auto sensorInfo =
+        std::make_tuple(std::move(entityInfo), std::move(sensors));
+    return std::make_tuple(pdr->terminus_handle, pdr->sensor_id,
+                           std::move(sensorInfo));
+}
+
 } // namespace pdr_utils
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/pdr_utils.hpp b/libpldmresponder/pdr_utils.hpp
index 6df5d94..8f4f825 100644
--- a/libpldmresponder/pdr_utils.hpp
+++ b/libpldmresponder/pdr_utils.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "types.hpp"
 #include "utils.hpp"
 
 #include <stdint.h>
@@ -195,6 +196,18 @@
     bool empty() override;
 };
 
+using namespace pldm::pdr;
+/** @brief Parse the State Sensor PDR and return the parsed sensor info which
+ *         will be used to lookup the sensor info in the PlatformEventMessage
+ *         command of sensorEvent type.
+ *
+ *  @param[in] stateSensorPdr - state sensor PDR
+ *
+ *  @return terminus handle, sensor ID and parsed sensor info
+ */
+std::tuple<TerminusHandle, SensorID, SensorInfo>
+    parseStateSensorPDR(const std::vector<uint8_t>& stateSensorPdr);
+
 } // namespace pdr_utils
 } // namespace responder
 } // namespace pldm
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index 5ea71ab..9af13d5 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -316,3 +316,71 @@
     pldm_pdr_destroy(inPDRRepo);
     pldm_pdr_destroy(numericEffecterPdrRepo);
 }
+
+TEST(parseStateSensor, allScenarios)
+{
+    // Sample state sensor with SensorID - 1, EntityType - Processor Module(67)
+    // State Set ID - Operational Running Status(11), Supported States - 3,4
+    std::vector<uint8_t> sample1PDR{0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
+                                    0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00,
+                                    0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x01, 0x0b, 0x00, 0x01, 0x18};
+
+    const auto& [terminusHandle1, sensorID1, sensorInfo1] =
+        parseStateSensorPDR(sample1PDR);
+    const auto& [containerID1, entityType1, entityInstance1] =
+        std::get<0>(sensorInfo1);
+    const auto& states1 = std::get<1>(sensorInfo1);
+    CompositeSensorStates statesCmp1{{3u, 4u}};
+
+    ASSERT_EQ(le16toh(terminusHandle1), 0u);
+    ASSERT_EQ(le16toh(sensorID1), 1u);
+    ASSERT_EQ(le16toh(containerID1), 0u);
+    ASSERT_EQ(le16toh(entityType1), 67u);
+    ASSERT_EQ(le16toh(entityInstance1), 1u);
+    ASSERT_EQ(states1, statesCmp1);
+
+    // Sample state sensor with SensorID - 2, EntityType - System Firmware(31)
+    // State Set ID - Availability(2), Supported States - 3,4,9,10,11,13
+    std::vector<uint8_t> sample2PDR{0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
+                                    0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00,
+                                    0x1F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x01, 0x02, 0x00, 0x02, 0x18, 0x2E};
+
+    const auto& [terminusHandle2, sensorID2, sensorInfo2] =
+        parseStateSensorPDR(sample2PDR);
+    const auto& [containerID2, entityType2, entityInstance2] =
+        std::get<0>(sensorInfo2);
+    const auto& states2 = std::get<1>(sensorInfo2);
+    CompositeSensorStates statesCmp2{{3u, 4u, 9u, 10u, 11u, 13u}};
+
+    ASSERT_EQ(le16toh(terminusHandle2), 0u);
+    ASSERT_EQ(le16toh(sensorID2), 2u);
+    ASSERT_EQ(le16toh(containerID2), 0u);
+    ASSERT_EQ(le16toh(entityType2), 31u);
+    ASSERT_EQ(le16toh(entityInstance2), 1u);
+    ASSERT_EQ(states2, statesCmp2);
+
+    // Sample state sensor with SensorID - 3, EntityType - Virtual Machine
+    // Manager(33), Composite State Sensor -2 , State Set ID - Link State(33),
+    // Supported States - 1,2, State Set ID - Configuration State(15),
+    // Supported States - 1,2,3,4
+    std::vector<uint8_t> sample3PDR{
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x17, 0x00, 0x00,
+        0x00, 0x03, 0x00, 0x21, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x02, 0x21, 0x00, 0x01, 0x06, 0x0F, 0x00, 0x01, 0x1E};
+
+    const auto& [terminusHandle3, sensorID3, sensorInfo3] =
+        parseStateSensorPDR(sample3PDR);
+    const auto& [containerID3, entityType3, entityInstance3] =
+        std::get<0>(sensorInfo3);
+    const auto& states3 = std::get<1>(sensorInfo3);
+    CompositeSensorStates statesCmp3{{1u, 2u}, {1u, 2u, 3u, 4u}};
+
+    ASSERT_EQ(le16toh(terminusHandle3), 0u);
+    ASSERT_EQ(le16toh(sensorID3), 3u);
+    ASSERT_EQ(le16toh(containerID3), 1u);
+    ASSERT_EQ(le16toh(entityType3), 33u);
+    ASSERT_EQ(le16toh(entityInstance3), 2u);
+    ASSERT_EQ(states3, statesCmp3);
+}
diff --git a/types.hpp b/types.hpp
new file mode 100644
index 0000000..e194c94
--- /dev/null
+++ b/types.hpp
@@ -0,0 +1,48 @@
+#pragma once

+

+#include <stdint.h>

+

+#include <set>

+#include <string>

+#include <variant>

+#include <vector>

+

+namespace pldm

+{

+

+namespace dbus

+{

+

+using ObjectPath = std::string;

+using Service = std::string;

+using Interface = std::string;

+using Interfaces = std::vector<std::string>;

+using Property = std::string;

+using PropertyType = std::string;

+using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,

+                           int64_t, uint64_t, double, std::string>;

+

+} // namespace dbus

+

+namespace pdr

+{

+

+using TerminusHandle = uint16_t;

+using TerminusID = uint16_t;

+using SensorID = uint16_t;

+using EntityType = uint16_t;

+using EntityInstance = uint16_t;

+using ContainerID = uint16_t;

+using CompositeCount = uint8_t;

+

+//!< Subset of the State Set that is supported by a effecter/sensor

+using PossibleStates = std::set<uint8_t>;

+//!< Subset of the State Set that is supported by each effecter/sensor in a

+//!< composite efffecter/sensor

+using CompositeSensorStates = std::vector<PossibleStates>;

+using EntityInfo = std::tuple<ContainerID, EntityType, EntityInstance>;

+using SensorInfo = std::tuple<EntityInfo, CompositeSensorStates>;

+

+} // namespace pdr

+

+} // namespace pldm