| /* |
| // 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 <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>>; |
| namespace variant_ns = sdbusplus::message::variant_ns; |
| |
| 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; |
| |
| SensorSubTree sensorTree; |
| static boost::container::flat_map<std::string, ManagedObjectType> SensorCache; |
| |
| // Specify the comparison required to sort and find char* map objects |
| struct CmpStr |
| { |
| bool operator()(const char *a, const char *b) const |
| { |
| return std::strcmp(a, b) < 0; |
| } |
| }; |
| const static boost::container::flat_map<const char *, SensorUnits, CmpStr> |
| 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 = variant_ns::visit(VariantToDoubleVisitor(), maxMap->second); |
| } |
| if (minMap != sensorPropertyMap.end()) |
| { |
| min = variant_ns::visit(VariantToDoubleVisitor(), minMap->second); |
| } |
| } |
| |
| 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 = variant_ns::visit(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 = variant_ns::visit(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 = variant_ns::visit(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 = variant_ns::visit(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 = variant_ns::visit(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 = sdbusplus::message::variant_ns::get<bool>( |
| warningHigh->second); |
| } |
| if (warningLow != warningMap.end()) |
| { |
| warningLowAlarm = sdbusplus::message::variant_ns::get<bool>( |
| warningLow->second); |
| } |
| 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 = sdbusplus::message::variant_ns::get<bool>( |
| criticalHigh->second); |
| } |
| if (criticalLow != criticalMap.end()) |
| { |
| criticalLowAlarm = sdbusplus::message::variant_ns::get<bool>( |
| criticalLow->second); |
| } |
| 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++; |
| if (sdrReservationID == 0) |
| { |
| 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 ((sdrReservationID == 0 || 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 = variant_ns::visit(VariantToDoubleVisitor(), maxObject->second); |
| } |
| |
| if (minObject != sensorObject->second.end()) |
| { |
| min = variant_ns::visit(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 |