Add Intel-specific IPMI sensor commands
Also includes SDR storage commands that are required to support
the 'ipmitool sensor list' command.
Change-Id: Id1830097d93882114085fce723f0b92367b2db48
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/src/sensorcommands.cpp b/src/sensorcommands.cpp
new file mode 100644
index 0000000..3c1f050
--- /dev/null
+++ b/src/sensorcommands.cpp
@@ -0,0 +1,1228 @@
+/*
+// Copyright (c) 2017 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <host-ipmid/ipmid-api.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/container/flat_map.hpp>
+#include <chrono>
+#include <cmath>
+#include <commandutils.hpp>
+#include <iostream>
+#include <phosphor-ipmi-host/utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdrutils.hpp>
+#include <sensorcommands.hpp>
+#include <sensorutils.hpp>
+#include <storagecommands.hpp>
+#include <string>
+
+namespace ipmi
+{
+using ManagedObjectType =
+ std::map<sdbusplus::message::object_path,
+ std::map<std::string, std::map<std::string, DbusVariant>>>;
+
+using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
+
+static constexpr int sensorListUpdatePeriod = 10;
+static constexpr int sensorMapUpdatePeriod = 2;
+
+constexpr size_t maxSDRTotalSize =
+ 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
+constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
+
+static uint16_t sdrReservationID;
+static uint32_t sdrLastAdd = noTimestamp;
+static uint32_t sdrLastRemove = noTimestamp;
+
+static SensorSubTree sensorTree;
+static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
+
+const static boost::container::flat_map<const char *, SensorUnits> sensorUnits{
+ {{"temperature", SensorUnits::degreesC},
+ {"voltage", SensorUnits::volts},
+ {"current", SensorUnits::amps},
+ {"fan_tach", SensorUnits::rpm},
+ {"power", SensorUnits::watts}}};
+
+void registerSensorFunctions() __attribute__((constructor));
+static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
+
+static sdbusplus::bus::match::match sensorAdded(
+ dbus,
+ "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
+ "sensors/'",
+ [](sdbusplus::message::message &m) {
+ sensorTree.clear();
+ sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ });
+
+static sdbusplus::bus::match::match sensorRemoved(
+ dbus,
+ "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
+ "sensors/'",
+ [](sdbusplus::message::message &m) {
+ sensorTree.clear();
+ sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ });
+
+static void
+ getSensorMaxMin(const std::map<std::string, DbusVariant> &sensorPropertyMap,
+ double &max, double &min)
+{
+ auto maxMap = sensorPropertyMap.find("MaxValue");
+ auto minMap = sensorPropertyMap.find("MinValue");
+ max = 127;
+ min = -128;
+
+ if (maxMap != sensorPropertyMap.end())
+ {
+ max = apply_visitor(VariantToDoubleVisitor(), maxMap->second);
+ }
+ if (minMap != sensorPropertyMap.end())
+ {
+ min = apply_visitor(VariantToDoubleVisitor(), minMap->second);
+ }
+}
+
+static ipmi_ret_t getSensorConnection(uint8_t sensnum, std::string &connection,
+ std::string &path)
+{
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (sensorTree.size() < (sensnum + 1))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ uint8_t sensorIndex = sensnum;
+ for (const auto &sensor : sensorTree)
+ {
+ if (sensorIndex-- == 0)
+ {
+ if (!sensor.second.size())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ connection = sensor.second.begin()->first;
+ path = sensor.first;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
+ SensorMap &sensorMap)
+{
+ static boost::container::flat_map<
+ std::string, std::chrono::time_point<std::chrono::steady_clock>>
+ updateTimeMap;
+
+ auto updateFind = updateTimeMap.find(sensorConnection);
+ auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
+ if (updateFind != updateTimeMap.end())
+ {
+ lastUpdate = updateFind->second;
+ }
+
+ auto now = std::chrono::steady_clock::now();
+
+ if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
+ .count() > sensorMapUpdatePeriod)
+ {
+ updateTimeMap[sensorConnection] = now;
+
+ auto managedObj = dbus.new_method_call(
+ sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+
+ ManagedObjectType managedObjects;
+ try
+ {
+ auto reply = dbus.call(managedObj);
+ reply.read(managedObjects);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error getting managed objects from connection",
+ phosphor::logging::entry("CONNECTION=%s",
+ sensorConnection.c_str()));
+ return false;
+ }
+
+ SensorCache[sensorConnection] = managedObjects;
+ }
+ auto connection = SensorCache.find(sensorConnection);
+ if (connection == SensorCache.end())
+ {
+ return false;
+ }
+ auto path = connection->second.find(sensorPath);
+ if (path == connection->second.end())
+ {
+ return false;
+ }
+ sensorMap = path->second;
+
+ return true;
+}
+
+/* sensor commands */
+ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ *dataLen = 0;
+ printCommand(+netfn, +cmd);
+ return IPMI_CC_INVALID;
+}
+
+ipmi_ret_t ipmiSenGetSensorReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorObject == sensorMap.end() ||
+ sensorObject->second.find("Value") == sensorObject->second.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ auto &value = sensorObject->second["Value"];
+ double reading = apply_visitor(VariantToDoubleVisitor(), value);
+
+ double max;
+ double min;
+ getSensorMaxMin(sensorObject->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ SensorReadingResp *msgReply = static_cast<SensorReadingResp *>(response);
+ *dataLen = sizeof(SensorReadingResp);
+
+ msgReply->value =
+ scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
+ msgReply->operation =
+ static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
+ msgReply->indication[0] = 0; // ignore for non-threshold sensors
+ msgReply->indication[1] = 0;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 8)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0;
+
+ SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
+
+ // upper two bits reserved
+ if (req->mask & 0xC0)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ // lower nc and upper nc not suppported on any sensor
+ if ((req->mask & static_cast<uint8_t>(
+ SensorThresholdReqEnable::setLowerNonRecoverable)) ||
+ (req->mask & static_cast<uint8_t>(
+ SensorThresholdReqEnable::setUpperNonRecoverable)))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ // if no bits are set in the mask, nothing to do
+ if (!(req->mask))
+ {
+ return IPMI_CC_OK;
+ }
+
+ std::string connection;
+ std::string path;
+
+ ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorObject == sensorMap.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ double max = 0;
+ double min = 0;
+ getSensorMaxMin(sensorObject->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ bool setLowerCritical =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
+ bool setUpperCritical =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
+
+ bool setLowerWarning =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
+ bool setUpperWarning =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
+
+ // store a vector of property name, value to set, and interface
+ std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
+
+ // define the indexes of the tuple
+ constexpr uint8_t propertyName = 0;
+ constexpr uint8_t thresholdValue = 1;
+ constexpr uint8_t interface = 2;
+ // verifiy all needed fields are present
+ if (setLowerCritical || setUpperCritical)
+ {
+ auto findThreshold =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+ if (findThreshold == sensorMap.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (setLowerCritical)
+ {
+ auto findLower = findThreshold->second.find("CriticalLow");
+ if (findLower == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
+ findThreshold->first);
+ }
+ if (setUpperCritical)
+ {
+ auto findUpper = findThreshold->second.find("CriticalHigh");
+ if (findUpper == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
+ findThreshold->first);
+ }
+ }
+ if (setLowerWarning || setUpperWarning)
+ {
+ auto findThreshold =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ if (findThreshold == sensorMap.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (setLowerWarning)
+ {
+ auto findLower = findThreshold->second.find("WarningLow");
+ if (findLower == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
+ findThreshold->first);
+ }
+ if (setUpperWarning)
+ {
+ auto findUpper = findThreshold->second.find("WarningHigh");
+ if (findUpper == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
+ findThreshold->first);
+ }
+ }
+
+ for (const auto &property : thresholdsToSet)
+ {
+ // from section 36.3 in the IPMI Spec, assume all linear
+ double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
+ (bValue * std::pow(10, bExp))) *
+ std::pow(10, rExp);
+ setDbusProperty(dbus, connection, path, std::get<interface>(property),
+ std::get<propertyName>(property),
+ ipmi::Value(valueToSet));
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorThresholdResp), 0);
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorPair == sensorMap.end())
+ {
+ // should not have been able to find a sensor not implementing
+ // the sensor object
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ double max;
+ double min;
+ getSensorMaxMin(sensorPair->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto msgReply = static_cast<SensorThresholdResp *>(response);
+
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningHigh");
+ auto warningLow = warningMap.find("WarningLow");
+
+ if (warningHigh != warningMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(
+ IPMIhresholdRespBits::upperNonCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ warningHigh->second);
+ msgReply->uppernc = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ if (warningLow != warningMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(
+ IPMIhresholdRespBits::lowerNonCritical);
+ double value =
+ apply_visitor(VariantToDoubleVisitor(), warningLow->second);
+ msgReply->lowernc = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalHigh");
+ auto criticalLow = criticalMap.find("CriticalLow");
+
+ if (criticalHigh != criticalMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(IPMIhresholdRespBits::upperCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ criticalHigh->second);
+ msgReply->uppercritical = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(IPMIhresholdRespBits::lowerCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ criticalLow->second);
+ msgReply->lowercritical = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ }
+ }
+
+ *dataLen = sizeof(SensorThresholdResp);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
+ 0);
+
+ // assume all threshold sensors
+ auto resp = static_cast<SensorEventEnableResp *>(response);
+
+ resp->enabled = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::sensorScanningEnable);
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningHigh");
+ auto warningLow = warningMap.find("WarningLow");
+ if (warningHigh != warningMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
+ }
+ if (warningLow != warningMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalHigh");
+ auto criticalLow = criticalMap.find("CriticalLow");
+
+ if (criticalHigh != criticalMap.end())
+ {
+ resp->assertionEnabledMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
+ resp->deassertionEnabledMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingLow);
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
+ }
+ }
+ *dataLen =
+ sizeof(SensorEventEnableResp); // todo only return needed bytes
+ }
+ // no thresholds enabled
+ else
+ {
+ *dataLen = 1;
+ auto resp = static_cast<uint8_t *>(response);
+ *resp = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::eventMessagesEnable);
+ *resp |= static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::sensorScanningEnable);
+ }
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
+ auto resp = static_cast<SensorEventStatusResp *>(response);
+ resp->enabled =
+ static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ resp->enabled = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::eventMessagesEnable);
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningAlarmHigh");
+ auto warningLow = warningMap.find("WarningAlarmLow");
+ auto warningHighAlarm = false;
+ auto warningLowAlarm = false;
+
+ if (warningHigh != warningMap.end())
+ {
+ warningHighAlarm = warningHigh->second.get<bool>();
+ }
+ if (warningLow != warningMap.end())
+ {
+ warningLowAlarm = warningLow->second.get<bool>();
+ }
+ if (warningHighAlarm)
+ {
+ resp->assertionsLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
+ }
+ if (warningLowAlarm)
+ {
+ resp->assertionsLSB |= 1; // lower nc going low
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
+ auto criticalLow = criticalMap.find("CriticalAlarmLow");
+ auto criticalHighAlarm = false;
+ auto criticalLowAlarm = false;
+
+ if (criticalHigh != criticalMap.end())
+ {
+ criticalHighAlarm = criticalHigh->second.get<bool>();
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ criticalLowAlarm = criticalLow->second.get<bool>();
+ }
+ if (criticalHighAlarm)
+ {
+ resp->assertionsMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
+ }
+ if (criticalLowAlarm)
+ {
+ resp->assertionsLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
+ }
+ }
+ *dataLen = sizeof(SensorEventStatusResp);
+ }
+
+ // no thresholds enabled, don't need assertionMSB
+ else
+ {
+ *dataLen = sizeof(SensorEventStatusResp) - 1;
+ }
+
+ return IPMI_CC_OK;
+}
+
+/* end sensor commands */
+
+/* storage commands */
+
+ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
+
+ auto resp = static_cast<GetSDRInfoResp *>(response);
+ resp->sdrVersion = ipmiSdrVersion;
+ uint16_t recordCount = sensorTree.size();
+
+ // todo: for now, sdr count is number of sensors
+ resp->recordCountLS = recordCount & 0xFF;
+ resp->recordCountMS = recordCount >> 8;
+
+ // free space unspcified
+ resp->freeSpace[0] = 0xFF;
+ resp->freeSpace[1] = 0xFF;
+
+ resp->mostRecentAddition = sdrLastAdd;
+ resp->mostRecentErase = sdrLastRemove;
+ resp->operationSupport = static_cast<uint8_t>(
+ SdrRepositoryInfoOps::overflow); // write not supported
+ resp->operationSupport |=
+ static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
+ resp->operationSupport |= static_cast<uint8_t>(
+ SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
+ *dataLen = sizeof(GetSDRInfoResp);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+ GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
+
+ // 0000h unspecified number of alloc units
+ resp->allocUnitsLSB = 0;
+ resp->allocUnitsMSB = 0;
+
+ // max unit size is size of max record
+ resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
+ resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
+ // read only sdr, no free alloc blocks
+ resp->allocUnitFreeLSB = 0;
+ resp->allocUnitFreeMSB = 0;
+ resp->allocUnitLargestFreeLSB = 0;
+ resp->allocUnitLargestFreeMSB = 0;
+ // only allow one block at a time
+ resp->maxRecordSize = 1;
+
+ *dataLen = sizeof(GetAllocInfoResp);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+ sdrReservationID++;
+ *dataLen = 2;
+ auto resp = static_cast<uint8_t *>(response);
+ resp[0] = sdrReservationID & 0xFF;
+ resp[1] = sdrReservationID >> 8;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen != 6)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ auto requestedSize = *dataLen;
+ *dataLen = 0; // default to 0 in case of an error
+
+ constexpr uint16_t lastRecordIndex = 0xFFFF;
+ auto req = static_cast<GetSDRReq *>(request);
+
+ // reservation required for partial reads with non zero offset into
+ // record
+ if (req->reservationID != sdrReservationID && req->offset)
+ {
+ return IPMI_CC_INVALID_RESERVATION_ID;
+ }
+
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ size_t fruCount = 0;
+ ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+
+ size_t lastRecord = sensorTree.size() + fruCount - 1;
+ if (req->recordID == lastRecordIndex)
+ {
+ req->recordID = lastRecord;
+ }
+ if (req->recordID > lastRecord)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ uint16_t nextRecord =
+ lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
+
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + requestedSize, 0);
+
+ auto resp = static_cast<get_sdr::GetSdrResp *>(response);
+ resp->next_record_id_lsb = nextRecord & 0xFF;
+ resp->next_record_id_msb = nextRecord >> 8;
+
+ if (req->recordID >= sensorTree.size())
+ {
+ size_t fruIndex = req->recordID - sensorTree.size();
+ if (fruIndex >= fruCount)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ get_sdr::SensorDataFruRecord data;
+ if (req->offset > sizeof(data))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ ret = ipmi::storage::getFruSdrs(fruIndex, data);
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+ data.header.record_id_msb = req->recordID << 8;
+ data.header.record_id_lsb = req->recordID & 0xFF;
+ if (sizeof(data) < (req->offset + req->bytesToRead))
+ {
+ req->bytesToRead = sizeof(data) - req->offset;
+ }
+ *dataLen = req->bytesToRead + 2; // next record
+ std::memcpy(&resp->record_data, (char *)&data + req->offset,
+ req->bytesToRead);
+ return IPMI_CC_OK;
+ }
+
+ std::string connection;
+ std::string path;
+ uint16_t sensorIndex = req->recordID;
+ for (const auto &sensor : sensorTree)
+ {
+ if (sensorIndex-- == 0)
+ {
+ if (!sensor.second.size())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ connection = sensor.second.begin()->first;
+ path = sensor.first;
+ break;
+ }
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ uint8_t sensornumber = (req->recordID & 0xFF);
+ get_sdr::SensorDataFullRecord record = {0};
+
+ record.header.record_id_msb = req->recordID << 8;
+ record.header.record_id_lsb = req->recordID & 0xFF;
+ record.header.sdr_version = ipmiSdrVersion;
+ record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
+ record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
+ sizeof(get_sdr::SensorDataRecordHeader);
+ record.key.owner_id = 0x20;
+ record.key.owner_lun = 0x0;
+ record.key.sensor_number = sensornumber;
+
+ record.body.entity_id = 0x0;
+ record.body.entity_instance = 0x01;
+ record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
+ record.body.sensor_type = getSensorTypeFromPath(path);
+ std::string type = getSensorTypeStringFromPath(path);
+ auto typeCstr = type.c_str();
+ auto findUnits = sensorUnits.find(typeCstr);
+ if (findUnits != sensorUnits.end())
+ {
+ record.body.sensor_units_2_base =
+ static_cast<uint8_t>(findUnits->second);
+ } // else default 0x0 unspecified
+
+ record.body.event_reading_type = getSensorEventTypeFromPath(path);
+
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+ if (sensorObject == sensorMap.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto maxObject = sensorObject->second.find("MaxValue");
+ auto minObject = sensorObject->second.find("MinValue");
+ double max = 128;
+ double min = -127;
+ if (maxObject != sensorObject->second.end())
+ {
+ max = apply_visitor(VariantToDoubleVisitor(), maxObject->second);
+ }
+
+ if (minObject != sensorObject->second.end())
+ {
+ min = apply_visitor(VariantToDoubleVisitor(), minObject->second);
+ }
+
+ int16_t mValue;
+ int8_t rExp;
+ int16_t bValue;
+ int8_t bExp;
+ bool bSigned;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
+ record.body.m_lsb = mValue & 0xFF;
+
+ // move the smallest bit of the MSB into place (bit 9)
+ // the MSbs are bits 7:8 in m_msb_and_tolerance
+ uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
+
+ // assign the negative
+ if (mValue < 0)
+ {
+ mMsb |= (1 << 7);
+ }
+ record.body.m_msb_and_tolerance = mMsb;
+
+ record.body.b_lsb = bValue & 0xFF;
+
+ // move the smallest bit of the MSB into place
+ // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
+ uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
+
+ // assign the negative
+ if (bValue < 0)
+ {
+ bMsb |= (1 << 7);
+ }
+ record.body.b_msb_and_accuracy_lsb = bMsb;
+
+ record.body.r_b_exponents = bExp & 0x7;
+ if (bExp < 0)
+ {
+ record.body.r_b_exponents |= 1 << 3;
+ }
+ record.body.r_b_exponents = (rExp & 0x7) << 4;
+ if (rExp < 0)
+ {
+ record.body.r_b_exponents |= 1 << 7;
+ }
+
+ // todo fill out rest of units
+ if (bSigned)
+ {
+ record.body.sensor_units_1 = 1 << 7;
+ }
+
+ // populate sensor name from path
+ std::string name;
+ size_t nameStart = path.rfind("/");
+ if (nameStart != std::string::npos)
+ {
+ name = path.substr(nameStart + 1, std::string::npos - nameStart);
+ }
+
+ std::replace(name.begin(), name.end(), '_', ' ');
+ if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
+ {
+ name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
+ }
+ record.body.id_string_info = name.size();
+ std::strncpy(record.body.id_string, name.c_str(),
+ sizeof(record.body.id_string));
+
+ if (sizeof(get_sdr::SensorDataFullRecord) <
+ (req->offset + req->bytesToRead))
+ {
+ req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
+ }
+
+ *dataLen =
+ 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
+
+ std::memcpy(&resp->record_data, (char *)&record + req->offset,
+ req->bytesToRead);
+
+ return IPMI_CC_OK;
+}
+/* end storage commands */
+
+void registerSensorFunctions()
+{
+ // get firmware version information
+ ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
+ ipmiSensorWildcardHandler, PRIVILEGE_USER);
+
+ // <Get Sensor Type>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
+ nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
+
+ // <Set Sensor Reading and Event Status>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
+ nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
+
+ // <Get Sensor Reading>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
+ nullptr, ipmiSenGetSensorReading, PRIVILEGE_USER);
+
+ // <Get Sensor Threshold>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
+ nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
+ nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
+
+ // <Get Sensor Event Enable>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
+ nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
+
+ // <Get Sensor Event Status>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
+ nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
+
+ // register all storage commands for both Sensor and Storage command
+ // versions
+
+ // <Get SDR Repository Info>
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
+ nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
+
+ // <Get SDR Allocation Info>
+ ipmiPrintAndRegister(NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
+ nullptr, ipmiStorageGetSDRAllocationInfo,
+ PRIVILEGE_USER);
+
+ // <Reserve SDR Repo>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
+ nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
+ nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
+
+ // <Get Sdr>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
+ nullptr, ipmiStorageGetSDR, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
+ ipmiStorageGetSDR, PRIVILEGE_USER);
+ return;
+}
+} // namespace ipmi