xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sensorcommands.cpp (revision 653aec092d86597aa7d7d1259eee58e12310f202)
1 /*
2 // Copyright (c) 2017 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "config.h"
18 
19 #include "dbus-sdr/sensorcommands.hpp"
20 
21 #include "dbus-sdr/sdrutils.hpp"
22 #include "dbus-sdr/sensorutils.hpp"
23 #include "dbus-sdr/storagecommands.hpp"
24 
25 #include <boost/algorithm/string.hpp>
26 #include <boost/container/flat_map.hpp>
27 #include <ipmid/api.hpp>
28 #include <ipmid/entity_map_json.hpp>
29 #include <ipmid/types.hpp>
30 #include <ipmid/utils.hpp>
31 #include <phosphor-logging/lg2.hpp>
32 #include <sdbusplus/bus.hpp>
33 #include <user_channel/channel_layer.hpp>
34 #include <xyz/openbmc_project/Sensor/Threshold/Critical/common.hpp>
35 #include <xyz/openbmc_project/Sensor/Threshold/Warning/common.hpp>
36 #include <xyz/openbmc_project/Sensor/Value/common.hpp>
37 
38 #include <algorithm>
39 #include <array>
40 #include <chrono>
41 #include <cmath>
42 #include <cstring>
43 #include <format>
44 #include <map>
45 #include <optional>
46 #include <stdexcept>
47 #include <string>
48 #include <utility>
49 #include <variant>
50 
51 using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
52 using SensorThresholdWarning =
53     sdbusplus::common::xyz::openbmc_project::sensor::threshold::Warning;
54 using SensorThresholdCritical =
55     sdbusplus::common::xyz::openbmc_project::sensor::threshold::Critical;
56 
57 #ifdef FEATURE_HYBRID_SENSORS
58 
59 #include "sensordatahandler.hpp"
60 namespace ipmi
61 {
62 namespace sensor
63 {
64 extern const IdInfoMap sensors;
65 } // namespace sensor
66 } // namespace ipmi
67 #endif
68 namespace ipmi
69 {
70 namespace dcmi
71 {
72 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
73 static const std::map<uint8_t, uint8_t> validEntityId{
74     {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
75     {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
76 constexpr uint8_t temperatureSensorType = 0x01;
77 constexpr uint8_t maxRecords = 8;
78 } // namespace dcmi
79 } // namespace ipmi
80 constexpr std::array<const char*, 7> suffixes = {
81     "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
82     "_Output_Power",   "_Input_Power",   "_Temperature"};
83 namespace ipmi
84 {
85 
86 using phosphor::logging::entry;
87 using phosphor::logging::level;
88 using phosphor::logging::log;
89 
90 static constexpr int sensorMapUpdatePeriod = 10;
91 static constexpr int sensorMapSdrUpdatePeriod = 60;
92 
93 // BMC I2C address is generally at 0x20
94 static constexpr uint8_t bmcI2CAddr = 0x20;
95 
96 constexpr size_t maxSDRTotalSize =
97     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
98 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
99 
100 static uint16_t sdrReservationID;
101 static uint32_t sdrLastAdd = noTimestamp;
102 static uint32_t sdrLastRemove = noTimestamp;
103 static constexpr size_t lastRecordIndex = 0xFFFF;
104 
105 // The IPMI spec defines four Logical Units (LUN), each capable of supporting
106 // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
107 // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
108 // number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
109 // location.
110 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
111 
112 static constexpr uint8_t lun0 = 0x0;
113 static constexpr uint8_t lun1 = 0x1;
114 static constexpr uint8_t lun3 = 0x3;
115 
116 static constexpr size_t lun0MaxSensorNum = 0xfe;
117 static constexpr size_t lun1MaxSensorNum = 0x1fe;
118 static constexpr size_t lun3MaxSensorNum = 0x3fe;
119 static constexpr int GENERAL_ERROR = -1;
120 
121 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
122 
123 // Specify the comparison required to sort and find char* map objects
124 struct CmpStr
125 {
operator ()ipmi::CmpStr126     bool operator()(const char* a, const char* b) const
127     {
128         return std::strcmp(a, b) < 0;
129     }
130 };
131 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
132     sensorUnits{{{"temperature", SensorUnits::degreesC},
133                  {"voltage", SensorUnits::volts},
134                  {"current", SensorUnits::amps},
135                  {"fan_tach", SensorUnits::rpm},
136                  {"power", SensorUnits::watts},
137                  {"energy", SensorUnits::joules}}};
138 
139 void registerSensorFunctions() __attribute__((constructor));
140 
141 static sdbusplus::bus::match_t sensorAdded(
142     *getSdBus(),
143     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
144     "sensors/'",
__anon2f2fc9af0102(sdbusplus::message_t&) 145     [](sdbusplus::message_t&) {
146         getSensorTree().clear();
147         getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
148         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
149                          std::chrono::system_clock::now().time_since_epoch())
150                          .count();
151     });
152 
153 static sdbusplus::bus::match_t sensorRemoved(
154     *getSdBus(),
155     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
156     "sensors/'",
__anon2f2fc9af0202(sdbusplus::message_t&) 157     [](sdbusplus::message_t&) {
158         getSensorTree().clear();
159         getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
160         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
161                             std::chrono::system_clock::now().time_since_epoch())
162                             .count();
163     });
164 
getSensorConnection(ipmi::Context::ptr ctx,uint8_t sensnum,std::string & connection,std::string & path,std::vector<std::string> * interfaces)165 ipmi::Cc getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
166                              std::string& connection, std::string& path,
167                              std::vector<std::string>* interfaces)
168 {
169     auto& sensorTree = getSensorTree();
170     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
171     {
172         return ipmi::ccResponseError;
173     }
174 
175     if (ctx == nullptr)
176     {
177         return ipmi::ccResponseError;
178     }
179 
180     path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
181     if (path.empty())
182     {
183         return ipmi::ccInvalidFieldRequest;
184     }
185 
186     for (const auto& sensor : sensorTree)
187     {
188         if (path == sensor.first)
189         {
190             connection = sensor.second.begin()->first;
191             if (interfaces)
192                 *interfaces = sensor.second.begin()->second;
193             break;
194         }
195     }
196 
197     return 0;
198 }
199 
getSensorTree()200 SensorSubTree& getSensorTree()
201 {
202     static SensorSubTree sensorTree;
203     return sensorTree;
204 }
205 
206 // this keeps track of deassertions for sensor event status command. A
207 // deasertion can only happen if an assertion was seen first.
208 static boost::container::flat_map<
209     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
210     thresholdDeassertMap;
211 
212 static sdbusplus::bus::match_t thresholdChanged(
213     *getSdBus(),
214     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
215     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
__anon2f2fc9af0302(sdbusplus::message_t& m) 216     [](sdbusplus::message_t& m) {
217         boost::container::flat_map<std::string, std::variant<bool, double>>
218             values;
219         m.read(std::string(), values);
220 
221         auto findAssert =
222             std::find_if(values.begin(), values.end(), [](const auto& pair) {
223                 return pair.first.find("Alarm") != std::string::npos;
224             });
225         if (findAssert != values.end())
226         {
227             auto ptr = std::get_if<bool>(&(findAssert->second));
228             if (ptr == nullptr)
229             {
230                 lg2::error("thresholdChanged: Assert non bool");
231                 return;
232             }
233             if (*ptr)
234             {
235                 lg2::info(
236                     "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
237                     "SENSOR_PATH", m.get_path());
238                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
239             }
240             else
241             {
242                 auto& value =
243                     thresholdDeassertMap[m.get_path()][findAssert->first];
244                 if (value)
245                 {
246                     lg2::info(
247                         "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
248                         "SENSOR_PATH", m.get_path());
249                     value = *ptr;
250                 }
251             }
252         }
253     });
254 
255 namespace sensor
256 {
257 static constexpr const char* vrInterface =
258     "xyz.openbmc_project.Control.VoltageRegulatorMode";
259 } // namespace sensor
260 
getSensorMaxMin(const DbusInterfaceMap & sensorMap,double & max,double & min)261 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
262                             double& min)
263 {
264     max = 127;
265     min = -128;
266 
267     auto sensorObject = sensorMap.find(SensorValue::interface);
268     auto critical = sensorMap.find(SensorThresholdCritical::interface);
269     auto warning = sensorMap.find(SensorThresholdWarning::interface);
270 
271     if (sensorObject != sensorMap.end())
272     {
273         auto maxMap =
274             sensorObject->second.find(SensorValue::property_names::max_value);
275         auto minMap =
276             sensorObject->second.find(SensorValue::property_names::min_value);
277 
278         if (maxMap != sensorObject->second.end())
279         {
280             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
281         }
282         if (minMap != sensorObject->second.end())
283         {
284             min = std::visit(VariantToDoubleVisitor(), minMap->second);
285         }
286     }
287     if (critical != sensorMap.end())
288     {
289         auto lower = critical->second.find(
290             SensorThresholdCritical::property_names::critical_low);
291         auto upper = critical->second.find(
292             SensorThresholdCritical::property_names::critical_high);
293         if (lower != critical->second.end())
294         {
295             double value = std::visit(VariantToDoubleVisitor(), lower->second);
296             if (std::isfinite(value))
297             {
298                 min = std::fmin(value, min);
299             }
300         }
301         if (upper != critical->second.end())
302         {
303             double value = std::visit(VariantToDoubleVisitor(), upper->second);
304             if (std::isfinite(value))
305             {
306                 max = std::fmax(value, max);
307             }
308         }
309     }
310     if (warning != sensorMap.end())
311     {
312         auto lower = warning->second.find(
313             SensorThresholdWarning::property_names::warning_low);
314         auto upper = warning->second.find(
315             SensorThresholdWarning::property_names::warning_high);
316         if (lower != warning->second.end())
317         {
318             double value = std::visit(VariantToDoubleVisitor(), lower->second);
319             if (std::isfinite(value))
320             {
321                 min = std::fmin(value, min);
322             }
323         }
324         if (upper != warning->second.end())
325         {
326             double value = std::visit(VariantToDoubleVisitor(), upper->second);
327             if (std::isfinite(value))
328             {
329                 max = std::fmax(value, max);
330             }
331         }
332     }
333 }
334 
getSensorMap(ipmi::Context::ptr ctx,std::string sensorConnection,std::string sensorPath,DbusInterfaceMap & sensorMap,int updatePeriod=sensorMapUpdatePeriod)335 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
336                          std::string sensorPath, DbusInterfaceMap& sensorMap,
337                          int updatePeriod = sensorMapUpdatePeriod)
338 {
339 #ifdef FEATURE_HYBRID_SENSORS
340     if (auto sensor = findStaticSensor(sensorPath);
341         sensor != ipmi::sensor::sensors.end() &&
342         getSensorEventTypeFromPath(sensorPath) !=
343             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
344     {
345         // If the incoming sensor is a discrete sensor, it might fail in
346         // getManagedObjects(), return true, and use its own getFunc to get
347         // value.
348         return true;
349     }
350 #endif
351 
352     static boost::container::flat_map<
353         std::string, std::chrono::time_point<std::chrono::steady_clock>>
354         updateTimeMap;
355 
356     auto updateFind = updateTimeMap.find(sensorConnection);
357     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
358     if (updateFind != updateTimeMap.end())
359     {
360         lastUpdate = updateFind->second;
361     }
362 
363     auto now = std::chrono::steady_clock::now();
364 
365     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
366             .count() > updatePeriod)
367     {
368         bool found = false;
369 
370         // Object managers for different kinds of OpenBMC DBus interfaces.
371         // Documented in the phosphor-dbus-interfaces repository.
372         const char* paths[] = {
373             "/xyz/openbmc_project/sensors",
374             "/xyz/openbmc_project/vr",
375         };
376         constexpr size_t numPaths = sizeof(paths) / sizeof(paths[0]);
377         ObjectValueTree allManagedObjects;
378 
379         for (size_t i = 0; i < numPaths; i++)
380         {
381             ObjectValueTree managedObjects;
382             boost::system::error_code ec = getManagedObjects(
383                 ctx, sensorConnection.c_str(), paths[i], managedObjects);
384             if (ec)
385             {
386                 continue;
387             }
388             allManagedObjects.merge(managedObjects);
389             found = true;
390         }
391 
392         if (!found)
393         {
394             lg2::error("GetMangagedObjects for getSensorMap failed, "
395                        "service: {SERVICE}",
396                        "SERVICE", sensorConnection);
397 
398             return false;
399         }
400 
401         SensorCache[sensorConnection] = allManagedObjects;
402         // Update time after finish building the map which allow the
403         // data to be cached for updatePeriod plus the build time.
404         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
405     }
406     auto connection = SensorCache.find(sensorConnection);
407     if (connection == SensorCache.end())
408     {
409         return false;
410     }
411     auto path = connection->second.find(sensorPath);
412     if (path == connection->second.end())
413     {
414         return false;
415     }
416     sensorMap = path->second;
417 
418     return true;
419 }
420 
421 namespace sensor
422 {
423 // Read VR profiles from sensor(daemon) interface
getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type & object)424 static std::optional<std::vector<std::string>> getSupportedVrProfiles(
425     const ipmi::DbusInterfaceMap::mapped_type& object)
426 {
427     // get VR mode profiles from Supported Interface
428     auto supportedProperty = object.find("Supported");
429     if (supportedProperty == object.end() ||
430         object.find("Selected") == object.end())
431     {
432         lg2::error("Missing the required Supported and Selected properties");
433         return std::nullopt;
434     }
435 
436     const auto profilesPtr =
437         std::get_if<std::vector<std::string>>(&supportedProperty->second);
438 
439     if (profilesPtr == nullptr)
440     {
441         lg2::error("property is not array of string");
442         return std::nullopt;
443     }
444     return *profilesPtr;
445 }
446 
447 // Calculate VR Mode from input IPMI discrete event bytes
calculateVRMode(uint15_t assertOffset,const ipmi::DbusInterfaceMap::mapped_type & VRObject)448 static std::optional<std::string> calculateVRMode(
449     uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
450 {
451     // get VR mode profiles from Supported Interface
452     auto profiles = getSupportedVrProfiles(VRObject);
453     if (!profiles)
454     {
455         return std::nullopt;
456     }
457 
458     // interpret IPMI cmd bits into profiles' index
459     long unsigned int index = 0;
460     // only one bit should be set and the highest bit should not be used.
461     if (assertOffset == 0 || assertOffset == (1u << 15) ||
462         (assertOffset & (assertOffset - 1)))
463     {
464         lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
465                    lg2::hex, static_cast<uint16_t>(assertOffset));
466         return std::nullopt;
467     }
468 
469     while (assertOffset != 1)
470     {
471         assertOffset >>= 1;
472         index++;
473     }
474 
475     if (index >= profiles->size())
476     {
477         lg2::error("profile index out of boundary");
478         return std::nullopt;
479     }
480 
481     return profiles->at(index);
482 }
483 
484 // Calculate sensor value from IPMI reading byte
calculateValue(uint8_t reading,const ipmi::DbusInterfaceMap & sensorMap,const ipmi::DbusInterfaceMap::mapped_type & valueObject)485 static std::optional<double> calculateValue(
486     uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
487     const ipmi::DbusInterfaceMap::mapped_type& valueObject)
488 {
489     if (valueObject.find(SensorValue::property_names::value) ==
490         valueObject.end())
491     {
492         lg2::error("Missing the required Value property");
493         return std::nullopt;
494     }
495 
496     double max = 0;
497     double min = 0;
498     getSensorMaxMin(sensorMap, max, min);
499 
500     int16_t mValue = 0;
501     int16_t bValue = 0;
502     int8_t rExp = 0;
503     int8_t bExp = 0;
504     bool bSigned = false;
505 
506     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
507     {
508         return std::nullopt;
509     }
510 
511     double value = bSigned ? ((int8_t)reading) : reading;
512 
513     value *= ((double)mValue);
514     value += ((double)bValue) * std::pow(10.0, bExp);
515     value *= std::pow(10.0, rExp);
516 
517     return value;
518 }
519 
520 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
521 // if it is too long.
parseSdrIdFromPath(const std::string & path)522 std::string parseSdrIdFromPath(const std::string& path)
523 {
524     std::string name;
525     size_t nameStart = path.rfind("/");
526     if (nameStart != std::string::npos)
527     {
528         name = path.substr(nameStart + 1, std::string::npos - nameStart);
529     }
530 
531     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
532     {
533 #ifdef SHORTNAME_REMOVE_SUFFIX
534         for (const auto& suffix : suffixes)
535         {
536             if (name.ends_with(suffix))
537             {
538                 boost::replace_all(name, suffix, "");
539                 break;
540             }
541         }
542 #endif
543 #ifdef SHORTNAME_REPLACE_WORDS
544         constexpr std::array<std::pair<const char*, const char*>, 2>
545             replaceWords = {std::make_pair("Output", "Out"),
546                             std::make_pair("Input", "In")};
547         for (const auto& [find, replace] : replaceWords)
548         {
549             boost::replace_all(name, find, replace);
550         }
551 #endif
552 
553         // as a backup and if nothing else is configured
554         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
555     }
556     return name;
557 }
558 
getVrEventStatus(ipmi::Context::ptr ctx,const std::string & connection,const std::string & path,const ipmi::DbusInterfaceMap::mapped_type & object,std::bitset<16> & assertions)559 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
560                       const std::string& path,
561                       const ipmi::DbusInterfaceMap::mapped_type& object,
562                       std::bitset<16>& assertions)
563 {
564     auto profiles = sensor::getSupportedVrProfiles(object);
565     if (!profiles)
566     {
567         return false;
568     }
569     std::string mode;
570 
571     auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
572                               "Selected", mode);
573     if (ec)
574     {
575         lg2::error("Failed to get Selected, path: {PATH}, "
576                    "interface: {INTERFACE}, error: {ERROR}",
577                    "PATH", path, "INTERFACE", SensorValue::interface, "ERROR",
578                    ec.message());
579         return false;
580     }
581 
582     auto itr = std::find(profiles->begin(), profiles->end(), mode);
583     if (itr == profiles->end())
584     {
585         lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
586                    "PATH", path);
587         return false;
588     }
589     std::size_t index =
590         static_cast<std::size_t>(std::distance(profiles->begin(), itr));
591 
592     // map index to response event assertion bit.
593     if (index < 16)
594     {
595         assertions.set(index);
596     }
597     else
598     {
599         lg2::error("VR profile index reaches max assertion bit, "
600                    "path: {PATH}, index: {INDEX}",
601                    "PATH", path, "INDEX", index);
602         return false;
603     }
604     if constexpr (debug)
605     {
606         lg2::error("VR sensor {PATH} mode is: [{INDEX}] {MODE}", "PATH",
607                    sensor::parseSdrIdFromPath(path), "INDEX", index, "MODE",
608                    mode);
609     }
610     return true;
611 }
612 
613 /*
614  * Handle every Sensor Data Record besides Type 01
615  *
616  * The D-Bus sensors work well for generating Type 01 SDRs.
617  * After the Type 01 sensors are processed the remaining sensor types require
618  * special handling. Each BMC vendor is going to have their own requirements for
619  * insertion of non-Type 01 records.
620  * Manage non-Type 01 records:
621  *
622  * Create a new file: dbus-sdr/sensorcommands_oem.cpp
623  * Populate it with the two weakly linked functions below, without adding the
624  * 'weak' attribute definition prior to the function definition.
625  *    getOtherSensorsCount(...)
626  *    getOtherSensorsDataRecord(...)
627  *    Example contents are provided in the weak definitions below
628  *    Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
629  *      'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
630  * The contents of the sensorcommands_oem.cpp file will then override the code
631  * provided below.
632  */
633 
634 size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
getOtherSensorsCount(ipmi::Context::ptr ctx)635 size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
636 {
637     size_t fruCount = 0;
638 
639     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
640     if (ret != ipmi::ccSuccess)
641     {
642         lg2::error("getOtherSensorsCount: getFruSdrCount error");
643         return std::numeric_limits<size_t>::max();
644     }
645 
646     const auto& entityRecords =
647         ipmi::sensor::EntityInfoMapContainer::getContainer()
648             ->getIpmiEntityRecords();
649     size_t entityCount = entityRecords.size();
650 
651     return fruCount + ipmi::storage::type12Count + entityCount;
652 }
653 
654 int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
655                               std::vector<uint8_t>& recordData)
656     __attribute__((weak));
getOtherSensorsDataRecord(ipmi::Context::ptr ctx,uint16_t recordID,std::vector<uint8_t> & recordData)657 int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
658                               std::vector<uint8_t>& recordData)
659 {
660     size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
661     if (otherCount == std::numeric_limits<size_t>::max())
662     {
663         return GENERAL_ERROR;
664     }
665     const auto& entityRecords =
666         ipmi::sensor::EntityInfoMapContainer::getContainer()
667             ->getIpmiEntityRecords();
668 
669     size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
670     size_t entityCount{entityRecords.size()};
671     size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
672 
673     if (sdrIndex > otherCount)
674     {
675         return std::numeric_limits<int>::min();
676     }
677     else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
678     {
679         // handle type 8 entity map records
680         ipmi::sensor::EntityInfoMap::const_iterator entity =
681             entityRecords.find(static_cast<uint8_t>(
682                 sdrIndex - fruCount - ipmi::storage::type12Count));
683 
684         if (entity == entityRecords.end())
685         {
686             return GENERAL_ERROR;
687         }
688         recordData = ipmi::storage::getType8SDRs(entity, recordID);
689     }
690     else if (sdrIndex >= fruCount)
691     {
692         // handle type 12 hardcoded records
693         size_t type12Index = sdrIndex - fruCount;
694         if (type12Index >= ipmi::storage::type12Count)
695         {
696             lg2::error("getSensorDataRecord: type12Index error");
697             return GENERAL_ERROR;
698         }
699         recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
700     }
701     else
702     {
703         // handle fru records
704         get_sdr::SensorDataFruRecord data;
705         if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
706             ret != ipmi::ccSuccess)
707         {
708             return GENERAL_ERROR;
709         }
710         data.header.recordId = recordID;
711         recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
712                           reinterpret_cast<uint8_t*>(&data) + sizeof(data));
713     }
714 
715     return 0;
716 }
717 
718 } // namespace sensor
719 
ipmiSenPlatformEvent(ipmi::Context::ptr ctx,ipmi::message::Payload & p)720 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
721                                      ipmi::message::Payload& p)
722 {
723     constexpr const uint8_t validEnvmRev = 0x04;
724     constexpr const uint8_t lastSensorType = 0x2C;
725     constexpr const uint8_t oemReserved = 0xC0;
726 
727     uint8_t sysgeneratorID = 0;
728     uint8_t evmRev = 0;
729     uint8_t sensorType = 0;
730     uint8_t sensorNum = 0;
731     uint8_t eventType = 0;
732     uint8_t eventData1 = 0;
733     std::optional<uint8_t> eventData2 = 0;
734     std::optional<uint8_t> eventData3 = 0;
735     [[maybe_unused]] uint16_t generatorID = 0;
736     ipmi::ChannelInfo chInfo;
737 
738     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
739     {
740         lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
741                    ctx->channel);
742         return ipmi::responseUnspecifiedError();
743     }
744 
745     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
746         ipmi::EChannelMediumType::systemInterface)
747     {
748         p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
749                  eventData1, eventData2, eventData3);
750         constexpr const uint8_t isSoftwareID = 0x01;
751         if (!(sysgeneratorID & isSoftwareID))
752         {
753             return ipmi::responseInvalidFieldRequest();
754         }
755         // Refer to IPMI Spec Table 32: SEL Event Records
756         generatorID = (ctx->channel << 12) // Channel
757                       | (0x0 << 10)        // Reserved
758                       | (0x0 << 8)         // 0x0 for sys-soft ID
759                       | sysgeneratorID;
760     }
761     else
762     {
763         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
764                  eventData2, eventData3);
765         // Refer to IPMI Spec Table 32: SEL Event Records
766         generatorID = (ctx->channel << 12)      // Channel
767                       | (0x0 << 10)             // Reserved
768                       | ((ctx->lun & 0x3) << 8) // Lun
769                       | (ctx->rqSA << 1);
770     }
771 
772     if (!p.fullyUnpacked())
773     {
774         return ipmi::responseReqDataLenInvalid();
775     }
776 
777     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
778     if (evmRev != validEnvmRev)
779     {
780         return ipmi::responseInvalidFieldRequest();
781     }
782     if ((sensorType > lastSensorType) && (sensorType < oemReserved))
783     {
784         return ipmi::responseInvalidFieldRequest();
785     }
786 
787     return ipmi::responseSuccess();
788 }
789 
ipmiSetSensorReading(ipmi::Context::ptr ctx,uint8_t sensorNumber,uint8_t,uint8_t reading,uint15_t assertOffset,bool,uint15_t,bool,uint8_t,uint8_t,uint8_t)790 ipmi::RspType<> ipmiSetSensorReading(
791     ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
792     uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
793 {
794     std::string connection;
795     std::string path;
796     std::vector<std::string> interfaces;
797 
798     ipmi::Cc status =
799         getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
800     if (status)
801     {
802         return ipmi::response(status);
803     }
804 
805     // we can tell the sensor type by its interface type
806     if (std::find(interfaces.begin(), interfaces.end(),
807                   SensorValue::interface) != interfaces.end())
808     {
809         DbusInterfaceMap sensorMap;
810         if (!getSensorMap(ctx, connection, path, sensorMap))
811         {
812             return ipmi::responseResponseError();
813         }
814         auto sensorObject = sensorMap.find(SensorValue::interface);
815         if (sensorObject == sensorMap.end())
816         {
817             return ipmi::responseResponseError();
818         }
819 
820         // Only allow external SetSensor if write permission granted
821         if (!details::sdrWriteTable.getWritePermission(
822                 (ctx->lun << 8) | sensorNumber))
823         {
824             return ipmi::responseResponseError();
825         }
826 
827         auto value =
828             sensor::calculateValue(reading, sensorMap, sensorObject->second);
829         if (!value)
830         {
831             return ipmi::responseResponseError();
832         }
833 
834         if constexpr (debug)
835         {
836             lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
837                       "byte: {BYTE}, value: {VALUE}",
838                       "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
839                       "VALUE", *value);
840         }
841 
842         boost::system::error_code ec = setDbusProperty(
843             ctx, connection, path, SensorValue::interface,
844             SensorValue::property_names::value, ipmi::Value(*value));
845 
846         // setDbusProperty intended to resolve dbus exception/rc within the
847         // function but failed to achieve that. Catch exception in the ipmi
848         // callback functions for now (e.g. ipmiSetSensorReading).
849         if (ec)
850         {
851             lg2::error("Failed to set Value, path: {PATH}, "
852                        "interface: {INTERFACE}, ERROR: {ERROR}",
853                        "PATH", path, "INTERFACE", SensorValue::interface,
854                        "ERROR", ec.message());
855             return ipmi::responseResponseError();
856         }
857         return ipmi::responseSuccess();
858     }
859 
860     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
861         interfaces.end())
862     {
863         DbusInterfaceMap sensorMap;
864         if (!getSensorMap(ctx, connection, path, sensorMap))
865         {
866             return ipmi::responseResponseError();
867         }
868         auto sensorObject = sensorMap.find(sensor::vrInterface);
869         if (sensorObject == sensorMap.end())
870         {
871             return ipmi::responseResponseError();
872         }
873 
874         // VR sensors are treated as a special case and we will not check the
875         // write permission for VR sensors, since they always deemed writable
876         // and permission table are not applied to VR sensors.
877         auto vrMode =
878             sensor::calculateVRMode(assertOffset, sensorObject->second);
879         if (!vrMode)
880         {
881             return ipmi::responseResponseError();
882         }
883         boost::system::error_code ec = setDbusProperty(
884             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
885         // setDbusProperty intended to resolve dbus exception/rc within the
886         // function but failed to achieve that. Catch exception in the ipmi
887         // callback functions for now (e.g. ipmiSetSensorReading).
888         if (ec)
889         {
890             lg2::error("Failed to set Selected, path: {PATH}, "
891                        "interface: {INTERFACE}, ERROR: {ERROR}",
892                        "PATH", path, "INTERFACE", SensorValue::interface,
893                        "ERROR", ec.message());
894         }
895         return ipmi::responseSuccess();
896     }
897 
898     lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
899     return ipmi::responseResponseError();
900 }
901 
902 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
ipmiSenGetSensorReading(ipmi::Context::ptr ctx,uint8_t sensnum)903     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
904 {
905     std::string connection;
906     std::string path;
907 
908     if (sensnum == reservedSensorNumber)
909     {
910         return ipmi::responseInvalidFieldRequest();
911     }
912 
913     auto status = getSensorConnection(ctx, sensnum, connection, path);
914     if (status)
915     {
916         return ipmi::response(status);
917     }
918 
919 #ifdef FEATURE_HYBRID_SENSORS
920     if (auto sensor = findStaticSensor(path);
921         sensor != ipmi::sensor::sensors.end() &&
922         getSensorEventTypeFromPath(path) !=
923             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
924     {
925         if (ipmi::sensor::Mutability::Read !=
926             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
927         {
928             return ipmi::responseIllegalCommand();
929         }
930 
931         uint8_t operation = 0;
932         try
933         {
934             ipmi::sensor::GetSensorResponse getResponse =
935                 sensor->second.getFunc(sensor->second);
936 
937             if (getResponse.readingOrStateUnavailable)
938             {
939                 operation |= static_cast<uint8_t>(
940                     IPMISensorReadingByte2::readingStateUnavailable);
941             }
942             if (getResponse.scanningEnabled)
943             {
944                 operation |= static_cast<uint8_t>(
945                     IPMISensorReadingByte2::sensorScanningEnable);
946             }
947             if (getResponse.allEventMessagesEnabled)
948             {
949                 operation |= static_cast<uint8_t>(
950                     IPMISensorReadingByte2::eventMessagesEnable);
951             }
952             return ipmi::responseSuccess(
953                 getResponse.reading, operation,
954                 getResponse.thresholdLevelsStates,
955                 getResponse.discreteReadingSensorStates);
956         }
957         catch (const std::exception& e)
958         {
959             operation |= static_cast<uint8_t>(
960                 IPMISensorReadingByte2::readingStateUnavailable);
961             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
962         }
963     }
964 #endif
965 
966     DbusInterfaceMap sensorMap;
967     if (!getSensorMap(ctx, connection, path, sensorMap))
968     {
969         return ipmi::responseResponseError();
970     }
971     auto sensorObject = sensorMap.find(SensorValue::interface);
972 
973     if (sensorObject == sensorMap.end() ||
974         sensorObject->second.find(SensorValue::property_names::value) ==
975             sensorObject->second.end())
976     {
977         return ipmi::responseResponseError();
978     }
979     auto& valueVariant =
980         sensorObject->second[SensorValue::property_names::value];
981     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
982 
983     double max = 0;
984     double min = 0;
985     getSensorMaxMin(sensorMap, max, min);
986 
987     int16_t mValue = 0;
988     int16_t bValue = 0;
989     int8_t rExp = 0;
990     int8_t bExp = 0;
991     bool bSigned = false;
992 
993     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
994     {
995         return ipmi::responseResponseError();
996     }
997 
998     uint8_t value =
999         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
1000     uint8_t operation =
1001         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
1002     operation |=
1003         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
1004     bool notReading = std::isnan(reading);
1005 
1006     if (!notReading)
1007     {
1008         auto availableObject =
1009             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1010         if (availableObject != sensorMap.end())
1011         {
1012             auto findAvailable = availableObject->second.find("Available");
1013             if (findAvailable != availableObject->second.end())
1014             {
1015                 bool* available = std::get_if<bool>(&(findAvailable->second));
1016                 if (available && !(*available))
1017                 {
1018                     notReading = true;
1019                 }
1020             }
1021         }
1022     }
1023 
1024     if (notReading)
1025     {
1026         operation |= static_cast<uint8_t>(
1027             IPMISensorReadingByte2::readingStateUnavailable);
1028     }
1029 
1030     if constexpr (details::enableInstrumentation)
1031     {
1032         int byteValue;
1033         if (bSigned)
1034         {
1035             byteValue = static_cast<int>(static_cast<int8_t>(value));
1036         }
1037         else
1038         {
1039             byteValue = static_cast<int>(static_cast<uint8_t>(value));
1040         }
1041 
1042         // Keep stats on the reading just obtained, even if it is "NaN"
1043         if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1044                                                  reading, byteValue))
1045         {
1046             // This is the first reading, show the coefficients
1047             double step = (max - min) / 255.0;
1048             lg2::error(
1049                 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, "
1050                 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} "
1051                 "bExp={BEXP} bSigned={BSIGNED}",
1052                 "NAME",
1053                 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum),
1054                 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP",
1055                 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned);
1056         }
1057     }
1058 
1059     uint8_t thresholds = 0;
1060 
1061     auto warningObject = sensorMap.find(SensorThresholdWarning::interface);
1062     if (warningObject != sensorMap.end())
1063     {
1064         auto alarmHigh = warningObject->second.find(
1065             SensorThresholdWarning::property_names::warning_alarm_high);
1066         auto alarmLow = warningObject->second.find(
1067             SensorThresholdWarning::property_names::warning_alarm_low);
1068         if (alarmHigh != warningObject->second.end())
1069         {
1070             if (std::get<bool>(alarmHigh->second))
1071             {
1072                 thresholds |= static_cast<uint8_t>(
1073                     IPMISensorReadingByte3::upperNonCritical);
1074             }
1075         }
1076         if (alarmLow != warningObject->second.end())
1077         {
1078             if (std::get<bool>(alarmLow->second))
1079             {
1080                 thresholds |= static_cast<uint8_t>(
1081                     IPMISensorReadingByte3::lowerNonCritical);
1082             }
1083         }
1084     }
1085 
1086     auto criticalObject = sensorMap.find(SensorThresholdCritical::interface);
1087     if (criticalObject != sensorMap.end())
1088     {
1089         auto alarmHigh = criticalObject->second.find(
1090             SensorThresholdCritical::property_names::critical_alarm_high);
1091         auto alarmLow = criticalObject->second.find(
1092             SensorThresholdCritical::property_names::critical_alarm_low);
1093         if (alarmHigh != criticalObject->second.end())
1094         {
1095             if (std::get<bool>(alarmHigh->second))
1096             {
1097                 thresholds |=
1098                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1099             }
1100         }
1101         if (alarmLow != criticalObject->second.end())
1102         {
1103             if (std::get<bool>(alarmLow->second))
1104             {
1105                 thresholds |=
1106                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1107             }
1108         }
1109     }
1110 
1111     // no discrete as of today so optional byte is never returned
1112     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1113 }
1114 
1115 /** @brief implements the Set Sensor threshold command
1116  *  @param sensorNumber        - sensor number
1117  *  @param lowerNonCriticalThreshMask
1118  *  @param lowerCriticalThreshMask
1119  *  @param lowerNonRecovThreshMask
1120  *  @param upperNonCriticalThreshMask
1121  *  @param upperCriticalThreshMask
1122  *  @param upperNonRecovThreshMask
1123  *  @param reserved
1124  *  @param lowerNonCritical    - lower non-critical threshold
1125  *  @param lowerCritical       - Lower critical threshold
1126  *  @param lowerNonRecoverable - Lower non recovarable threshold
1127  *  @param upperNonCritical    - Upper non-critical threshold
1128  *  @param upperCritical       - Upper critical
1129  *  @param upperNonRecoverable - Upper Non-recoverable
1130  *
1131  *  @returns IPMI completion code
1132  */
ipmiSenSetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNum,bool lowerNonCriticalThreshMask,bool lowerCriticalThreshMask,bool lowerNonRecovThreshMask,bool upperNonCriticalThreshMask,bool upperCriticalThreshMask,bool upperNonRecovThreshMask,uint2_t reserved,uint8_t lowerNonCritical,uint8_t lowerCritical,uint8_t lowerNonRecoverable,uint8_t upperNonCritical,uint8_t upperCritical,uint8_t upperNonRecoverable)1133 ipmi::RspType<> ipmiSenSetSensorThresholds(
1134     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1135     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1136     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1137     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
1138     uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
1139     uint8_t upperNonCritical, uint8_t upperCritical,
1140     [[maybe_unused]] uint8_t upperNonRecoverable)
1141 {
1142     if (sensorNum == reservedSensorNumber || reserved)
1143     {
1144         return ipmi::responseInvalidFieldRequest();
1145     }
1146 
1147     // lower nc and upper nc not suppported on any sensor
1148     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1149     {
1150         return ipmi::responseInvalidFieldRequest();
1151     }
1152 
1153     // if none of the threshold mask are set, nothing to do
1154     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1155           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1156           upperCriticalThreshMask | upperNonRecovThreshMask))
1157     {
1158         return ipmi::responseSuccess();
1159     }
1160 
1161     std::string connection;
1162     std::string path;
1163 
1164     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1165     if (status)
1166     {
1167         return ipmi::response(status);
1168     }
1169     DbusInterfaceMap sensorMap;
1170     if (!getSensorMap(ctx, connection, path, sensorMap))
1171     {
1172         return ipmi::responseResponseError();
1173     }
1174 
1175     double max = 0;
1176     double min = 0;
1177     getSensorMaxMin(sensorMap, max, min);
1178 
1179     int16_t mValue = 0;
1180     int16_t bValue = 0;
1181     int8_t rExp = 0;
1182     int8_t bExp = 0;
1183     bool bSigned = false;
1184 
1185     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1186     {
1187         return ipmi::responseResponseError();
1188     }
1189 
1190     // store a vector of property name, value to set, and interface
1191     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1192 
1193     // define the indexes of the tuple
1194     constexpr uint8_t propertyName = 0;
1195     constexpr uint8_t thresholdValue = 1;
1196     constexpr uint8_t interface = 2;
1197     // verifiy all needed fields are present
1198     if (lowerCriticalThreshMask || upperCriticalThreshMask)
1199     {
1200         auto findThreshold = sensorMap.find(SensorThresholdCritical::interface);
1201         if (findThreshold == sensorMap.end())
1202         {
1203             return ipmi::responseInvalidFieldRequest();
1204         }
1205         if (lowerCriticalThreshMask)
1206         {
1207             auto findLower = findThreshold->second.find(
1208                 SensorThresholdCritical::property_names::critical_low);
1209             if (findLower == findThreshold->second.end())
1210             {
1211                 return ipmi::responseInvalidFieldRequest();
1212             }
1213             thresholdsToSet.emplace_back(
1214                 SensorThresholdCritical::property_names::critical_low,
1215                 lowerCritical, findThreshold->first);
1216         }
1217         if (upperCriticalThreshMask)
1218         {
1219             auto findUpper = findThreshold->second.find(
1220                 SensorThresholdCritical::property_names::critical_high);
1221             if (findUpper == findThreshold->second.end())
1222             {
1223                 return ipmi::responseInvalidFieldRequest();
1224             }
1225             thresholdsToSet.emplace_back(
1226                 SensorThresholdCritical::property_names::critical_high,
1227                 upperCritical, findThreshold->first);
1228         }
1229     }
1230     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1231     {
1232         auto findThreshold = sensorMap.find(SensorThresholdWarning::interface);
1233         if (findThreshold == sensorMap.end())
1234         {
1235             return ipmi::responseInvalidFieldRequest();
1236         }
1237         if (lowerNonCriticalThreshMask)
1238         {
1239             auto findLower = findThreshold->second.find(
1240                 SensorThresholdWarning::property_names::warning_low);
1241             if (findLower == findThreshold->second.end())
1242             {
1243                 return ipmi::responseInvalidFieldRequest();
1244             }
1245             thresholdsToSet.emplace_back(
1246                 SensorThresholdWarning::property_names::warning_low,
1247                 lowerNonCritical, findThreshold->first);
1248         }
1249         if (upperNonCriticalThreshMask)
1250         {
1251             auto findUpper = findThreshold->second.find(
1252                 SensorThresholdWarning::property_names::warning_high);
1253             if (findUpper == findThreshold->second.end())
1254             {
1255                 return ipmi::responseInvalidFieldRequest();
1256             }
1257             thresholdsToSet.emplace_back(
1258                 SensorThresholdWarning::property_names::warning_high,
1259                 upperNonCritical, findThreshold->first);
1260         }
1261     }
1262     for (const auto& property : thresholdsToSet)
1263     {
1264         // from section 36.3 in the IPMI Spec, assume all linear
1265         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1266                              (bValue * std::pow(10.0, bExp))) *
1267                             std::pow(10.0, rExp);
1268         setDbusProperty(
1269             *getSdBus(), connection, path, std::get<interface>(property),
1270             std::get<propertyName>(property), ipmi::Value(valueToSet));
1271     }
1272     return ipmi::responseSuccess();
1273 }
1274 
getIPMIThresholds(const DbusInterfaceMap & sensorMap)1275 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1276 {
1277     IPMIThresholds resp;
1278     auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1279     auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
1280 
1281     if ((warningInterface != sensorMap.end()) ||
1282         (criticalInterface != sensorMap.end()))
1283     {
1284         auto sensorPair = sensorMap.find(SensorValue::interface);
1285 
1286         if (sensorPair == sensorMap.end())
1287         {
1288             // should not have been able to find a sensor not implementing
1289             // the sensor object
1290             throw std::runtime_error("Invalid sensor map");
1291         }
1292 
1293         double max = 0;
1294         double min = 0;
1295         getSensorMaxMin(sensorMap, max, min);
1296 
1297         int16_t mValue = 0;
1298         int16_t bValue = 0;
1299         int8_t rExp = 0;
1300         int8_t bExp = 0;
1301         bool bSigned = false;
1302 
1303         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1304         {
1305             throw std::runtime_error("Invalid sensor atrributes");
1306         }
1307         if (warningInterface != sensorMap.end())
1308         {
1309             auto& warningMap = warningInterface->second;
1310 
1311             auto warningHigh = warningMap.find(
1312                 SensorThresholdWarning::property_names::warning_high);
1313             auto warningLow = warningMap.find(
1314                 SensorThresholdWarning::property_names::warning_low);
1315 
1316             if (warningHigh != warningMap.end())
1317             {
1318                 double value =
1319                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1320                 if (std::isfinite(value))
1321                 {
1322                     resp.warningHigh = scaleIPMIValueFromDouble(
1323                         value, mValue, rExp, bValue, bExp, bSigned);
1324                 }
1325             }
1326             if (warningLow != warningMap.end())
1327             {
1328                 double value =
1329                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1330                 if (std::isfinite(value))
1331                 {
1332                     resp.warningLow = scaleIPMIValueFromDouble(
1333                         value, mValue, rExp, bValue, bExp, bSigned);
1334                 }
1335             }
1336         }
1337         if (criticalInterface != sensorMap.end())
1338         {
1339             auto& criticalMap = criticalInterface->second;
1340 
1341             auto criticalHigh = criticalMap.find(
1342                 SensorThresholdCritical::property_names::critical_high);
1343             auto criticalLow = criticalMap.find(
1344                 SensorThresholdCritical::property_names::critical_low);
1345 
1346             if (criticalHigh != criticalMap.end())
1347             {
1348                 double value =
1349                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1350                 if (std::isfinite(value))
1351                 {
1352                     resp.criticalHigh = scaleIPMIValueFromDouble(
1353                         value, mValue, rExp, bValue, bExp, bSigned);
1354                 }
1355             }
1356             if (criticalLow != criticalMap.end())
1357             {
1358                 double value =
1359                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1360                 if (std::isfinite(value))
1361                 {
1362                     resp.criticalLow = scaleIPMIValueFromDouble(
1363                         value, mValue, rExp, bValue, bExp, bSigned);
1364                 }
1365             }
1366         }
1367     }
1368     return resp;
1369 }
1370 
1371 ipmi::RspType<uint8_t, // readable
1372               uint8_t, // lowerNCrit
1373               uint8_t, // lowerCrit
1374               uint8_t, // lowerNrecoverable
1375               uint8_t, // upperNC
1376               uint8_t, // upperCrit
1377               uint8_t> // upperNRecoverable
ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNumber)1378     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1379 {
1380     std::string connection;
1381     std::string path;
1382 
1383     if (sensorNumber == reservedSensorNumber)
1384     {
1385         return ipmi::responseInvalidFieldRequest();
1386     }
1387 
1388     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1389     if (status)
1390     {
1391         return ipmi::response(status);
1392     }
1393 
1394     DbusInterfaceMap sensorMap;
1395     if (!getSensorMap(ctx, connection, path, sensorMap))
1396     {
1397         return ipmi::responseResponseError();
1398     }
1399 
1400     IPMIThresholds thresholdData;
1401     try
1402     {
1403         thresholdData = getIPMIThresholds(sensorMap);
1404     }
1405     catch (const std::exception&)
1406     {
1407         return ipmi::responseResponseError();
1408     }
1409 
1410     uint8_t readable = 0;
1411     uint8_t lowerNC = 0;
1412     uint8_t lowerCritical = 0;
1413     uint8_t lowerNonRecoverable = 0;
1414     uint8_t upperNC = 0;
1415     uint8_t upperCritical = 0;
1416     uint8_t upperNonRecoverable = 0;
1417 
1418     if (thresholdData.warningHigh)
1419     {
1420         readable |=
1421             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1422         upperNC = *thresholdData.warningHigh;
1423     }
1424     if (thresholdData.warningLow)
1425     {
1426         readable |=
1427             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1428         lowerNC = *thresholdData.warningLow;
1429     }
1430 
1431     if (thresholdData.criticalHigh)
1432     {
1433         readable |=
1434             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1435         upperCritical = *thresholdData.criticalHigh;
1436     }
1437     if (thresholdData.criticalLow)
1438     {
1439         readable |=
1440             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1441         lowerCritical = *thresholdData.criticalLow;
1442     }
1443 
1444     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1445                                  lowerNonRecoverable, upperNC, upperCritical,
1446                                  upperNonRecoverable);
1447 }
1448 
1449 /** @brief implements the get Sensor event enable command
1450  *  @param sensorNumber - sensor number
1451  *
1452  *  @returns IPMI completion code plus response data
1453  *   - enabled               - Sensor Event messages
1454  *   - assertionEnabledLsb   - Assertion event messages
1455  *   - assertionEnabledMsb   - Assertion event messages
1456  *   - deassertionEnabledLsb - Deassertion event messages
1457  *   - deassertionEnabledMsb - Deassertion event messages
1458  */
1459 
1460 ipmi::RspType<uint8_t, // enabled
1461               uint8_t, // assertionEnabledLsb
1462               uint8_t, // assertionEnabledMsb
1463               uint8_t, // deassertionEnabledLsb
1464               uint8_t> // deassertionEnabledMsb
ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx,uint8_t sensorNum)1465     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1466 {
1467     std::string connection;
1468     std::string path;
1469 
1470     uint8_t enabled = 0;
1471     uint8_t assertionEnabledLsb = 0;
1472     uint8_t assertionEnabledMsb = 0;
1473     uint8_t deassertionEnabledLsb = 0;
1474     uint8_t deassertionEnabledMsb = 0;
1475 
1476     if (sensorNum == reservedSensorNumber)
1477     {
1478         return ipmi::responseInvalidFieldRequest();
1479     }
1480 
1481     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1482     if (status)
1483     {
1484         return ipmi::response(status);
1485     }
1486 
1487 #ifdef FEATURE_HYBRID_SENSORS
1488     if (auto sensor = findStaticSensor(path);
1489         sensor != ipmi::sensor::sensors.end() &&
1490         getSensorEventTypeFromPath(path) !=
1491             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1492     {
1493         enabled = static_cast<uint8_t>(
1494             IPMISensorEventEnableByte2::sensorScanningEnable);
1495         uint16_t assertionEnabled = 0;
1496         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1497                                       ->second.begin()
1498                                       ->second.second)
1499         {
1500             assertionEnabled |= (1 << offsetValMap.first);
1501         }
1502         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1503         assertionEnabledMsb =
1504             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1505 
1506         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1507                                      assertionEnabledMsb, deassertionEnabledLsb,
1508                                      deassertionEnabledMsb);
1509     }
1510 #endif
1511 
1512     DbusInterfaceMap sensorMap;
1513     if (!getSensorMap(ctx, connection, path, sensorMap))
1514     {
1515         return ipmi::responseResponseError();
1516     }
1517 
1518     auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1519     auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
1520     if ((warningInterface != sensorMap.end()) ||
1521         (criticalInterface != sensorMap.end()))
1522     {
1523         enabled = static_cast<uint8_t>(
1524             IPMISensorEventEnableByte2::sensorScanningEnable);
1525         if (warningInterface != sensorMap.end())
1526         {
1527             auto& warningMap = warningInterface->second;
1528 
1529             auto warningHigh = warningMap.find(
1530                 SensorThresholdWarning::property_names::warning_high);
1531             auto warningLow = warningMap.find(
1532                 SensorThresholdWarning::property_names::warning_low);
1533             if (warningHigh != warningMap.end())
1534             {
1535                 double value =
1536                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1537                 if (std::isfinite(value))
1538                 {
1539                     assertionEnabledLsb |= static_cast<uint8_t>(
1540                         IPMISensorEventEnableThresholds::
1541                             upperNonCriticalGoingHigh);
1542                     deassertionEnabledLsb |= static_cast<uint8_t>(
1543                         IPMISensorEventEnableThresholds::
1544                             upperNonCriticalGoingLow);
1545                 }
1546             }
1547             if (warningLow != warningMap.end())
1548             {
1549                 double value =
1550                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1551                 if (std::isfinite(value))
1552                 {
1553                     assertionEnabledLsb |= static_cast<uint8_t>(
1554                         IPMISensorEventEnableThresholds::
1555                             lowerNonCriticalGoingLow);
1556                     deassertionEnabledLsb |= static_cast<uint8_t>(
1557                         IPMISensorEventEnableThresholds::
1558                             lowerNonCriticalGoingHigh);
1559                 }
1560             }
1561         }
1562         if (criticalInterface != sensorMap.end())
1563         {
1564             auto& criticalMap = criticalInterface->second;
1565 
1566             auto criticalHigh = criticalMap.find(
1567                 SensorThresholdCritical::property_names::critical_high);
1568             auto criticalLow = criticalMap.find(
1569                 SensorThresholdCritical::property_names::critical_low);
1570 
1571             if (criticalHigh != criticalMap.end())
1572             {
1573                 double value =
1574                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1575                 if (std::isfinite(value))
1576                 {
1577                     assertionEnabledMsb |= static_cast<uint8_t>(
1578                         IPMISensorEventEnableThresholds::
1579                             upperCriticalGoingHigh);
1580                     deassertionEnabledMsb |= static_cast<uint8_t>(
1581                         IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1582                 }
1583             }
1584             if (criticalLow != criticalMap.end())
1585             {
1586                 double value =
1587                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1588                 if (std::isfinite(value))
1589                 {
1590                     assertionEnabledLsb |= static_cast<uint8_t>(
1591                         IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1592                     deassertionEnabledLsb |= static_cast<uint8_t>(
1593                         IPMISensorEventEnableThresholds::
1594                             lowerCriticalGoingHigh);
1595                 }
1596             }
1597         }
1598     }
1599 
1600     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1601                                  assertionEnabledMsb, deassertionEnabledLsb,
1602                                  deassertionEnabledMsb);
1603 }
1604 
1605 /** @brief implements the get Sensor event status command
1606  *  @param sensorNumber - sensor number, FFh = reserved
1607  *
1608  *  @returns IPMI completion code plus response data
1609  *   - sensorEventStatus - Sensor Event messages state
1610  *   - assertions        - Assertion event messages
1611  *   - deassertions      - Deassertion event messages
1612  */
1613 ipmi::RspType<uint8_t,         // sensorEventStatus
1614               std::bitset<16>, // assertions
1615               std::bitset<16>  // deassertion
1616               >
ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx,uint8_t sensorNum)1617     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1618 {
1619     if (sensorNum == reservedSensorNumber)
1620     {
1621         return ipmi::responseInvalidFieldRequest();
1622     }
1623 
1624     std::string connection;
1625     std::string path;
1626     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1627     if (status)
1628     {
1629         lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1630                    "sensor number: {SENSOR_NUM}",
1631                    "SENSOR_NUM", sensorNum);
1632         return ipmi::response(status);
1633     }
1634 
1635 #ifdef FEATURE_HYBRID_SENSORS
1636     if (auto sensor = findStaticSensor(path);
1637         sensor != ipmi::sensor::sensors.end() &&
1638         getSensorEventTypeFromPath(path) !=
1639             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1640     {
1641         auto response = ipmi::sensor::get::mapDbusToAssertion(
1642             sensor->second, path, sensor->second.sensorInterface);
1643         std::bitset<16> assertions;
1644         // deassertions are not used.
1645         std::bitset<16> deassertions = 0;
1646         uint8_t sensorEventStatus = 0;
1647         if (response.readingOrStateUnavailable)
1648         {
1649             sensorEventStatus |= static_cast<uint8_t>(
1650                 IPMISensorReadingByte2::readingStateUnavailable);
1651         }
1652         if (response.scanningEnabled)
1653         {
1654             sensorEventStatus |= static_cast<uint8_t>(
1655                 IPMISensorReadingByte2::sensorScanningEnable);
1656         }
1657         if (response.allEventMessagesEnabled)
1658         {
1659             sensorEventStatus |= static_cast<uint8_t>(
1660                 IPMISensorReadingByte2::eventMessagesEnable);
1661         }
1662         assertions |= response.discreteReadingSensorStates << 8;
1663         assertions |= response.thresholdLevelsStates;
1664         return ipmi::responseSuccess(sensorEventStatus, assertions,
1665                                      deassertions);
1666     }
1667 #endif
1668 
1669     DbusInterfaceMap sensorMap;
1670     if (!getSensorMap(ctx, connection, path, sensorMap))
1671     {
1672         lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1673                    "sensor path: {SENSOR_PATH}",
1674                    "SENSOR_PATH", path);
1675         return ipmi::responseResponseError();
1676     }
1677 
1678     uint8_t sensorEventStatus =
1679         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1680     std::bitset<16> assertions = 0;
1681     std::bitset<16> deassertions = 0;
1682 
1683     // handle VR typed sensor
1684     auto vrInterface = sensorMap.find(sensor::vrInterface);
1685     if (vrInterface != sensorMap.end())
1686     {
1687         if (!sensor::getVrEventStatus(ctx, connection, path,
1688                                       vrInterface->second, assertions))
1689         {
1690             return ipmi::responseResponseError();
1691         }
1692 
1693         // both Event Message and Sensor Scanning are disable for VR.
1694         sensorEventStatus = 0;
1695         return ipmi::responseSuccess(sensorEventStatus, assertions,
1696                                      deassertions);
1697     }
1698 
1699     auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1700     auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
1701 
1702     std::optional<bool> criticalDeassertHigh = thresholdDeassertMap
1703         [path][SensorThresholdCritical::property_names::critical_alarm_high];
1704     std::optional<bool> criticalDeassertLow = thresholdDeassertMap
1705         [path][SensorThresholdCritical::property_names::critical_alarm_low];
1706     std::optional<bool> warningDeassertHigh = thresholdDeassertMap
1707         [path][SensorThresholdWarning::property_names::warning_alarm_high];
1708     std::optional<bool> warningDeassertLow = thresholdDeassertMap
1709         [path][SensorThresholdWarning::property_names::warning_alarm_low];
1710 
1711     if (criticalDeassertHigh && !*criticalDeassertHigh)
1712     {
1713         deassertions.set(static_cast<size_t>(
1714             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1715     }
1716     if (criticalDeassertLow && !*criticalDeassertLow)
1717     {
1718         deassertions.set(static_cast<size_t>(
1719             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1720     }
1721     if (warningDeassertHigh && !*warningDeassertHigh)
1722     {
1723         deassertions.set(static_cast<size_t>(
1724             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1725     }
1726     if (warningDeassertLow && !*warningDeassertLow)
1727     {
1728         deassertions.set(static_cast<size_t>(
1729             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1730     }
1731     if ((warningInterface != sensorMap.end()) ||
1732         (criticalInterface != sensorMap.end()))
1733     {
1734         sensorEventStatus = static_cast<size_t>(
1735             IPMISensorEventEnableByte2::eventMessagesEnable);
1736         if (warningInterface != sensorMap.end())
1737         {
1738             auto& warningMap = warningInterface->second;
1739 
1740             auto warningHigh = warningMap.find(
1741                 SensorThresholdWarning::property_names::warning_alarm_high);
1742             auto warningLow = warningMap.find(
1743                 SensorThresholdWarning::property_names::warning_alarm_low);
1744             auto warningHighAlarm = false;
1745             auto warningLowAlarm = false;
1746 
1747             if (warningHigh != warningMap.end())
1748             {
1749                 warningHighAlarm = std::get<bool>(warningHigh->second);
1750             }
1751             if (warningLow != warningMap.end())
1752             {
1753                 warningLowAlarm = std::get<bool>(warningLow->second);
1754             }
1755             if (warningHighAlarm)
1756             {
1757                 assertions.set(static_cast<size_t>(
1758                     IPMIGetSensorEventEnableThresholds::
1759                         upperNonCriticalGoingHigh));
1760             }
1761             if (warningLowAlarm)
1762             {
1763                 assertions.set(static_cast<size_t>(
1764                     IPMIGetSensorEventEnableThresholds::
1765                         lowerNonCriticalGoingLow));
1766             }
1767         }
1768         if (criticalInterface != sensorMap.end())
1769         {
1770             auto& criticalMap = criticalInterface->second;
1771 
1772             auto criticalHigh = criticalMap.find(
1773                 SensorThresholdCritical::property_names::critical_alarm_high);
1774             auto criticalLow = criticalMap.find(
1775                 SensorThresholdCritical::property_names::critical_alarm_low);
1776             auto criticalHighAlarm = false;
1777             auto criticalLowAlarm = false;
1778 
1779             if (criticalHigh != criticalMap.end())
1780             {
1781                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1782             }
1783             if (criticalLow != criticalMap.end())
1784             {
1785                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1786             }
1787             if (criticalHighAlarm)
1788             {
1789                 assertions.set(static_cast<size_t>(
1790                     IPMIGetSensorEventEnableThresholds::
1791                         upperCriticalGoingHigh));
1792             }
1793             if (criticalLowAlarm)
1794             {
1795                 assertions.set(static_cast<size_t>(
1796                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1797             }
1798         }
1799     }
1800 
1801     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1802 }
1803 
1804 // Construct a type 1 SDR for threshold sensor.
constructSensorSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataFullRecord & record)1805 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1806                                  get_sdr::SensorDataFullRecord& record)
1807 {
1808     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1809     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1810 
1811     record.header.recordId = recordID;
1812     record.header.sdrVersion = ipmiSdrVersion;
1813     record.header.recordType = get_sdr::SENSOR_DATA_FULL_RECORD;
1814     record.header.recordLength = sizeof(get_sdr::SensorDataFullRecord) -
1815                                  sizeof(get_sdr::SensorDataRecordHeader);
1816     record.key.ownerId = bmcI2CAddr;
1817     record.key.ownerLun = lun;
1818     record.key.sensorNumber = sensornumber;
1819 }
constructSensorSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataFullRecord & record)1820 bool constructSensorSdr(
1821     ipmi::Context::ptr ctx,
1822     const std::unordered_set<std::string>& ipmiDecoratorPaths,
1823     uint16_t sensorNum, uint16_t recordID, const std::string& service,
1824     const std::string& path, get_sdr::SensorDataFullRecord& record)
1825 {
1826     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1827 
1828     DbusInterfaceMap sensorMap;
1829     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1830     {
1831         lg2::error("Failed to update sensor map for threshold sensor, "
1832                    "service: {SERVICE}, path: {PATH}",
1833                    "SERVICE", service, "PATH", path);
1834         return false;
1835     }
1836 
1837     record.body.sensorCapabilities = 0x68; // auto rearm - todo hysteresis
1838     record.body.sensorType = getSensorTypeFromPath(path);
1839     std::string type = getSensorTypeStringFromPath(path);
1840     auto typeCstr = type.c_str();
1841     auto findUnits = sensorUnits.find(typeCstr);
1842     if (findUnits != sensorUnits.end())
1843     {
1844         record.body.sensorUnits2Base = static_cast<uint8_t>(findUnits->second);
1845     } // else default 0x0 unspecified
1846 
1847     record.body.eventReadingType = getSensorEventTypeFromPath(path);
1848 
1849     auto sensorObject = sensorMap.find(SensorValue::interface);
1850     if (sensorObject == sensorMap.end())
1851     {
1852         lg2::error("constructSensorSdr: sensorObject error");
1853         return false;
1854     }
1855 
1856     uint8_t entityId = 0;
1857     uint8_t entityInstance = 0x01;
1858 
1859     // follow the association chain to get the parent board's entityid and
1860     // entityInstance
1861     updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1862                               entityInstance);
1863 
1864     record.body.entityId = entityId;
1865     record.body.entityInstance = entityInstance;
1866 
1867     double max = 0;
1868     double min = 0;
1869     getSensorMaxMin(sensorMap, max, min);
1870 
1871     int16_t mValue = 0;
1872     int8_t rExp = 0;
1873     int16_t bValue = 0;
1874     int8_t bExp = 0;
1875     bool bSigned = false;
1876 
1877     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1878     {
1879         lg2::error("constructSensorSdr: getSensorAttributes error");
1880         return false;
1881     }
1882 
1883     // The record.body is a struct SensorDataFullRecordBody
1884     // from sensorhandler.hpp in phosphor-ipmi-host.
1885     // The meaning of these bits appears to come from
1886     // table 43.1 of the IPMI spec.
1887     // The above 5 sensor attributes are stuffed in as follows:
1888     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1889     // Byte 22-24 are for other purposes
1890     // Byte 25 = MMMMMMMM = LSB of M
1891     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1892     // Byte 27 = BBBBBBBB = LSB of B
1893     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1894     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1895     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1896 
1897     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1898     record.body.mLsb = mValue & 0xFF;
1899 
1900     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1901     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1902 
1903     // move the smallest bit of the MSB into place (bit 9)
1904     // the MSbs are bits 7:8 in mMsbAndToLerance
1905     record.body.mMsbAndTolerance = (mBitSign << 7) | (mBitNine << 6);
1906 
1907     record.body.bLsb = bValue & 0xFF;
1908 
1909     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1910     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1911 
1912     // move the smallest bit of the MSB into place (bit 9)
1913     // the MSbs are bits 7:8 in bMsbAndAccuracyLsb
1914     record.body.bMsbAndAccuracyLsb = (bBitSign << 7) | (bBitNine << 6);
1915 
1916     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1917     uint8_t rExpBits = rExp & 0x07;
1918 
1919     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1920     uint8_t bExpBits = bExp & 0x07;
1921 
1922     // move rExp and bExp into place
1923     record.body.rbExponents =
1924         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1925 
1926     // Set the analog reading byte interpretation accordingly
1927     record.body.sensorUnits1 = (bSigned ? 1 : 0) << 7;
1928 
1929     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1930     // These seem redundant, but derivable from the above 5 attributes
1931     // Original comment said "todo fill out rest of units"
1932 
1933     // populate sensor name from path
1934     auto name = sensor::parseSdrIdFromPath(path);
1935     get_sdr::body::setIdStrLen(name.size(), record.body);
1936     get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
1937     std::memcpy(record.body.idString, name.c_str(),
1938                 std::min(name.length() + 1, sizeof(record.body.idString)));
1939 
1940     // Remember the sensor name, as determined for this sensor number
1941     details::sdrStatsTable.updateName(sensorNum, name);
1942 
1943     bool sensorSettable = false;
1944     auto mutability =
1945         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1946     if (mutability != sensorMap.end())
1947     {
1948         sensorSettable =
1949             mappedVariant<bool>(mutability->second, "Mutable", false);
1950     }
1951     get_sdr::body::initSettableState(sensorSettable, record.body);
1952 
1953     // Grant write permission to sensors deemed externally settable
1954     details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
1955 
1956     IPMIThresholds thresholdData;
1957     try
1958     {
1959         thresholdData = getIPMIThresholds(sensorMap);
1960     }
1961     catch (const std::exception&)
1962     {
1963         lg2::error("constructSensorSdr: getIPMIThresholds error");
1964         return false;
1965     }
1966 
1967     if (thresholdData.criticalHigh)
1968     {
1969         record.body.upperCriticalThreshold = *thresholdData.criticalHigh;
1970         record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
1971             IPMISensorEventEnableThresholds::criticalThreshold);
1972         record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
1973             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1974         record.body.supportedAssertions[1] |= static_cast<uint8_t>(
1975             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1976         record.body.discreteReadingSettingMask[0] |=
1977             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1978     }
1979     if (thresholdData.warningHigh)
1980     {
1981         record.body.upperNoncriticalThreshold = *thresholdData.warningHigh;
1982         record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
1983             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1984         record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
1985             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1986         record.body.supportedAssertions[0] |= static_cast<uint8_t>(
1987             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1988         record.body.discreteReadingSettingMask[0] |=
1989             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1990     }
1991     if (thresholdData.criticalLow)
1992     {
1993         record.body.lowerCriticalThreshold = *thresholdData.criticalLow;
1994         record.body.supportedAssertions[1] |= static_cast<uint8_t>(
1995             IPMISensorEventEnableThresholds::criticalThreshold);
1996         record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
1997             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1998         record.body.supportedAssertions[0] |= static_cast<uint8_t>(
1999             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
2000         record.body.discreteReadingSettingMask[0] |=
2001             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
2002     }
2003     if (thresholdData.warningLow)
2004     {
2005         record.body.lowerNoncriticalThreshold = *thresholdData.warningLow;
2006         record.body.supportedAssertions[1] |= static_cast<uint8_t>(
2007             IPMISensorEventEnableThresholds::nonCriticalThreshold);
2008         record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
2009             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2010         record.body.supportedAssertions[0] |= static_cast<uint8_t>(
2011             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2012         record.body.discreteReadingSettingMask[0] |=
2013             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
2014     }
2015 
2016     // everything that is readable is setable
2017     record.body.discreteReadingSettingMask[1] =
2018         record.body.discreteReadingSettingMask[0];
2019     return true;
2020 }
2021 
2022 #ifdef FEATURE_HYBRID_SENSORS
2023 // Construct a type 1 SDR for discrete Sensor typed sensor.
constructStaticSensorSdr(ipmi::Context::ptr,uint16_t sensorNum,uint16_t recordID,ipmi::sensor::IdInfoMap::const_iterator sensor,get_sdr::SensorDataFullRecord & record)2024 void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
2025                               uint16_t recordID,
2026                               ipmi::sensor::IdInfoMap::const_iterator sensor,
2027                               get_sdr::SensorDataFullRecord& record)
2028 {
2029     constructSensorSdrHeaderKey(sensorNum, recordID, record);
2030 
2031     record.body.entityId = sensor->second.entityType;
2032     record.body.sensorType = sensor->second.sensorType;
2033     record.body.eventReadingType = sensor->second.sensorReadingType;
2034     record.body.entityInstance = sensor->second.instance;
2035     if (ipmi::sensor::Mutability::Write ==
2036         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2037     {
2038         get_sdr::body::initSettableState(true, record.body);
2039     }
2040 
2041     auto idString = sensor->second.sensorName;
2042 
2043     if (idString.empty())
2044     {
2045         idString = sensor->second.sensorNameFunc(sensor->second);
2046     }
2047 
2048     if (idString.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2049     {
2050         get_sdr::body::setIdStrLen(FULL_RECORD_ID_STR_MAX_LENGTH, record.body);
2051     }
2052     else
2053     {
2054         get_sdr::body::setIdStrLen(idString.length(), record.body);
2055     }
2056     get_sdr::body::setIdType(3, &record.body); // "8-bit ASCII + Latin 1"
2057     std::strncpy(record.body.idString, idString.c_str(),
2058                  get_sdr::body::setIdStrLen(record.body));
2059 }
2060 #endif
2061 
2062 // Construct type 3 SDR header and key (for VR and other discrete sensors)
constructEventSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataEventRecord & record)2063 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2064                                 get_sdr::SensorDataEventRecord& record)
2065 {
2066     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2067     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2068 
2069     record.header.recordId = recordID;
2070     record.header.sdrVersion = ipmiSdrVersion;
2071     record.header.recordType = get_sdr::SENSOR_DATA_EVENT_RECORD;
2072     record.header.recordLength = sizeof(get_sdr::SensorDataEventRecord) -
2073                                  sizeof(get_sdr::SensorDataRecordHeader);
2074     record.key.ownerId = bmcI2CAddr;
2075     record.key.ownerLun = lun;
2076     record.key.sensorNumber = sensornumber;
2077 
2078     record.body.entityId = 0x00;
2079     record.body.entityInstance = 0x01;
2080 }
2081 
2082 // Construct a type 3 SDR for VR typed sensor(daemon).
constructVrSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataEventRecord & record)2083 bool constructVrSdr(ipmi::Context::ptr ctx,
2084                     const std::unordered_set<std::string>& ipmiDecoratorPaths,
2085                     uint16_t sensorNum, uint16_t recordID,
2086                     const std::string& service, const std::string& path,
2087                     get_sdr::SensorDataEventRecord& record)
2088 {
2089     constructEventSdrHeaderKey(sensorNum, recordID, record);
2090 
2091     DbusInterfaceMap sensorMap;
2092     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2093     {
2094         lg2::error("Failed to update sensor map for VR sensor, "
2095                    "service: {SERVICE}, path: {PATH}",
2096                    "SERVICE", service, "PATH", path);
2097         return false;
2098     }
2099     // follow the association chain to get the parent board's entityid and
2100     // entityInstance
2101     updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2102                               record.body.entityId, record.body.entityInstance);
2103 
2104     // Sensor type is hardcoded as a module/board type instead of parsing from
2105     // sensor path. This is because VR control is allocated in an independent
2106     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2107     // types.
2108     static constexpr const uint8_t moduleBoardType = 0x15;
2109     record.body.sensorType = moduleBoardType;
2110     record.body.eventReadingType = 0x00;
2111 
2112     record.body.sensorRecordSharing1 = 0x00;
2113     record.body.sensorRecordSharing2 = 0x00;
2114 
2115     // populate sensor name from path
2116     auto name = sensor::parseSdrIdFromPath(path);
2117     int nameSize = std::min(name.size(), sizeof(record.body.idString));
2118     get_sdr::body::setIdStrLen(nameSize, record.body);
2119     get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
2120     std::memset(record.body.idString, 0x00, sizeof(record.body.idString));
2121     std::memcpy(record.body.idString, name.c_str(), nameSize);
2122 
2123     // Remember the sensor name, as determined for this sensor number
2124     details::sdrStatsTable.updateName(sensorNum, name);
2125 
2126     return true;
2127 }
2128 
getNumberOfSensors()2129 uint16_t getNumberOfSensors()
2130 {
2131     return std::min(getSensorTree().size(), maxIPMISensors);
2132 }
2133 
getSensorDataRecord(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,std::vector<uint8_t> & recordData,uint16_t recordID,uint8_t readBytes=std::numeric_limits<uint8_t>::max ())2134 static int getSensorDataRecord(
2135     ipmi::Context::ptr ctx,
2136     const std::unordered_set<std::string>& ipmiDecoratorPaths,
2137     std::vector<uint8_t>& recordData, uint16_t recordID,
2138     uint8_t readBytes = std::numeric_limits<uint8_t>::max())
2139 {
2140     recordData.clear();
2141     size_t lastRecord = ipmi::getNumberOfSensors() +
2142                         ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2143     uint16_t nextRecord(recordID + 1);
2144 
2145     if (recordID == lastRecordIndex)
2146     {
2147         recordID = lastRecord;
2148     }
2149     if (recordID == lastRecord)
2150     {
2151         nextRecord = lastRecordIndex;
2152     }
2153     if (recordID > lastRecord)
2154     {
2155         lg2::error("getSensorDataRecord: recordID > lastRecord error");
2156         return GENERAL_ERROR;
2157     }
2158     if (recordID >= ipmi::getNumberOfSensors())
2159     {
2160         if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2161                                                                recordData);
2162             err < 0)
2163         {
2164             return lastRecordIndex;
2165         }
2166         return nextRecord;
2167     }
2168 
2169     // Perform a incremental scan of the SDR Record ID's and translate the
2170     // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2171     // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2172     // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2173     // which has special meaning.
2174     std::string connection;
2175     std::string path;
2176     std::vector<std::string> interfaces;
2177     uint16_t sensNumFromRecID{recordID};
2178     if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
2179     {
2180         // LUN 0 has one reserved sensor number. Compensate here by adding one
2181         // to the record ID
2182         sensNumFromRecID = recordID + 1;
2183         ctx->lun = lun1;
2184     }
2185     else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
2186     {
2187         // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2188         // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2189         // rules governing its use.
2190         sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
2191         ctx->lun = lun3;
2192     }
2193 
2194     auto status =
2195         getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2196                             connection, path, &interfaces);
2197     if (status)
2198     {
2199         lg2::error("getSensorDataRecord: getSensorConnection error");
2200         return GENERAL_ERROR;
2201     }
2202     uint16_t sensorNum = getSensorNumberFromPath(path);
2203     // Return an error on LUN 2 assingments, and any sensor number beyond the
2204     // range of LUN 3
2205     if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2206         (sensorNum > lun3MaxSensorNum))
2207     {
2208         lg2::error("getSensorDataRecord: invalidSensorNumber");
2209         return GENERAL_ERROR;
2210     }
2211     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2212     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2213 
2214     if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2215         (lun != ctx->lun))
2216     {
2217         lg2::error("getSensorDataRecord: sensor record mismatch");
2218         return GENERAL_ERROR;
2219     }
2220 
2221     // Construct full record (SDR type 1) for the threshold sensors
2222     if (std::find(interfaces.begin(), interfaces.end(),
2223                   SensorValue::interface) != interfaces.end())
2224     {
2225         get_sdr::SensorDataFullRecord record = {};
2226 
2227         // If the request doesn't read SDR body, construct only header and key
2228         // part to avoid additional DBus transaction.
2229         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2230         {
2231             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2232         }
2233         else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2234                                      recordID, connection, path, record))
2235         {
2236             return GENERAL_ERROR;
2237         }
2238 
2239         recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2240                           reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2241 
2242         return nextRecord;
2243     }
2244 
2245 #ifdef FEATURE_HYBRID_SENSORS
2246     if (auto sensor = findStaticSensor(path);
2247         sensor != ipmi::sensor::sensors.end() &&
2248         getSensorEventTypeFromPath(path) !=
2249             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2250     {
2251         get_sdr::SensorDataFullRecord record = {};
2252 
2253         // If the request doesn't read SDR body, construct only header and key
2254         // part to avoid additional DBus transaction.
2255         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2256         {
2257             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2258         }
2259         else
2260         {
2261             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2262         }
2263 
2264         recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2265                           reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2266 
2267         return nextRecord;
2268     }
2269 #endif
2270 
2271     // Contruct SDR type 3 record for VR sensor (daemon)
2272     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2273         interfaces.end())
2274     {
2275         get_sdr::SensorDataEventRecord record = {};
2276 
2277         // If the request doesn't read SDR body, construct only header and key
2278         // part to avoid additional DBus transaction.
2279         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2280         {
2281             constructEventSdrHeaderKey(sensorNum, recordID, record);
2282         }
2283         else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2284                                  connection, path, record))
2285         {
2286             return GENERAL_ERROR;
2287         }
2288         recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2289                           reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2290     }
2291 
2292     return nextRecord;
2293 }
2294 
2295 /** @brief implements the get SDR Info command
2296  *  @param operation : 0 or not supplied returns sensor count
2297  *                     1 return SDR count
2298  *
2299  *  @returns IPMI completion code plus response data
2300  *   - sdrCount - sensor/SDR count
2301  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2302  */
2303 static ipmi::RspType<uint8_t, // respcount
2304                      uint8_t, // dynamic population flags
2305                      uint32_t // last time a sensor was added
2306                      >
ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,std::optional<uint8_t> operation)2307     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2308                                std::optional<uint8_t> operation)
2309 {
2310     auto& sensorTree{getSensorTree()};
2311     uint8_t sdrCount{};
2312     // Sensors are dynamically allocated
2313     uint8_t lunsAndDynamicPopulation{0x80};
2314     constexpr uint8_t getSdrCount{1};
2315     constexpr uint8_t getSensorCount{0};
2316 
2317     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2318     {
2319         return ipmi::responseResponseError();
2320     }
2321     uint16_t numSensors{ipmi::getNumberOfSensors()};
2322     if (operation.value_or(0) == getSdrCount)
2323     {
2324         sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2325     }
2326     else if (operation.value_or(0) == getSensorCount)
2327     {
2328         // Return the number of sensors attached to the LUN
2329         if ((ctx->lun == lun0) && (numSensors > 0))
2330         {
2331             sdrCount =
2332                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2333         }
2334         else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
2335         {
2336             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2337                            ? maxSensorsPerLUN
2338                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2339         }
2340         else if (ctx->lun == lun3)
2341         {
2342             if (numSensors <= maxIPMISensors)
2343             {
2344                 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2345                            maxSensorsPerLUN;
2346             }
2347             else
2348             {
2349                 throw std::out_of_range(
2350                     "Maximum number of IPMI sensors exceeded.");
2351             }
2352         }
2353     }
2354     else
2355     {
2356         return ipmi::responseInvalidFieldRequest();
2357     }
2358 
2359     // Flag which LUNs have sensors associated
2360     if (numSensors > 0)
2361     {
2362         lunsAndDynamicPopulation |= 1;
2363     }
2364     if (numSensors > maxSensorsPerLUN)
2365     {
2366         lunsAndDynamicPopulation |= 2;
2367     }
2368     if (numSensors >= (maxSensorsPerLUN * 2))
2369     {
2370         lunsAndDynamicPopulation |= 8;
2371     }
2372     if (numSensors > maxIPMISensors)
2373     {
2374         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2375     }
2376 
2377     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2378                                  sdrLastAdd);
2379 }
2380 
2381 /* end sensor commands */
2382 
2383 /* storage commands */
2384 
2385 ipmi::RspType<uint8_t,  // sdr version
2386               uint16_t, // record count
2387               uint16_t, // free space
2388               uint32_t, // most recent addition
2389               uint32_t, // most recent erase
2390               uint8_t   // operationSupport
2391               >
ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)2392     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2393 {
2394     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2395     uint16_t recordCount =
2396         ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
2397 
2398     uint8_t operationSupport = static_cast<uint8_t>(
2399         SdrRepositoryInfoOps::overflow); // write not supported
2400 
2401     operationSupport |=
2402         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2403     operationSupport |= static_cast<uint8_t>(
2404         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2405     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2406                                  unspecifiedFreeSpace, sdrLastAdd,
2407                                  sdrLastRemove, operationSupport);
2408 }
2409 
2410 /** @brief implements the get SDR allocation info command
2411  *
2412  *  @returns IPMI completion code plus response data
2413  *   - allocUnits    - Number of possible allocation units
2414  *   - allocUnitSize - Allocation unit size in bytes.
2415  *   - allocUnitFree - Number of free allocation units
2416  *   - allocUnitLargestFree - Largest free block in allocation units
2417  *   - maxRecordSize    - Maximum record size in allocation units.
2418  */
2419 ipmi::RspType<uint16_t, // allocUnits
2420               uint16_t, // allocUnitSize
2421               uint16_t, // allocUnitFree
2422               uint16_t, // allocUnitLargestFree
2423               uint8_t   // maxRecordSize
2424               >
ipmiStorageGetSDRAllocationInfo()2425     ipmiStorageGetSDRAllocationInfo()
2426 {
2427     // 0000h unspecified number of alloc units
2428     constexpr uint16_t allocUnits = 0;
2429 
2430     constexpr uint16_t allocUnitFree = 0;
2431     constexpr uint16_t allocUnitLargestFree = 0;
2432     // only allow one block at a time
2433     constexpr uint8_t maxRecordSize = 1;
2434 
2435     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2436                                  allocUnitLargestFree, maxRecordSize);
2437 }
2438 
2439 /** @brief implements the reserve SDR command
2440  *  @returns IPMI completion code plus response data
2441  *   - sdrReservationID
2442  */
ipmiStorageReserveSDR()2443 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2444 {
2445     sdrReservationID++;
2446     if (sdrReservationID == 0)
2447     {
2448         sdrReservationID++;
2449     }
2450 
2451     return ipmi::responseSuccess(sdrReservationID);
2452 }
2453 
2454 ipmi::RspType<uint16_t,            // next record ID
2455               std::vector<uint8_t> // payload
2456               >
ipmiStorageGetSDR(ipmi::Context::ptr ctx,uint16_t reservationID,uint16_t recordID,uint8_t offset,uint8_t bytesToRead)2457     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2458                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2459 {
2460     // reservation required for partial reads with non zero offset into
2461     // record
2462     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2463     {
2464         lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
2465         return ipmi::responseInvalidReservationId();
2466     }
2467 
2468     auto& sensorTree = getSensorTree();
2469     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2470     {
2471         lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
2472         return ipmi::responseResponseError();
2473     }
2474 
2475     auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2476 
2477     std::vector<uint8_t> record;
2478     int nextRecordId = getSensorDataRecord(
2479         ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2480         record, recordID, offset + bytesToRead);
2481 
2482     if (nextRecordId < 0)
2483     {
2484         lg2::error("ipmiStorageGetSDR: fail to get SDR");
2485         return ipmi::responseInvalidFieldRequest();
2486     }
2487     get_sdr::SensorDataRecordHeader* hdr =
2488         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2489     if (!hdr)
2490     {
2491         lg2::error("ipmiStorageGetSDR: record header is null");
2492         return ipmi::responseSuccess(nextRecordId, record);
2493     }
2494 
2495     size_t sdrLength =
2496         sizeof(get_sdr::SensorDataRecordHeader) + hdr->recordLength;
2497     if (offset >= sdrLength)
2498     {
2499         lg2::error("ipmiStorageGetSDR: offset is outside the record");
2500         return ipmi::responseParmOutOfRange();
2501     }
2502     if (sdrLength < (offset + bytesToRead))
2503     {
2504         bytesToRead = sdrLength - offset;
2505     }
2506 
2507     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2508     if (!respStart)
2509     {
2510         lg2::error("ipmiStorageGetSDR: record is null");
2511         return ipmi::responseSuccess(nextRecordId, record);
2512     }
2513 
2514     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2515 
2516     return ipmi::responseSuccess(nextRecordId, recordData);
2517 }
2518 namespace dcmi
2519 {
2520 
2521 std::tuple<uint8_t,                // Total of instance sensors
2522            std::vector<sensorInfo> // The list of sensors
2523            >
getSensorsByEntityId(ipmi::Context::ptr ctx,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2524     getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2525                          uint8_t entityInstance, uint8_t instanceStart)
2526 {
2527     std::vector<sensorInfo> sensorList;
2528     uint8_t totalInstSensor = 0;
2529     auto match = ipmi::dcmi::validEntityId.find(entityId);
2530 
2531     if (match == ipmi::dcmi::validEntityId.end())
2532     {
2533         return std::make_tuple(totalInstSensor, sensorList);
2534     }
2535 
2536     auto& sensorTree = getSensorTree();
2537     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2538     {
2539         return std::make_tuple(totalInstSensor, sensorList);
2540     }
2541 
2542     auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2543 
2544     size_t invalidSensorNumberErrCount = 0;
2545     for (const auto& sensor : sensorTree)
2546     {
2547         const std::string& sensorObjPath = sensor.first;
2548         const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2549 
2550         /*
2551          * In the DCMI specification, it only supports the sensor type is 0x01
2552          * (temperature type) for both Get Sensor Info and Get Temperature
2553          * Readings commands.
2554          */
2555         if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2556         {
2557             continue;
2558         }
2559 
2560         const auto& connection = sensor.second.begin()->first;
2561         DbusInterfaceMap sensorMap;
2562 
2563         if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2564                           sensorMapSdrUpdatePeriod))
2565         {
2566             lg2::error("Failed to update sensor map for threshold sensor, "
2567                        "service: {SERVICE}, path: {PATH}",
2568                        "SERVICE", connection, "PATH", sensorObjPath);
2569             continue;
2570         }
2571 
2572         uint8_t entityIdValue = 0;
2573         uint8_t entityInstanceValue = 0;
2574 
2575         /*
2576          * Get the Entity ID, Entity Instance information which are configured
2577          * in the Entity-Manger.
2578          */
2579         updateIpmiFromAssociation(
2580             sensorObjPath,
2581             ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2582             sensorMap, entityIdValue, entityInstanceValue);
2583 
2584         if (entityIdValue == match->first || entityIdValue == match->second)
2585         {
2586             totalInstSensor++;
2587 
2588             /*
2589              * When Entity Instance parameter is not 0, we only get the first
2590              * sensor whose Entity Instance number is equal input Entity
2591              * Instance parameter.
2592              */
2593             if (entityInstance)
2594             {
2595                 if (!sensorList.empty())
2596                 {
2597                     continue;
2598                 }
2599 
2600                 if (entityInstanceValue == entityInstance)
2601                 {
2602                     auto recordId = getSensorNumberFromPath(sensorObjPath);
2603                     if (recordId == invalidSensorNumber)
2604                     {
2605                         ++invalidSensorNumberErrCount;
2606                         continue;
2607                     }
2608                     sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2609                                             recordId, entityIdValue,
2610                                             entityInstanceValue);
2611                 }
2612             }
2613             else if (entityInstanceValue >= instanceStart)
2614             {
2615                 auto recordId = getSensorNumberFromPath(sensorObjPath);
2616                 if (recordId == invalidSensorNumber)
2617                 {
2618                     ++invalidSensorNumberErrCount;
2619                     continue;
2620                 }
2621                 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2622                                         recordId, entityIdValue,
2623                                         entityInstanceValue);
2624             }
2625         }
2626     }
2627     if (invalidSensorNumberErrCount != 0)
2628     {
2629         lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2630                    "{ERR_COUNT} times",
2631                    "ERR_COUNT", invalidSensorNumberErrCount);
2632     }
2633 
2634     auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2635         return first.entityInstance <= second.entityInstance;
2636     };
2637 
2638     sort(sensorList.begin(), sensorList.end(), cmpFunc);
2639 
2640     return std::make_tuple(totalInstSensor, sensorList);
2641 }
2642 
2643 std::tuple<bool,    // Reading result
2644            uint7_t, // Temp value
2645            bool>    // Sign bit
readTemp(ipmi::Context::ptr ctx,const std::string & objectPath)2646     readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2647 {
2648     std::string service{};
2649     boost::system::error_code ec =
2650         ipmi::getService(ctx, SensorValue::interface, objectPath, service);
2651     if (ec.value())
2652     {
2653         return std::make_tuple(false, 0, false);
2654     }
2655 
2656     ipmi::PropertyMap properties{};
2657     ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2658                                     SensorValue::interface, properties);
2659     if (ec.value())
2660     {
2661         return std::make_tuple(false, 0, false);
2662     }
2663 
2664     auto scaleIt = properties.find("Scale");
2665     double scaleVal = 0.0;
2666     if (scaleIt != properties.end())
2667     {
2668         scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2669     }
2670 
2671     auto tempValIt = properties.find(SensorValue::property_names::value);
2672     double tempVal = 0.0;
2673     if (tempValIt == properties.end())
2674     {
2675         return std::make_tuple(false, 0, false);
2676     }
2677 
2678     const double maxTemp = 127;
2679     double absTempVal = 0.0;
2680     bool signBit = false;
2681 
2682     tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2683     tempVal = std::pow(10, scaleVal) * tempVal;
2684     absTempVal = std::abs(tempVal);
2685     absTempVal = std::min(absTempVal, maxTemp);
2686     signBit = (tempVal < 0) ? true : false;
2687 
2688     return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2689 }
2690 
2691 ipmi::RspType<uint8_t,              // No of instances for requested id
2692               uint8_t,              // No of record ids in the response
2693               std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2694                                     // IDs
2695               >
getSensorInfo(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2696     getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
2697                   uint8_t entityInstance, uint8_t instanceStart)
2698 {
2699     auto match = ipmi::dcmi::validEntityId.find(entityId);
2700     if (match == ipmi::dcmi::validEntityId.end())
2701     {
2702         lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2703 
2704         return ipmi::responseInvalidFieldRequest();
2705     }
2706 
2707     if (sensorType != ipmi::dcmi::temperatureSensorType)
2708     {
2709         lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2710                    sensorType);
2711 
2712         return ipmi::responseInvalidFieldRequest();
2713     }
2714 
2715     std::vector<uint16_t> sensorRec{};
2716     const auto& [totalSensorInst, sensorList] =
2717         getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2718 
2719     if (sensorList.empty())
2720     {
2721         return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2722     }
2723 
2724     /*
2725      * As DCMI specification, the maximum number of Record Ids of response data
2726      * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2727      * Record Ids of response data is 8. Therefore, not all of sensors are shown
2728      * in response data.
2729      */
2730     uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2731 
2732     for (const auto& sensor : sensorList)
2733     {
2734         sensorRec.emplace_back(sensor.recordId);
2735         if (sensorRec.size() >= numOfRec)
2736         {
2737             break;
2738         }
2739     }
2740 
2741     return ipmi::responseSuccess(
2742         totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
2743 }
2744 
2745 ipmi::RspType<uint8_t,                // No of instances for requested id
2746               uint8_t,                // No of record ids in the response
2747               std::vector<            // Temperature Data
2748                   std::tuple<uint7_t, // Temperature value
2749                              bool,    // Sign bit
2750                              uint8_t  // Entity Instance of sensor
2751                              >>>
getTempReadings(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2752     getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2753                     uint8_t entityId, uint8_t entityInstance,
2754                     uint8_t instanceStart)
2755 {
2756     auto match = ipmi::dcmi::validEntityId.find(entityId);
2757     if (match == ipmi::dcmi::validEntityId.end())
2758     {
2759         lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2760 
2761         return ipmi::responseInvalidFieldRequest();
2762     }
2763 
2764     if (sensorType != ipmi::dcmi::temperatureSensorType)
2765     {
2766         lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2767                    sensorType);
2768 
2769         return ipmi::responseInvalidFieldRequest();
2770     }
2771 
2772     std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2773     const auto& [totalSensorInst, sensorList] =
2774         getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2775 
2776     if (sensorList.empty())
2777     {
2778         return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2779     }
2780 
2781     /*
2782      * As DCMI specification, the maximum number of Record Ids of response data
2783      * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2784      * Record Ids of response data is 8. Therefore, not all of sensors are shown
2785      * in response data.
2786      */
2787     uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2788 
2789     for (const auto& sensor : sensorList)
2790     {
2791         const auto& [readResult, tempVal, signBit] =
2792             readTemp(ctx, sensor.objectPath);
2793 
2794         if (readResult)
2795         {
2796             tempReadingVal.emplace_back(
2797                 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2798 
2799             if (tempReadingVal.size() >= numOfRec)
2800             {
2801                 break;
2802             }
2803         }
2804     }
2805 
2806     return ipmi::responseSuccess(totalSensorInst,
2807                                  static_cast<uint8_t>(tempReadingVal.size()),
2808                                  tempReadingVal);
2809 }
2810 
2811 } // namespace dcmi
2812 
2813 /* end storage commands */
2814 
registerSensorFunctions()2815 void registerSensorFunctions()
2816 {
2817     // <Platform Event>
2818     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2819                           ipmi::sensor_event::cmdPlatformEvent,
2820                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2821 
2822     // <Set Sensor Reading and Event Status>
2823     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2824                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2825                           ipmi::Privilege::Operator, ipmiSetSensorReading);
2826 
2827     // <Get Sensor Reading>
2828     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2829                           ipmi::sensor_event::cmdGetSensorReading,
2830                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2831 
2832     // <Get Sensor Threshold>
2833     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2834                           ipmi::sensor_event::cmdGetSensorThreshold,
2835                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2836 
2837     // <Set Sensor Threshold>
2838     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2839                           ipmi::sensor_event::cmdSetSensorThreshold,
2840                           ipmi::Privilege::Operator,
2841                           ipmiSenSetSensorThresholds);
2842 
2843     // <Get Sensor Event Enable>
2844     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2845                           ipmi::sensor_event::cmdGetSensorEventEnable,
2846                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2847 
2848     // <Get Sensor Event Status>
2849     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2850                           ipmi::sensor_event::cmdGetSensorEventStatus,
2851                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2852 
2853     // register all storage commands for both Sensor and Storage command
2854     // versions
2855 
2856     // <Get SDR Repository Info>
2857     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2858                           ipmi::storage::cmdGetSdrRepositoryInfo,
2859                           ipmi::Privilege::User,
2860                           ipmiStorageGetSDRRepositoryInfo);
2861 
2862     // <Get Device SDR Info>
2863     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2864                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2865                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2866 
2867     // <Get SDR Allocation Info>
2868     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2869                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2870                           ipmi::Privilege::User,
2871                           ipmiStorageGetSDRAllocationInfo);
2872 
2873     // <Reserve SDR Repo>
2874     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2875                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2876                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2877 
2878     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2879                           ipmi::storage::cmdReserveSdrRepository,
2880                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2881 
2882     // <Get Sdr>
2883     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2884                           ipmi::sensor_event::cmdGetDeviceSdr,
2885                           ipmi::Privilege::User, ipmiStorageGetSDR);
2886 
2887     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2888                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2889                           ipmiStorageGetSDR);
2890     // <Get DCMI Sensor Info>
2891     ipmi::registerGroupHandler(
2892         ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2893         ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2894         ipmi::dcmi::getSensorInfo);
2895     // <Get Temperature Readings>
2896     ipmi::registerGroupHandler(
2897         ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2898         ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2899         ipmi::dcmi::getTempReadings);
2900 }
2901 } // namespace ipmi
2902