Support Get Sensor Thresholds Command

Adding support for sensor thresholds command

Resolves openbmc/openbmc#2624

Change-Id: I904c1b18c8709bceb7ecb7eec6e8e42e1f51525a
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/sensorhandler.cpp b/sensorhandler.cpp
index e6320be..de3c228 100644
--- a/sensorhandler.cpp
+++ b/sensorhandler.cpp
@@ -666,6 +666,141 @@
     return rc;
 }
 
+ipmi_ret_t ipmi_sen_get_sensor_thresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+        ipmi_request_t request, ipmi_response_t response,
+        ipmi_data_len_t data_len, ipmi_context_t context)
+{
+    constexpr auto warningThresholdInterface =
+        "xyz.openbmc_project.Sensor.Threshold.Warning";
+    constexpr auto criticalThresholdInterface =
+        "xyz.openbmc_project.Sensor.Threshold.Critical";
+    constexpr auto valueInterface =
+        "xyz.openbmc_project.Sensor.Value";
+    constexpr auto sensorRoot = "/xyz/openbmc_project/sensors";
+
+    ipmi::sensor::Thresholds thresholds =
+    {
+        {
+            warningThresholdInterface,
+            {
+                {
+                    "WarningLow",
+                    ipmi::sensor::ThresholdMask::NON_CRITICAL_LOW_MASK,
+                    ipmi::sensor::ThresholdIndex::NON_CRITICAL_LOW_IDX
+                },
+                {
+                    "WarningHigh",
+                    ipmi::sensor::ThresholdMask::NON_CRITICAL_HIGH_MASK,
+                    ipmi::sensor::ThresholdIndex::NON_CRITICAL_HIGH_IDX
+                }
+            }
+        },
+        {
+            criticalThresholdInterface,
+            {
+               {
+                    "CriticalLow",
+                    ipmi::sensor::ThresholdMask::CRITICAL_LOW_MASK,
+                    ipmi::sensor::ThresholdIndex::CRITICAL_LOW_IDX
+                },
+                {
+                    "CriticalHigh",
+                    ipmi::sensor::ThresholdMask::CRITICAL_HIGH_MASK,
+                    ipmi::sensor::ThresholdIndex::CRITICAL_HIGH_IDX
+                }
+            }
+        }
+    };
+
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+
+    if (*data_len != sizeof(uint8_t))
+    {
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+    auto sensorNum = *(reinterpret_cast<uint8_t*>(request));
+
+    auto responseData =
+        reinterpret_cast<get_sdr::GetSensorThresholdsResponse*>(response);
+
+    responseData->validMask = 0;
+
+    const auto iter = sensors.find(sensorNum);
+    if (iter == sensors.end())
+    {
+        return IPMI_CC_SENSOR_INVALID;
+    }
+
+    const auto sensorInfo = iter->second;
+
+    //Proceed only if the sensor value interface is implemented.
+    if (sensorInfo.propertyInterfaces.find(valueInterface) ==
+        sensorInfo.propertyInterfaces.end())
+    {
+        //return with valid mask as 0
+        return IPMI_CC_OK;
+    }
+
+    std::string service;
+    try
+    {
+        service = ipmi::getService(bus,
+                                   sensorInfo.sensorInterface,
+                                   sensorInfo.sensorPath);
+    }
+    catch (const std::runtime_error& e)
+    {
+        log<level::ERR>(e.what());
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    //prevent divide by 0
+    auto coefficientM =
+        sensorInfo.coefficientM ? sensorInfo.coefficientM : 1;
+
+    try
+    {
+        auto mngObjects = ipmi::getManagedObjects(bus,
+                          service,
+                          sensorRoot);
+
+        auto senIter = mngObjects.find(sensorInfo.sensorPath);
+        if (senIter == mngObjects.end())
+        {
+            return IPMI_CC_SENSOR_INVALID;
+        }
+
+        for (const auto& threshold : thresholds)
+        {
+            auto thresholdType = senIter->second.find(threshold.first);
+            if (thresholdType != senIter->second.end())
+            {
+                for (const auto& threshLevel : threshold.second)
+                {
+                    auto val = thresholdType->
+                        second[threshLevel.property].get<int64_t>();
+                    if (val != 0)
+                    {
+                        auto idx = static_cast<uint8_t>(threshLevel.idx);
+                        responseData->data[idx] = static_cast<uint8_t>(
+                            (val - sensorInfo.scaledOffset) / coefficientM);
+                        responseData->validMask |=
+                            static_cast<uint8_t>(threshLevel.maskValue);
+                    }
+                }
+            }
+        }
+    }
+    catch (InternalFailure& e)
+    {
+        //Not able to get the values, reset the mask.
+        responseData->validMask = 0;
+    }
+
+    *data_len = sizeof(get_sdr::GetSensorThresholdsResponse);
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                              ipmi_request_t request, ipmi_response_t response,
                              ipmi_data_len_t data_len, ipmi_context_t context)
@@ -987,5 +1122,10 @@
                            nullptr, ipmi_sen_get_sdr,
                            PRIVILEGE_USER);
 
+    // <Get Sensor Thresholds>
+    ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_THRESHOLDS,
+                           nullptr, ipmi_sen_get_sensor_thresholds,
+                           PRIVILEGE_USER);
+
     return;
 }
diff --git a/sensorhandler.h b/sensorhandler.h
index 8995447..1cf43ca 100644
--- a/sensorhandler.h
+++ b/sensorhandler.h
@@ -13,6 +13,7 @@
     IPMI_CMD_GET_SENSOR_READING = 0x2D,
     IPMI_CMD_GET_SENSOR_TYPE    = 0x2F,
     IPMI_CMD_SET_SENSOR         = 0x30,
+    IPMI_CMD_GET_SENSOR_THRESHOLDS = 0x27,
 };
 
 // Discrete sensor types.
@@ -206,6 +207,16 @@
 
 } // namespace key
 
+/** @struct GetSensorThresholdsResponse
+ *
+ *  Response structure for Get Sensor Thresholds command
+ */
+struct GetSensorThresholdsResponse
+{
+    uint8_t validMask; //Indicates which values are valid
+    uint8_t data[6];   //Container for threshold values
+} __attribute__((packed));
+
 // Body - full record
 #define FULL_RECORD_ID_STR_MAX_LENGTH 16
 struct SensorDataFullRecordBody
diff --git a/types.hpp b/types.hpp
index 260deb5..61f3d68 100644
--- a/types.hpp
+++ b/types.hpp
@@ -27,6 +27,11 @@
 
 using InterfaceList = std::vector<std::string>;
 
+using DbusInterfaceMap = std::map<DbusInterface, PropertyMap>;
+
+using ObjectValueTree =
+    std::map<sdbusplus::message::object_path, DbusInterfaceMap>;
+
 namespace sensor
 {
 
@@ -183,6 +188,34 @@
 
 using InvObjectIDMap = std::map<InventoryPath, SelData>;
 
+enum class ThresholdMask
+{
+    NON_CRITICAL_LOW_MASK = 0x01,
+    CRITICAL_LOW_MASK = 0x02,
+    NON_CRITICAL_HIGH_MASK = 0x08,
+    CRITICAL_HIGH_MASK = 0x10,
+};
+
+enum class ThresholdIndex
+{
+    NON_CRITICAL_LOW_IDX = 0,
+    CRITICAL_LOW_IDX = 1,
+    NON_RECOVERABLE_LOW_IDX = 2,
+    NON_CRITICAL_HIGH_IDX = 3,
+    CRITICAL_HIGH_IDX = 4,
+    NON_RECOVERABLE_HIGH_IDX = 5,
+};
+
+struct ThresholdLevel
+{
+    std::string property;
+    ThresholdMask maskValue;
+    ThresholdIndex idx;
+};
+
+using SensorThresholds = std::vector<ThresholdLevel>;
+using Thresholds = std::map<std::string, SensorThresholds>;
+
 }// namespace sensor
 
 namespace network
diff --git a/utils.cpp b/utils.cpp
index fa6bd04..bd8fade 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -198,6 +198,31 @@
     return properties;
 }
 
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+                                 const std::string& service,
+                                 const std::string& objPath)
+{
+    ipmi::ObjectValueTree interfaces;
+
+    auto method = bus.new_method_call(
+                      service.c_str(),
+                      objPath.c_str(),
+                      "org.freedesktop.DBus.ObjectManager",
+                      "GetManagedObjects");
+
+    auto reply = bus.call(method);
+
+    if (reply.is_method_error())
+    {
+         log<level::ERR>("Failed to get managed objects",
+                         entry("PATH=%s", objPath.c_str()));
+         elog<InternalFailure>();
+    }
+
+    reply.read(interfaces);
+    return interfaces;
+}
+
 void setDbusProperty(sdbusplus::bus::bus& bus,
                      const std::string& service,
                      const std::string& objPath,
diff --git a/utils.hpp b/utils.hpp
index 1699c52..b4f090f 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -87,6 +87,17 @@
                                  const std::string& objPath,
                                  const std::string& interface);
 
+/** @brief Gets all managed objects associated with the given object
+ *         path and service.
+ *  @param[in] bus - D-Bus Bus Object.
+ *  @param[in] service - D-Bus service name.
+ *  @param[in] objPath - D-Bus object path.
+ *  @return On success returns the map of name value pair.
+ */
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+                                 const std::string& service,
+                                 const std::string& objPath);
+
 /** @brief Sets the property value of the given object.
  *  @param[in] bus - DBUS Bus Object.
  *  @param[in] service - Dbus service name.