blob: 9084b7a2fcb5a62f1386db533ed8e78d1edc020f [file] [log] [blame]
#pragma once
#include "config.h"
#include "sensorhandler.hpp"
#include <cmath>
#include <ipmid/api.hpp>
#include <ipmid/types.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/message/types.hpp>
#ifdef FEATURE_SENSORS_CACHE
extern ipmi::sensor::SensorCacheMap sensorCacheMap;
// The signal's message type is 0x04 from DBus spec:
// https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
static constexpr auto msgTypeSignal = 0x04;
#endif
namespace ipmi
{
namespace sensor
{
using Assertion = uint16_t;
using Deassertion = uint16_t;
using AssertionSet = std::pair<Assertion, Deassertion>;
using Service = std::string;
using Path = std::string;
using Interface = std::string;
using ServicePath = std::pair<Path, Service>;
using Interfaces = std::vector<Interface>;
using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
using namespace phosphor::logging;
/** @brief get the D-Bus service and service path
* @param[in] bus - The Dbus bus object
* @param[in] interface - interface to the service
* @param[in] path - interested path in the list of objects
* @return pair of service path and service
*/
ServicePath getServiceAndPath(sdbusplus::bus::bus& bus,
const std::string& interface,
const std::string& path = std::string());
/** @brief Make assertion set from input data
* @param[in] cmdData - Input sensor data
* @return pair of assertion and deassertion set
*/
AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData);
/** @brief send the message to DBus
* @param[in] msg - message to send
* @return failure status in IPMI error code
*/
ipmi_ret_t updateToDbus(IpmiUpdateData& msg);
namespace get
{
/** @brief Populate sensor name from the D-Bus property associated with the
* sensor. In the example entry from the yaml, the name of the D-bus
* property "AttemptsLeft" is the sensor name.
*
* 0x07:
* sensorType: 195
* path: /xyz/openbmc_project/state/host0
* sensorReadingType: 0x6F
* serviceInterface: org.freedesktop.DBus.Properties
* readingType: readingAssertion
* sensorNamePattern: nameProperty
* interfaces:
* xyz.openbmc_project.Control.Boot.RebootAttempts:
* AttemptsLeft:
* Offsets:
* 0xFF:
* type: uint32_t
*
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return On success return the sensor name for the sensor.
*/
inline SensorName nameProperty(const Info& sensorInfo)
{
return sensorInfo.propertyInterfaces.begin()->second.begin()->first;
}
/** @brief Populate sensor name from the D-Bus object associated with the
* sensor. If the object path is /system/chassis/motherboard/dimm0 then
* the leaf dimm0 is considered as the sensor name.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return On success return the sensor name for the sensor.
*/
inline SensorName nameLeaf(const Info& sensorInfo)
{
return sensorInfo.sensorPath.substr(
sensorInfo.sensorPath.find_last_of('/') + 1,
sensorInfo.sensorPath.length());
}
/** @brief Populate sensor name from the D-Bus object associated with the
* sensor and the property.
* If the object path is /xyz/openbmc_project/inventory/Fan0 and
* the property is Present, the leaf Fan0 and the Property is
* joined to Fan0_Present as the sensor name.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return On success return the sensor name for the sensor.
*/
inline SensorName nameLeafProperty(const Info& sensorInfo)
{
return nameLeaf(sensorInfo) + "_" + nameProperty(sensorInfo);
}
/** @brief Populate sensor name from the D-Bus object associated with the
* sensor. If the object path is /system/chassis/motherboard/cpu0/core0
* then the sensor name is cpu0_core0. The leaf and the parent is put
* together to get the sensor name.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return On success return the sensor name for the sensor.
*/
SensorName nameParentLeaf(const Info& sensorInfo);
/**
* @brief Helper function to map the dbus info to sensor's assertion status
* for the get sensor reading command.
*
* @param[in] sensorInfo - Dbus info related to sensor.
* @param[in] path - Dbus object path.
* @param[in] interface - Dbus interface.
*
* @return Response for get sensor reading command.
*/
GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
const InstancePath& path,
const DbusInterface& interface);
#ifndef FEATURE_SENSORS_CACHE
/**
* @brief Map the Dbus info to sensor's assertion status in the Get sensor
* reading command response.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return Response for get sensor reading command.
*/
GetSensorResponse assertion(const Info& sensorInfo);
/**
* @brief Maps the Dbus info to the reading field in the Get sensor reading
* command response.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return Response for get sensor reading command.
*/
GetSensorResponse eventdata2(const Info& sensorInfo);
/**
* @brief readingAssertion is a case where the entire assertion state field
* serves as the sensor value.
*
* @tparam T - type of the dbus property related to sensor.
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return Response for get sensor reading command.
*/
template <typename T>
GetSensorResponse readingAssertion(const Info& sensorInfo)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
GetSensorResponse response{};
enableScanning(&response);
auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
sensorInfo.sensorPath);
auto propValue = ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
sensorInfo.propertyInterfaces.begin()->first,
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
setAssertionBytes(static_cast<uint16_t>(std::get<T>(propValue)), &response);
return response;
}
/** @brief Map the Dbus info to the reading field in the Get sensor reading
* command response
*
* @tparam T - type of the dbus property related to sensor.
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return Response for get sensor reading command.
*/
template <typename T>
GetSensorResponse readingData(const Info& sensorInfo)
{
sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
GetSensorResponse response{};
enableScanning(&response);
auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
sensorInfo.sensorPath);
#ifdef UPDATE_FUNCTIONAL_ON_FAIL
// Check the OperationalStatus interface for functional property
if (sensorInfo.propertyInterfaces.begin()->first ==
"xyz.openbmc_project.Sensor.Value")
{
bool functional = true;
try
{
auto funcValue = ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
"xyz.openbmc_project.State.Decorator.OperationalStatus",
"Functional");
functional = std::get<bool>(funcValue);
}
catch (...)
{
// No-op if Functional property could not be found since this
// check is only valid for Sensor.Value read for hwmonio
}
if (!functional)
{
throw SensorFunctionalError();
}
}
#endif
auto propValue = ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
sensorInfo.propertyInterfaces.begin()->first,
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
double value = std::get<T>(propValue) *
std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
int32_t rawData =
(value - sensorInfo.scaledOffset) / sensorInfo.coefficientM;
constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
constexpr uint8_t signedDataFormat = 0x80;
// if sensorUnits1 [7:6] = 10b, sensor is signed
if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
{
if (rawData > std::numeric_limits<int8_t>::max() ||
rawData < std::numeric_limits<int8_t>::lowest())
{
log<level::ERR>("Value out of range");
throw std::out_of_range("Value out of range");
}
setReading(static_cast<int8_t>(rawData), &response);
}
else
{
if (rawData > std::numeric_limits<uint8_t>::max() ||
rawData < std::numeric_limits<uint8_t>::lowest())
{
log<level::ERR>("Value out of range");
throw std::out_of_range("Value out of range");
}
setReading(static_cast<uint8_t>(rawData), &response);
}
if (!std::isfinite(value))
{
response.readingOrStateUnavailable = 1;
}
bool critAlarmHigh;
try
{
critAlarmHigh = std::get<bool>(ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
"xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalAlarmHigh"));
}
catch (const std::exception& e)
{
critAlarmHigh = false;
}
bool critAlarmLow;
try
{
critAlarmLow = std::get<bool>(ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
"xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalAlarmLow"));
}
catch (const std::exception& e)
{
critAlarmLow = false;
}
bool warningAlarmHigh;
try
{
warningAlarmHigh = std::get<bool>(ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
"xyz.openbmc_project.Sensor.Threshold.Warning",
"WarningAlarmHigh"));
}
catch (const std::exception& e)
{
warningAlarmHigh = false;
}
bool warningAlarmLow;
try
{
warningAlarmLow = std::get<bool>(ipmi::getDbusProperty(
bus, service, sensorInfo.sensorPath,
"xyz.openbmc_project.Sensor.Threshold.Warning", "WarningAlarmlow"));
}
catch (const std::exception& e)
{
warningAlarmLow = false;
}
response.thresholdLevelsStates =
(static_cast<uint8_t>(critAlarmHigh) << 4) |
(static_cast<uint8_t>(warningAlarmHigh) << 3) |
(static_cast<uint8_t>(warningAlarmLow) << 2) |
(static_cast<uint8_t>(critAlarmLow) << 1);
return response;
}
#else
/**
* @brief Map the Dbus info to sensor's assertion status in the Get sensor
* reading command response.
*
* @param[in] id - The sensor id
* @param[in] sensorInfo - Dbus info related to sensor.
* @param[in] msg - Dbus message from match callback.
*
* @return Response for get sensor reading command.
*/
std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
sdbusplus::message::message& msg);
/**
* @brief Maps the Dbus info to the reading field in the Get sensor reading
* command response.
*
* @param[in] id - The sensor id
* @param[in] sensorInfo - Dbus info related to sensor.
* @param[in] msg - Dbus message from match callback.
*
* @return Response for get sensor reading command.
*/
std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo,
sdbusplus::message::message& msg);
/**
* @brief readingAssertion is a case where the entire assertion state field
* serves as the sensor value.
*
* @tparam T - type of the dbus property related to sensor.
* @param[in] id - The sensor id
* @param[in] sensorInfo - Dbus info related to sensor.
* @param[in] msg - Dbus message from match callback.
*
* @return Response for get sensor reading command.
*/
template <typename T>
std::optional<GetSensorResponse>
readingAssertion(uint8_t id, const Info& sensorInfo,
sdbusplus::message::message& msg)
{
auto type = msg.get_type();
if (type == msgTypeSignal)
{
// This is signal callback
std::string interfaceName;
msg.read(interfaceName);
if (interfaceName != sensorInfo.sensorInterface)
{
// Not the interface we need
return {};
}
}
// Now the message only contains the properties.
GetSensorResponse response{};
std::map<std::string, ipmi::Value> properties;
enableScanning(&response);
msg.read(properties);
auto iter = properties.find(
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
if (iter == properties.end())
{
return {};
}
setAssertionBytes(static_cast<uint16_t>(std::get<T>(iter->second)),
&response);
if (!sensorCacheMap[id].has_value())
{
sensorCacheMap[id] = SensorData{};
}
sensorCacheMap[id]->response = response;
return response;
}
/** @brief Get sensor reading from the dbus message from match
*
* @tparam T - type of the dbus property related to sensor.
* @param[in] id - The sensor id
* @param[in] sensorInfo - Dbus info related to sensor.
* @param[in] msg - Dbus message from match callback.
*
* @return Response for get sensor reading command.
*/
template <typename T>
std::optional<GetSensorResponse> readingData(uint8_t id, const Info& sensorInfo,
sdbusplus::message::message& msg)
{
std::map<std::string, ipmi::Value> properties;
auto type = msg.get_type();
if (type == msgTypeSignal)
{
// This is signal callback
std::string interfaceName;
msg.read(interfaceName);
if (interfaceName ==
"xyz.openbmc_project.State.Decorator.OperationalStatus")
{
msg.read(properties);
auto val = properties.find("Functional");
if (val != properties.end())
{
sensorCacheMap[id]->functional = std::get<bool>(val->second);
}
return {};
}
if (interfaceName == "xyz.openbmc_project.State.Decorator.Availability")
{
msg.read(properties);
auto val = properties.find("Available");
if (val != properties.end())
{
sensorCacheMap[id]->available = std::get<bool>(val->second);
}
return {};
}
if (interfaceName != sensorInfo.sensorInterface)
{
// Not the interface we need
return {};
}
#ifdef UPDATE_FUNCTIONAL_ON_FAIL
if (sensorCacheMap[id])
{
if (!sensorCacheMap[id]->functional)
{
throw SensorFunctionalError();
}
}
#endif
}
// Now the message only contains the properties.
GetSensorResponse response{};
enableScanning(&response);
msg.read(properties);
auto iter = properties.find(
sensorInfo.propertyInterfaces.begin()->second.begin()->first);
if (iter == properties.end())
{
return {};
}
double value = std::get<T>(iter->second) *
std::pow(10, sensorInfo.scale - sensorInfo.exponentR);
int32_t rawData =
(value - sensorInfo.scaledOffset) / sensorInfo.coefficientM;
constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
constexpr uint8_t signedDataFormat = 0x80;
// if sensorUnits1 [7:6] = 10b, sensor is signed
if ((sensorInfo.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
{
if (rawData > std::numeric_limits<int8_t>::max() ||
rawData < std::numeric_limits<int8_t>::lowest())
{
log<level::ERR>("Value out of range");
throw std::out_of_range("Value out of range");
}
setReading(static_cast<int8_t>(rawData), &response);
}
else
{
if (rawData > std::numeric_limits<uint8_t>::max() ||
rawData < std::numeric_limits<uint8_t>::lowest())
{
log<level::ERR>("Value out of range");
throw std::out_of_range("Value out of range");
}
setReading(static_cast<uint8_t>(rawData), &response);
}
if (!std::isfinite(value))
{
response.readingOrStateUnavailable = 1;
}
if (!sensorCacheMap[id].has_value())
{
sensorCacheMap[id] = SensorData{};
}
sensorCacheMap[id]->response = response;
return response;
}
#endif // FEATURE_SENSORS_CACHE
} // namespace get
namespace set
{
/** @brief Make a DBus message for a Dbus call
* @param[in] updateInterface - Interface name
* @param[in] sensorPath - Path of the sensor
* @param[in] command - command to be executed
* @param[in] sensorInterface - DBus interface of sensor
* @return a dbus message
*/
IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
const std::string& sensorPath,
const std::string& command,
const std::string& sensorInterface);
/** @brief Update d-bus based on assertion type sensor data
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
const Info& sensorInfo);
/** @brief Update d-bus based on a reading assertion
* @tparam T - type of d-bus property mapping this sensor
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
template <typename T>
ipmi_ret_t readingAssertion(const SetSensorReadingReq& cmdData,
const Info& sensorInfo)
{
auto msg =
makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
"Set", sensorInfo.sensorInterface);
const auto& interface = sensorInfo.propertyInterfaces.begin();
msg.append(interface->first);
for (const auto& property : interface->second)
{
msg.append(property.first);
std::variant<T> value = static_cast<T>((cmdData.assertOffset8_14 << 8) |
cmdData.assertOffset0_7);
msg.append(value);
}
return updateToDbus(msg);
}
/** @brief Update d-bus based on a discrete reading
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return an IPMI error code
*/
template <typename T>
ipmi_ret_t readingData(const SetSensorReadingReq& cmdData,
const Info& sensorInfo)
{
T raw_value =
(sensorInfo.coefficientM * cmdData.reading) + sensorInfo.scaledOffset;
raw_value *= std::pow(10, sensorInfo.exponentR - sensorInfo.scale);
auto msg =
makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
"Set", sensorInfo.sensorInterface);
const auto& interface = sensorInfo.propertyInterfaces.begin();
msg.append(interface->first);
for (const auto& property : interface->second)
{
msg.append(property.first);
std::variant<T> value = raw_value;
msg.append(value);
}
return updateToDbus(msg);
}
/** @brief Update d-bus based on eventdata type sensor data
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
uint8_t data);
/** @brief Update d-bus based on eventdata1 type sensor data
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
inline ipmi_ret_t eventdata1(const SetSensorReadingReq& cmdData,
const Info& sensorInfo)
{
return eventdata(cmdData, sensorInfo, cmdData.eventData1);
}
/** @brief Update d-bus based on eventdata2 type sensor data
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
inline ipmi_ret_t eventdata2(const SetSensorReadingReq& cmdData,
const Info& sensorInfo)
{
return eventdata(cmdData, sensorInfo, cmdData.eventData2);
}
/** @brief Update d-bus based on eventdata3 type sensor data
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
inline ipmi_ret_t eventdata3(const SetSensorReadingReq& cmdData,
const Info& sensorInfo)
{
return eventdata(cmdData, sensorInfo, cmdData.eventData3);
}
} // namespace set
namespace notify
{
/** @brief Make a DBus message for a Dbus call
* @param[in] updateInterface - Interface name
* @param[in] sensorPath - Path of the sensor
* @param[in] command - command to be executed
* @param[in] sensorInterface - DBus interface of sensor
* @return a dbus message
*/
IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
const std::string& sensorPath,
const std::string& command,
const std::string& sensorInterface);
/** @brief Update d-bus based on assertion type sensor data
* @param[in] interfaceMap - sensor interface
* @param[in] cmdData - input sensor data
* @param[in] sensorInfo - sensor d-bus info
* @return a IPMI error code
*/
ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
const Info& sensorInfo);
} // namespace notify
namespace inventory
{
namespace get
{
/**
* @brief Map the Dbus info to sensor's assertion status in the Get sensor
* reading command response.
*
* @param[in] sensorInfo - Dbus info related to sensor.
*
* @return Response for get sensor reading command.
*/
GetSensorResponse assertion(const Info& sensorInfo);
} // namespace get
} // namespace inventory
} // namespace sensor
} // namespace ipmi