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 "dbus-sdr/sensorcommands.hpp"
18 
19 #include "dbus-sdr/sdrutils.hpp"
20 #include "dbus-sdr/sensorutils.hpp"
21 #include "dbus-sdr/storagecommands.hpp"
22 
23 #include <algorithm>
24 #include <array>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/container/flat_map.hpp>
27 #include <chrono>
28 #include <cmath>
29 #include <cstring>
30 #include <iostream>
31 #include <ipmid/api.hpp>
32 #include <ipmid/types.hpp>
33 #include <ipmid/utils.hpp>
34 #include <map>
35 #include <memory>
36 #include <optional>
37 #include <phosphor-logging/log.hpp>
38 #include <sdbusplus/bus.hpp>
39 #include <stdexcept>
40 #include <string>
41 #include <utility>
42 #include <variant>
43 
44 namespace ipmi
45 {
46 static constexpr int sensorMapUpdatePeriod = 10;
47 static constexpr int sensorMapSdrUpdatePeriod = 60;
48 
49 constexpr size_t maxSDRTotalSize =
50     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
51 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
52 
53 static uint16_t sdrReservationID;
54 static uint32_t sdrLastAdd = noTimestamp;
55 static uint32_t sdrLastRemove = noTimestamp;
56 static constexpr size_t lastRecordIndex = 0xFFFF;
57 static constexpr int GENERAL_ERROR = -1;
58 
59 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
60 
61 // Specify the comparison required to sort and find char* map objects
62 struct CmpStr
63 {
64     bool operator()(const char* a, const char* b) const
65     {
66         return std::strcmp(a, b) < 0;
67     }
68 };
69 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
70     sensorUnits{{{"temperature", SensorUnits::degreesC},
71                  {"voltage", SensorUnits::volts},
72                  {"current", SensorUnits::amps},
73                  {"fan_tach", SensorUnits::rpm},
74                  {"power", SensorUnits::watts}}};
75 
76 void registerSensorFunctions() __attribute__((constructor));
77 
78 static sdbusplus::bus::match::match sensorAdded(
79     *getSdBus(),
80     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
81     "sensors/'",
82     [](sdbusplus::message::message& m) {
83         getSensorTree().clear();
84         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
85                          std::chrono::system_clock::now().time_since_epoch())
86                          .count();
87     });
88 
89 static sdbusplus::bus::match::match sensorRemoved(
90     *getSdBus(),
91     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
92     "sensors/'",
93     [](sdbusplus::message::message& m) {
94         getSensorTree().clear();
95         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
96                             std::chrono::system_clock::now().time_since_epoch())
97                             .count();
98     });
99 
100 // this keeps track of deassertions for sensor event status command. A
101 // deasertion can only happen if an assertion was seen first.
102 static boost::container::flat_map<
103     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
104     thresholdDeassertMap;
105 
106 static sdbusplus::bus::match::match thresholdChanged(
107     *getSdBus(),
108     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
109     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
110     [](sdbusplus::message::message& m) {
111         boost::container::flat_map<std::string, std::variant<bool, double>>
112             values;
113         m.read(std::string(), values);
114 
115         auto findAssert =
116             std::find_if(values.begin(), values.end(), [](const auto& pair) {
117                 return pair.first.find("Alarm") != std::string::npos;
118             });
119         if (findAssert != values.end())
120         {
121             auto ptr = std::get_if<bool>(&(findAssert->second));
122             if (ptr == nullptr)
123             {
124                 phosphor::logging::log<phosphor::logging::level::ERR>(
125                     "thresholdChanged: Assert non bool");
126                 return;
127             }
128             if (*ptr)
129             {
130                 phosphor::logging::log<phosphor::logging::level::INFO>(
131                     "thresholdChanged: Assert",
132                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
133                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
134             }
135             else
136             {
137                 auto& value =
138                     thresholdDeassertMap[m.get_path()][findAssert->first];
139                 if (value)
140                 {
141                     phosphor::logging::log<phosphor::logging::level::INFO>(
142                         "thresholdChanged: deassert",
143                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
144                     value = *ptr;
145                 }
146             }
147         }
148     });
149 
150 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
151                             double& min)
152 {
153     max = 127;
154     min = -128;
155 
156     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
157     auto critical =
158         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
159     auto warning =
160         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
161 
162     if (sensorObject != sensorMap.end())
163     {
164         auto maxMap = sensorObject->second.find("MaxValue");
165         auto minMap = sensorObject->second.find("MinValue");
166 
167         if (maxMap != sensorObject->second.end())
168         {
169             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
170         }
171         if (minMap != sensorObject->second.end())
172         {
173             min = std::visit(VariantToDoubleVisitor(), minMap->second);
174         }
175     }
176     if (critical != sensorMap.end())
177     {
178         auto lower = critical->second.find("CriticalLow");
179         auto upper = critical->second.find("CriticalHigh");
180         if (lower != critical->second.end())
181         {
182             double value = std::visit(VariantToDoubleVisitor(), lower->second);
183             min = std::min(value, min);
184         }
185         if (upper != critical->second.end())
186         {
187             double value = std::visit(VariantToDoubleVisitor(), upper->second);
188             max = std::max(value, max);
189         }
190     }
191     if (warning != sensorMap.end())
192     {
193 
194         auto lower = warning->second.find("WarningLow");
195         auto upper = warning->second.find("WarningHigh");
196         if (lower != warning->second.end())
197         {
198             double value = std::visit(VariantToDoubleVisitor(), lower->second);
199             min = std::min(value, min);
200         }
201         if (upper != warning->second.end())
202         {
203             double value = std::visit(VariantToDoubleVisitor(), upper->second);
204             max = std::max(value, max);
205         }
206     }
207 }
208 
209 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
210                          std::string sensorPath, DbusInterfaceMap& sensorMap,
211                          int updatePeriod = sensorMapUpdatePeriod)
212 {
213     static boost::container::flat_map<
214         std::string, std::chrono::time_point<std::chrono::steady_clock>>
215         updateTimeMap;
216 
217     auto updateFind = updateTimeMap.find(sensorConnection);
218     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
219     if (updateFind != updateTimeMap.end())
220     {
221         lastUpdate = updateFind->second;
222     }
223 
224     auto now = std::chrono::steady_clock::now();
225 
226     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
227             .count() > updatePeriod)
228     {
229         ObjectValueTree managedObjects;
230         boost::system::error_code ec = getManagedObjects(
231             ctx, sensorConnection.c_str(), "/", managedObjects);
232         if (ec)
233         {
234             phosphor::logging::log<phosphor::logging::level::ERR>(
235                 "GetMangagedObjects for getSensorMap failed",
236                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
237 
238             return false;
239         }
240 
241         SensorCache[sensorConnection] = managedObjects;
242         // Update time after finish building the map which allow the
243         // data to be cached for updatePeriod plus the build time.
244         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
245     }
246     auto connection = SensorCache.find(sensorConnection);
247     if (connection == SensorCache.end())
248     {
249         return false;
250     }
251     auto path = connection->second.find(sensorPath);
252     if (path == connection->second.end())
253     {
254         return false;
255     }
256     sensorMap = path->second;
257 
258     return true;
259 }
260 
261 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
262                                      uint8_t sensorType, uint8_t sensorNum,
263                                      uint8_t eventType, uint8_t eventData1,
264                                      std::optional<uint8_t> eventData2,
265                                      std::optional<uint8_t> eventData3)
266 {
267     return ipmi::responseSuccess();
268 }
269 
270 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
271                                      uint8_t sensorNumber, uint8_t operation,
272                                      uint8_t reading, uint15_t assertOffset,
273                                      bool resvd1, uint15_t deassertOffset,
274                                      bool resvd2, uint8_t eventData1,
275                                      uint8_t eventData2, uint8_t eventData3)
276 {
277     std::string connection;
278     std::string path;
279     ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path);
280     if (status)
281     {
282         return ipmi::response(status);
283     }
284 
285     DbusInterfaceMap sensorMap;
286     if (!getSensorMap(ctx, connection, path, sensorMap))
287     {
288         return ipmi::responseResponseError();
289     }
290     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
291 
292     if (sensorObject == sensorMap.end() ||
293         sensorObject->second.find("Value") == sensorObject->second.end())
294     {
295         return ipmi::responseResponseError();
296     }
297 
298     double max = 0;
299     double min = 0;
300     getSensorMaxMin(sensorMap, max, min);
301 
302     int16_t mValue = 0;
303     int16_t bValue = 0;
304     int8_t rExp = 0;
305     int8_t bExp = 0;
306     bool bSigned = false;
307 
308     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
309     {
310         return ipmi::responseResponseError();
311     }
312 
313     double value = bSigned ? ((int8_t)reading) : reading;
314 
315     value *= ((double)mValue);
316     value += ((double)bValue) * std::pow(10.0, bExp);
317     value *= std::pow(10.0, rExp);
318 
319     if constexpr (debug)
320     {
321         phosphor::logging::log<phosphor::logging::level::INFO>(
322             "IPMI SET_SENSOR",
323             phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
324             phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
325             phosphor::logging::entry("VALUE=%f", value));
326     }
327 
328     try
329     {
330         setDbusProperty(ctx, connection, path,
331                         "xyz.openbmc_project.Sensor.Value", "Value",
332                         ipmi::Value(value));
333     }
334     // setDbusProperty intended to resolve dbus exception/rc within the
335     // function but failed to achieve that. Catch SdBusError in the ipmi
336     // callback functions for now (e.g. ipmiSetSensorReading).
337     catch (const sdbusplus::exception::SdBusError& e)
338     {
339         using namespace phosphor::logging;
340         log<level::ERR>(
341             "Failed to set property", entry("PROPERTY=%s", "Value"),
342             entry("PATH=%s", path.c_str()),
343             entry("INTERFACE=%s", "xyz.openbmc_project.Sensor.Value"),
344             entry("WHAT=%s", e.what()));
345         return ipmi::responseResponseError();
346     }
347     return ipmi::responseSuccess();
348 }
349 
350 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
351     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
352 {
353     std::string connection;
354     std::string path;
355 
356     auto status = getSensorConnection(ctx, sensnum, connection, path);
357     if (status)
358     {
359         return ipmi::response(status);
360     }
361 
362     DbusInterfaceMap sensorMap;
363     if (!getSensorMap(ctx, connection, path, sensorMap))
364     {
365         return ipmi::responseResponseError();
366     }
367     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
368 
369     if (sensorObject == sensorMap.end() ||
370         sensorObject->second.find("Value") == sensorObject->second.end())
371     {
372         return ipmi::responseResponseError();
373     }
374     auto& valueVariant = sensorObject->second["Value"];
375     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
376 
377     double max = 0;
378     double min = 0;
379     getSensorMaxMin(sensorMap, max, min);
380 
381     int16_t mValue = 0;
382     int16_t bValue = 0;
383     int8_t rExp = 0;
384     int8_t bExp = 0;
385     bool bSigned = false;
386 
387     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
388     {
389         return ipmi::responseResponseError();
390     }
391 
392     uint8_t value =
393         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
394     uint8_t operation =
395         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
396     operation |=
397         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
398     bool notReading = std::isnan(reading);
399 
400     if (!notReading)
401     {
402         auto availableObject =
403             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
404         if (availableObject != sensorMap.end())
405         {
406             auto findAvailable = availableObject->second.find("Available");
407             if (findAvailable != availableObject->second.end())
408             {
409                 bool* available = std::get_if<bool>(&(findAvailable->second));
410                 if (available && !(*available))
411                 {
412                     notReading = true;
413                 }
414             }
415         }
416     }
417 
418     if (notReading)
419     {
420         operation |= static_cast<uint8_t>(
421             IPMISensorReadingByte2::readingStateUnavailable);
422     }
423 
424     if constexpr (details::enableInstrumentation)
425     {
426         int byteValue;
427         if (bSigned)
428         {
429             byteValue = static_cast<int>(static_cast<int8_t>(value));
430         }
431         else
432         {
433             byteValue = static_cast<int>(static_cast<uint8_t>(value));
434         }
435 
436         // Keep stats on the reading just obtained, even if it is "NaN"
437         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
438         {
439             // This is the first reading, show the coefficients
440             double step = (max - min) / 255.0;
441             std::cerr << "IPMI sensor "
442                       << details::sdrStatsTable.getName(sensnum)
443                       << ": Range min=" << min << " max=" << max
444                       << ", step=" << step
445                       << ", Coefficients mValue=" << static_cast<int>(mValue)
446                       << " rExp=" << static_cast<int>(rExp)
447                       << " bValue=" << static_cast<int>(bValue)
448                       << " bExp=" << static_cast<int>(bExp)
449                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
450         }
451     }
452 
453     uint8_t thresholds = 0;
454 
455     auto warningObject =
456         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
457     if (warningObject != sensorMap.end())
458     {
459         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
460         auto alarmLow = warningObject->second.find("WarningAlarmLow");
461         if (alarmHigh != warningObject->second.end())
462         {
463             if (std::get<bool>(alarmHigh->second))
464             {
465                 thresholds |= static_cast<uint8_t>(
466                     IPMISensorReadingByte3::upperNonCritical);
467             }
468         }
469         if (alarmLow != warningObject->second.end())
470         {
471             if (std::get<bool>(alarmLow->second))
472             {
473                 thresholds |= static_cast<uint8_t>(
474                     IPMISensorReadingByte3::lowerNonCritical);
475             }
476         }
477     }
478 
479     auto criticalObject =
480         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
481     if (criticalObject != sensorMap.end())
482     {
483         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
484         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
485         if (alarmHigh != criticalObject->second.end())
486         {
487             if (std::get<bool>(alarmHigh->second))
488             {
489                 thresholds |=
490                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
491             }
492         }
493         if (alarmLow != criticalObject->second.end())
494         {
495             if (std::get<bool>(alarmLow->second))
496             {
497                 thresholds |=
498                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
499             }
500         }
501     }
502 
503     // no discrete as of today so optional byte is never returned
504     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
505 }
506 
507 /** @brief implements the Set Sensor threshold command
508  *  @param sensorNumber        - sensor number
509  *  @param lowerNonCriticalThreshMask
510  *  @param lowerCriticalThreshMask
511  *  @param lowerNonRecovThreshMask
512  *  @param upperNonCriticalThreshMask
513  *  @param upperCriticalThreshMask
514  *  @param upperNonRecovThreshMask
515  *  @param reserved
516  *  @param lowerNonCritical    - lower non-critical threshold
517  *  @param lowerCritical       - Lower critical threshold
518  *  @param lowerNonRecoverable - Lower non recovarable threshold
519  *  @param upperNonCritical    - Upper non-critical threshold
520  *  @param upperCritical       - Upper critical
521  *  @param upperNonRecoverable - Upper Non-recoverable
522  *
523  *  @returns IPMI completion code
524  */
525 ipmi::RspType<> ipmiSenSetSensorThresholds(
526     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
527     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
528     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
529     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
530     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
531     uint8_t upperNonCritical, uint8_t upperCritical,
532     uint8_t upperNonRecoverable)
533 {
534     if (reserved)
535     {
536         return ipmi::responseInvalidFieldRequest();
537     }
538 
539     // lower nc and upper nc not suppported on any sensor
540     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
541     {
542         return ipmi::responseInvalidFieldRequest();
543     }
544 
545     // if none of the threshold mask are set, nothing to do
546     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
547           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
548           upperCriticalThreshMask | upperNonRecovThreshMask))
549     {
550         return ipmi::responseSuccess();
551     }
552 
553     std::string connection;
554     std::string path;
555 
556     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
557     if (status)
558     {
559         return ipmi::response(status);
560     }
561     DbusInterfaceMap sensorMap;
562     if (!getSensorMap(ctx, connection, path, sensorMap))
563     {
564         return ipmi::responseResponseError();
565     }
566 
567     double max = 0;
568     double min = 0;
569     getSensorMaxMin(sensorMap, max, min);
570 
571     int16_t mValue = 0;
572     int16_t bValue = 0;
573     int8_t rExp = 0;
574     int8_t bExp = 0;
575     bool bSigned = false;
576 
577     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
578     {
579         return ipmi::responseResponseError();
580     }
581 
582     // store a vector of property name, value to set, and interface
583     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
584 
585     // define the indexes of the tuple
586     constexpr uint8_t propertyName = 0;
587     constexpr uint8_t thresholdValue = 1;
588     constexpr uint8_t interface = 2;
589     // verifiy all needed fields are present
590     if (lowerCriticalThreshMask || upperCriticalThreshMask)
591     {
592         auto findThreshold =
593             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
594         if (findThreshold == sensorMap.end())
595         {
596             return ipmi::responseInvalidFieldRequest();
597         }
598         if (lowerCriticalThreshMask)
599         {
600             auto findLower = findThreshold->second.find("CriticalLow");
601             if (findLower == findThreshold->second.end())
602             {
603                 return ipmi::responseInvalidFieldRequest();
604             }
605             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
606                                          findThreshold->first);
607         }
608         if (upperCriticalThreshMask)
609         {
610             auto findUpper = findThreshold->second.find("CriticalHigh");
611             if (findUpper == findThreshold->second.end())
612             {
613                 return ipmi::responseInvalidFieldRequest();
614             }
615             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
616                                          findThreshold->first);
617         }
618     }
619     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
620     {
621         auto findThreshold =
622             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
623         if (findThreshold == sensorMap.end())
624         {
625             return ipmi::responseInvalidFieldRequest();
626         }
627         if (lowerNonCriticalThreshMask)
628         {
629             auto findLower = findThreshold->second.find("WarningLow");
630             if (findLower == findThreshold->second.end())
631             {
632                 return ipmi::responseInvalidFieldRequest();
633             }
634             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
635                                          findThreshold->first);
636         }
637         if (upperNonCriticalThreshMask)
638         {
639             auto findUpper = findThreshold->second.find("WarningHigh");
640             if (findUpper == findThreshold->second.end())
641             {
642                 return ipmi::responseInvalidFieldRequest();
643             }
644             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
645                                          findThreshold->first);
646         }
647     }
648     for (const auto& property : thresholdsToSet)
649     {
650         // from section 36.3 in the IPMI Spec, assume all linear
651         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
652                              (bValue * std::pow(10.0, bExp))) *
653                             std::pow(10.0, rExp);
654         setDbusProperty(
655             *getSdBus(), connection, path, std::get<interface>(property),
656             std::get<propertyName>(property), ipmi::Value(valueToSet));
657     }
658     return ipmi::responseSuccess();
659 }
660 
661 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
662 {
663     IPMIThresholds resp;
664     auto warningInterface =
665         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
666     auto criticalInterface =
667         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
668 
669     if ((warningInterface != sensorMap.end()) ||
670         (criticalInterface != sensorMap.end()))
671     {
672         auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
673 
674         if (sensorPair == sensorMap.end())
675         {
676             // should not have been able to find a sensor not implementing
677             // the sensor object
678             throw std::runtime_error("Invalid sensor map");
679         }
680 
681         double max = 0;
682         double min = 0;
683         getSensorMaxMin(sensorMap, max, min);
684 
685         int16_t mValue = 0;
686         int16_t bValue = 0;
687         int8_t rExp = 0;
688         int8_t bExp = 0;
689         bool bSigned = false;
690 
691         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
692         {
693             throw std::runtime_error("Invalid sensor atrributes");
694         }
695         if (warningInterface != sensorMap.end())
696         {
697             auto& warningMap = warningInterface->second;
698 
699             auto warningHigh = warningMap.find("WarningHigh");
700             auto warningLow = warningMap.find("WarningLow");
701 
702             if (warningHigh != warningMap.end())
703             {
704 
705                 double value =
706                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
707                 resp.warningHigh = scaleIPMIValueFromDouble(
708                     value, mValue, rExp, bValue, bExp, bSigned);
709             }
710             if (warningLow != warningMap.end())
711             {
712                 double value =
713                     std::visit(VariantToDoubleVisitor(), warningLow->second);
714                 resp.warningLow = scaleIPMIValueFromDouble(
715                     value, mValue, rExp, bValue, bExp, bSigned);
716             }
717         }
718         if (criticalInterface != sensorMap.end())
719         {
720             auto& criticalMap = criticalInterface->second;
721 
722             auto criticalHigh = criticalMap.find("CriticalHigh");
723             auto criticalLow = criticalMap.find("CriticalLow");
724 
725             if (criticalHigh != criticalMap.end())
726             {
727                 double value =
728                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
729                 resp.criticalHigh = scaleIPMIValueFromDouble(
730                     value, mValue, rExp, bValue, bExp, bSigned);
731             }
732             if (criticalLow != criticalMap.end())
733             {
734                 double value =
735                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
736                 resp.criticalLow = scaleIPMIValueFromDouble(
737                     value, mValue, rExp, bValue, bExp, bSigned);
738             }
739         }
740     }
741     return resp;
742 }
743 
744 ipmi::RspType<uint8_t, // readable
745               uint8_t, // lowerNCrit
746               uint8_t, // lowerCrit
747               uint8_t, // lowerNrecoverable
748               uint8_t, // upperNC
749               uint8_t, // upperCrit
750               uint8_t> // upperNRecoverable
751     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
752 {
753     std::string connection;
754     std::string path;
755 
756     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
757     if (status)
758     {
759         return ipmi::response(status);
760     }
761 
762     DbusInterfaceMap sensorMap;
763     if (!getSensorMap(ctx, connection, path, sensorMap))
764     {
765         return ipmi::responseResponseError();
766     }
767 
768     IPMIThresholds thresholdData;
769     try
770     {
771         thresholdData = getIPMIThresholds(sensorMap);
772     }
773     catch (std::exception&)
774     {
775         return ipmi::responseResponseError();
776     }
777 
778     uint8_t readable = 0;
779     uint8_t lowerNC = 0;
780     uint8_t lowerCritical = 0;
781     uint8_t lowerNonRecoverable = 0;
782     uint8_t upperNC = 0;
783     uint8_t upperCritical = 0;
784     uint8_t upperNonRecoverable = 0;
785 
786     if (thresholdData.warningHigh)
787     {
788         readable |=
789             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
790         upperNC = *thresholdData.warningHigh;
791     }
792     if (thresholdData.warningLow)
793     {
794         readable |=
795             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
796         lowerNC = *thresholdData.warningLow;
797     }
798 
799     if (thresholdData.criticalHigh)
800     {
801         readable |=
802             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
803         upperCritical = *thresholdData.criticalHigh;
804     }
805     if (thresholdData.criticalLow)
806     {
807         readable |=
808             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
809         lowerCritical = *thresholdData.criticalLow;
810     }
811 
812     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
813                                  lowerNonRecoverable, upperNC, upperCritical,
814                                  upperNonRecoverable);
815 }
816 
817 /** @brief implements the get Sensor event enable command
818  *  @param sensorNumber - sensor number
819  *
820  *  @returns IPMI completion code plus response data
821  *   - enabled               - Sensor Event messages
822  *   - assertionEnabledLsb   - Assertion event messages
823  *   - assertionEnabledMsb   - Assertion event messages
824  *   - deassertionEnabledLsb - Deassertion event messages
825  *   - deassertionEnabledMsb - Deassertion event messages
826  */
827 
828 ipmi::RspType<uint8_t, // enabled
829               uint8_t, // assertionEnabledLsb
830               uint8_t, // assertionEnabledMsb
831               uint8_t, // deassertionEnabledLsb
832               uint8_t> // deassertionEnabledMsb
833     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
834 {
835     std::string connection;
836     std::string path;
837 
838     uint8_t enabled = 0;
839     uint8_t assertionEnabledLsb = 0;
840     uint8_t assertionEnabledMsb = 0;
841     uint8_t deassertionEnabledLsb = 0;
842     uint8_t deassertionEnabledMsb = 0;
843 
844     auto status = getSensorConnection(ctx, sensorNum, connection, path);
845     if (status)
846     {
847         return ipmi::response(status);
848     }
849 
850     DbusInterfaceMap sensorMap;
851     if (!getSensorMap(ctx, connection, path, sensorMap))
852     {
853         return ipmi::responseResponseError();
854     }
855 
856     auto warningInterface =
857         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
858     auto criticalInterface =
859         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
860     if ((warningInterface != sensorMap.end()) ||
861         (criticalInterface != sensorMap.end()))
862     {
863         enabled = static_cast<uint8_t>(
864             IPMISensorEventEnableByte2::sensorScanningEnable);
865         if (warningInterface != sensorMap.end())
866         {
867             auto& warningMap = warningInterface->second;
868 
869             auto warningHigh = warningMap.find("WarningHigh");
870             auto warningLow = warningMap.find("WarningLow");
871             if (warningHigh != warningMap.end())
872             {
873                 assertionEnabledLsb |= static_cast<uint8_t>(
874                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
875                 deassertionEnabledLsb |= static_cast<uint8_t>(
876                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
877             }
878             if (warningLow != warningMap.end())
879             {
880                 assertionEnabledLsb |= static_cast<uint8_t>(
881                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
882                 deassertionEnabledLsb |= static_cast<uint8_t>(
883                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
884             }
885         }
886         if (criticalInterface != sensorMap.end())
887         {
888             auto& criticalMap = criticalInterface->second;
889 
890             auto criticalHigh = criticalMap.find("CriticalHigh");
891             auto criticalLow = criticalMap.find("CriticalLow");
892 
893             if (criticalHigh != criticalMap.end())
894             {
895                 assertionEnabledMsb |= static_cast<uint8_t>(
896                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
897                 deassertionEnabledMsb |= static_cast<uint8_t>(
898                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
899             }
900             if (criticalLow != criticalMap.end())
901             {
902                 assertionEnabledLsb |= static_cast<uint8_t>(
903                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
904                 deassertionEnabledLsb |= static_cast<uint8_t>(
905                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
906             }
907         }
908     }
909 
910     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
911                                  assertionEnabledMsb, deassertionEnabledLsb,
912                                  deassertionEnabledMsb);
913 }
914 
915 /** @brief implements the get Sensor event status command
916  *  @param sensorNumber - sensor number, FFh = reserved
917  *
918  *  @returns IPMI completion code plus response data
919  *   - sensorEventStatus - Sensor Event messages state
920  *   - assertions        - Assertion event messages
921  *   - deassertions      - Deassertion event messages
922  */
923 ipmi::RspType<uint8_t,         // sensorEventStatus
924               std::bitset<16>, // assertions
925               std::bitset<16>  // deassertion
926               >
927     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
928 {
929     if (sensorNum == reservedSensorNumber)
930     {
931         return ipmi::responseInvalidFieldRequest();
932     }
933 
934     std::string connection;
935     std::string path;
936     auto status = getSensorConnection(ctx, sensorNum, connection, path);
937     if (status)
938     {
939         phosphor::logging::log<phosphor::logging::level::ERR>(
940             "ipmiSenGetSensorEventStatus: Sensor connection Error",
941             phosphor::logging::entry("SENSOR=%d", sensorNum));
942         return ipmi::response(status);
943     }
944 
945     DbusInterfaceMap sensorMap;
946     if (!getSensorMap(ctx, connection, path, sensorMap))
947     {
948         phosphor::logging::log<phosphor::logging::level::ERR>(
949             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
950             phosphor::logging::entry("SENSOR=%s", path.c_str()));
951         return ipmi::responseResponseError();
952     }
953     auto warningInterface =
954         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
955     auto criticalInterface =
956         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
957 
958     uint8_t sensorEventStatus =
959         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
960 
961     std::optional<bool> criticalDeassertHigh =
962         thresholdDeassertMap[path]["CriticalAlarmHigh"];
963     std::optional<bool> criticalDeassertLow =
964         thresholdDeassertMap[path]["CriticalAlarmLow"];
965     std::optional<bool> warningDeassertHigh =
966         thresholdDeassertMap[path]["WarningAlarmHigh"];
967     std::optional<bool> warningDeassertLow =
968         thresholdDeassertMap[path]["WarningAlarmLow"];
969 
970     std::bitset<16> assertions = 0;
971     std::bitset<16> deassertions = 0;
972 
973     if (criticalDeassertHigh && !*criticalDeassertHigh)
974     {
975         deassertions.set(static_cast<size_t>(
976             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
977     }
978     if (criticalDeassertLow && !*criticalDeassertLow)
979     {
980         deassertions.set(static_cast<size_t>(
981             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
982     }
983     if (warningDeassertHigh && !*warningDeassertHigh)
984     {
985         deassertions.set(static_cast<size_t>(
986             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
987     }
988     if (warningDeassertLow && !*warningDeassertLow)
989     {
990         deassertions.set(static_cast<size_t>(
991             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
992     }
993     if ((warningInterface != sensorMap.end()) ||
994         (criticalInterface != sensorMap.end()))
995     {
996         sensorEventStatus = static_cast<size_t>(
997             IPMISensorEventEnableByte2::eventMessagesEnable);
998         if (warningInterface != sensorMap.end())
999         {
1000             auto& warningMap = warningInterface->second;
1001 
1002             auto warningHigh = warningMap.find("WarningAlarmHigh");
1003             auto warningLow = warningMap.find("WarningAlarmLow");
1004             auto warningHighAlarm = false;
1005             auto warningLowAlarm = false;
1006 
1007             if (warningHigh != warningMap.end())
1008             {
1009                 warningHighAlarm = std::get<bool>(warningHigh->second);
1010             }
1011             if (warningLow != warningMap.end())
1012             {
1013                 warningLowAlarm = std::get<bool>(warningLow->second);
1014             }
1015             if (warningHighAlarm)
1016             {
1017                 assertions.set(
1018                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1019                                             upperNonCriticalGoingHigh));
1020             }
1021             if (warningLowAlarm)
1022             {
1023                 assertions.set(
1024                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1025                                             lowerNonCriticalGoingLow));
1026             }
1027         }
1028         if (criticalInterface != sensorMap.end())
1029         {
1030             auto& criticalMap = criticalInterface->second;
1031 
1032             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1033             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1034             auto criticalHighAlarm = false;
1035             auto criticalLowAlarm = false;
1036 
1037             if (criticalHigh != criticalMap.end())
1038             {
1039                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1040             }
1041             if (criticalLow != criticalMap.end())
1042             {
1043                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1044             }
1045             if (criticalHighAlarm)
1046             {
1047                 assertions.set(
1048                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1049                                             upperCriticalGoingHigh));
1050             }
1051             if (criticalLowAlarm)
1052             {
1053                 assertions.set(static_cast<size_t>(
1054                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1055             }
1056         }
1057     }
1058 
1059     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1060 }
1061 
1062 static int getSensorDataRecord(ipmi::Context::ptr ctx,
1063                                std::vector<uint8_t>& recordData,
1064                                uint16_t recordID)
1065 {
1066     auto& sensorTree = getSensorTree();
1067     size_t fruCount = 0;
1068     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1069     if (ret != ipmi::ccSuccess)
1070     {
1071         phosphor::logging::log<phosphor::logging::level::ERR>(
1072             "getSensorDataRecord: getFruSdrCount error");
1073         return GENERAL_ERROR;
1074     }
1075 
1076     size_t lastRecord =
1077         sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
1078     if (recordID == lastRecordIndex)
1079     {
1080         recordID = lastRecord;
1081     }
1082     if (recordID > lastRecord)
1083     {
1084         phosphor::logging::log<phosphor::logging::level::ERR>(
1085             "getSensorDataRecord: recordID > lastRecord error");
1086         return GENERAL_ERROR;
1087     }
1088 
1089     if (recordID >= sensorTree.size())
1090     {
1091         size_t fruIndex = recordID - sensorTree.size();
1092         if (fruIndex >= fruCount)
1093         {
1094             // handle type 12 hardcoded records
1095             size_t type12Index = fruIndex - fruCount;
1096             if (type12Index >= ipmi::storage::type12Count)
1097             {
1098                 phosphor::logging::log<phosphor::logging::level::ERR>(
1099                     "getSensorDataRecord: type12Index error");
1100                 return GENERAL_ERROR;
1101             }
1102             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1103         }
1104         else
1105         {
1106             // handle fru records
1107             get_sdr::SensorDataFruRecord data;
1108             ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1109             if (ret != IPMI_CC_OK)
1110             {
1111                 return GENERAL_ERROR;
1112             }
1113             data.header.record_id_msb = recordID >> 8;
1114             data.header.record_id_lsb = recordID & 0xFF;
1115             recordData.insert(recordData.end(),
1116                               reinterpret_cast<uint8_t*>(&data),
1117                               reinterpret_cast<uint8_t*>(&data) + sizeof(data));
1118         }
1119 
1120         return 0;
1121     }
1122 
1123     std::string connection;
1124     std::string path;
1125     auto status = getSensorConnection(ctx, recordID, connection, path);
1126     if (status)
1127     {
1128         phosphor::logging::log<phosphor::logging::level::ERR>(
1129             "getSensorDataRecord: getSensorConnection error");
1130         return GENERAL_ERROR;
1131     }
1132     DbusInterfaceMap sensorMap;
1133     if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod))
1134     {
1135         phosphor::logging::log<phosphor::logging::level::ERR>(
1136             "getSensorDataRecord: getSensorMap error");
1137         return GENERAL_ERROR;
1138     }
1139     uint16_t sensorNum = getSensorNumberFromPath(path);
1140     if (sensorNum == invalidSensorNumber)
1141     {
1142         phosphor::logging::log<phosphor::logging::level::ERR>(
1143             "getSensorDataRecord: invalidSensorNumber");
1144         return GENERAL_ERROR;
1145     }
1146     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1147     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1148 
1149     get_sdr::SensorDataFullRecord record = {0};
1150 
1151     get_sdr::header::set_record_id(
1152         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1153 
1154     record.header.sdr_version = ipmiSdrVersion;
1155     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1156     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1157                                   sizeof(get_sdr::SensorDataRecordHeader);
1158     record.key.owner_id = 0x20;
1159     record.key.owner_lun = lun;
1160     record.key.sensor_number = sensornumber;
1161 
1162     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1163     record.body.sensor_type = getSensorTypeFromPath(path);
1164     std::string type = getSensorTypeStringFromPath(path);
1165     auto typeCstr = type.c_str();
1166     auto findUnits = sensorUnits.find(typeCstr);
1167     if (findUnits != sensorUnits.end())
1168     {
1169         record.body.sensor_units_2_base =
1170             static_cast<uint8_t>(findUnits->second);
1171     } // else default 0x0 unspecified
1172 
1173     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1174 
1175     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1176     if (sensorObject == sensorMap.end())
1177     {
1178         phosphor::logging::log<phosphor::logging::level::ERR>(
1179             "getSensorDataRecord: sensorObject error");
1180         return GENERAL_ERROR;
1181     }
1182 
1183     uint8_t entityId = 0;
1184     uint8_t entityInstance = 0x01;
1185 
1186     // follow the association chain to get the parent board's entityid and
1187     // entityInstance
1188     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1189 
1190     record.body.entity_id = entityId;
1191     record.body.entity_instance = entityInstance;
1192 
1193     auto maxObject = sensorObject->second.find("MaxValue");
1194     auto minObject = sensorObject->second.find("MinValue");
1195 
1196     // If min and/or max are left unpopulated,
1197     // then default to what a signed byte would be, namely (-128,127) range.
1198     auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1199     auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1200     if (maxObject != sensorObject->second.end())
1201     {
1202         max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1203     }
1204 
1205     if (minObject != sensorObject->second.end())
1206     {
1207         min = std::visit(VariantToDoubleVisitor(), minObject->second);
1208     }
1209 
1210     int16_t mValue = 0;
1211     int8_t rExp = 0;
1212     int16_t bValue = 0;
1213     int8_t bExp = 0;
1214     bool bSigned = false;
1215 
1216     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1217     {
1218         phosphor::logging::log<phosphor::logging::level::ERR>(
1219             "getSensorDataRecord: getSensorAttributes error");
1220         return GENERAL_ERROR;
1221     }
1222 
1223     // The record.body is a struct SensorDataFullRecordBody
1224     // from sensorhandler.hpp in phosphor-ipmi-host.
1225     // The meaning of these bits appears to come from
1226     // table 43.1 of the IPMI spec.
1227     // The above 5 sensor attributes are stuffed in as follows:
1228     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1229     // Byte 22-24 are for other purposes
1230     // Byte 25 = MMMMMMMM = LSB of M
1231     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1232     // Byte 27 = BBBBBBBB = LSB of B
1233     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1234     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1235     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1236 
1237     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1238     record.body.m_lsb = mValue & 0xFF;
1239 
1240     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1241     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1242 
1243     // move the smallest bit of the MSB into place (bit 9)
1244     // the MSbs are bits 7:8 in m_msb_and_tolerance
1245     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1246 
1247     record.body.b_lsb = bValue & 0xFF;
1248 
1249     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1250     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1251 
1252     // move the smallest bit of the MSB into place (bit 9)
1253     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1254     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1255 
1256     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1257     uint8_t rExpBits = rExp & 0x07;
1258 
1259     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1260     uint8_t bExpBits = bExp & 0x07;
1261 
1262     // move rExp and bExp into place
1263     record.body.r_b_exponents =
1264         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1265 
1266     // Set the analog reading byte interpretation accordingly
1267     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1268 
1269     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1270     // These seem redundant, but derivable from the above 5 attributes
1271     // Original comment said "todo fill out rest of units"
1272 
1273     // populate sensor name from path
1274     std::string name;
1275     size_t nameStart = path.rfind("/");
1276     if (nameStart != std::string::npos)
1277     {
1278         name = path.substr(nameStart + 1, std::string::npos - nameStart);
1279     }
1280 
1281     std::replace(name.begin(), name.end(), '_', ' ');
1282     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1283     {
1284         // try to not truncate by replacing common words
1285         constexpr std::array<std::pair<const char*, const char*>, 2>
1286             replaceWords = {std::make_pair("Output", "Out"),
1287                             std::make_pair("Input", "In")};
1288         for (const auto& [find, replace] : replaceWords)
1289         {
1290             boost::replace_all(name, find, replace);
1291         }
1292 
1293         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1294     }
1295     record.body.id_string_info = name.size();
1296     std::strncpy(record.body.id_string, name.c_str(),
1297                  sizeof(record.body.id_string));
1298 
1299     // Remember the sensor name, as determined for this sensor number
1300     details::sdrStatsTable.updateName(sensornumber, name);
1301 
1302     IPMIThresholds thresholdData;
1303     try
1304     {
1305         thresholdData = getIPMIThresholds(sensorMap);
1306     }
1307     catch (std::exception&)
1308     {
1309         phosphor::logging::log<phosphor::logging::level::ERR>(
1310             "getSensorDataRecord: getIPMIThresholds error");
1311         return GENERAL_ERROR;
1312     }
1313 
1314     if (thresholdData.criticalHigh)
1315     {
1316         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1317         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1318             IPMISensorEventEnableThresholds::criticalThreshold);
1319         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1320             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1321         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1322             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1323         record.body.discrete_reading_setting_mask[0] |=
1324             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1325     }
1326     if (thresholdData.warningHigh)
1327     {
1328         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1329         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1330             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1331         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1332             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1333         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1334             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1335         record.body.discrete_reading_setting_mask[0] |=
1336             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1337     }
1338     if (thresholdData.criticalLow)
1339     {
1340         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1341         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1342             IPMISensorEventEnableThresholds::criticalThreshold);
1343         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1344             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1345         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1346             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1347         record.body.discrete_reading_setting_mask[0] |=
1348             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1349     }
1350     if (thresholdData.warningLow)
1351     {
1352         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1353         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1354             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1355         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1356             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1357         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1358             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1359         record.body.discrete_reading_setting_mask[0] |=
1360             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1361     }
1362 
1363     // everything that is readable is setable
1364     record.body.discrete_reading_setting_mask[1] =
1365         record.body.discrete_reading_setting_mask[0];
1366     recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
1367                       reinterpret_cast<uint8_t*>(&record) + sizeof(record));
1368     return 0;
1369 }
1370 
1371 /** @brief implements the get SDR Info command
1372  *  @param count - Operation
1373  *
1374  *  @returns IPMI completion code plus response data
1375  *   - sdrCount - sensor/SDR count
1376  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1377  */
1378 static ipmi::RspType<uint8_t, // respcount
1379                      uint8_t, // dynamic population flags
1380                      uint32_t // last time a sensor was added
1381                      >
1382     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1383                                std::optional<uint8_t> count)
1384 {
1385     auto& sensorTree = getSensorTree();
1386     uint8_t sdrCount = 0;
1387     uint16_t recordID = 0;
1388     std::vector<uint8_t> record;
1389     // Sensors are dynamically allocated, and there is at least one LUN
1390     uint8_t lunsAndDynamicPopulation = 0x80;
1391     constexpr uint8_t getSdrCount = 0x01;
1392     constexpr uint8_t getSensorCount = 0x00;
1393 
1394     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1395     {
1396         return ipmi::responseResponseError();
1397     }
1398     uint16_t numSensors = sensorTree.size();
1399     if (count.value_or(0) == getSdrCount)
1400     {
1401         // Count the number of Type 1 SDR entries assigned to the LUN
1402         while (!getSensorDataRecord(ctx, record, recordID++))
1403         {
1404             get_sdr::SensorDataRecordHeader* hdr =
1405                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1406                     record.data());
1407             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1408             {
1409                 get_sdr::SensorDataFullRecord* recordData =
1410                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
1411                         record.data());
1412                 if (ctx->lun == recordData->key.owner_lun)
1413                 {
1414                     sdrCount++;
1415                 }
1416             }
1417         }
1418     }
1419     else if (count.value_or(0) == getSensorCount)
1420     {
1421         // Return the number of sensors attached to the LUN
1422         if ((ctx->lun == 0) && (numSensors > 0))
1423         {
1424             sdrCount =
1425                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1426         }
1427         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1428         {
1429             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1430                            ? maxSensorsPerLUN
1431                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1432         }
1433         else if (ctx->lun == 3)
1434         {
1435             if (numSensors <= maxIPMISensors)
1436             {
1437                 sdrCount =
1438                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1439             }
1440             else
1441             {
1442                 // error
1443                 throw std::out_of_range(
1444                     "Maximum number of IPMI sensors exceeded.");
1445             }
1446         }
1447     }
1448     else
1449     {
1450         return ipmi::responseInvalidFieldRequest();
1451     }
1452 
1453     // Get Sensor count. This returns the number of sensors
1454     if (numSensors > 0)
1455     {
1456         lunsAndDynamicPopulation |= 1;
1457     }
1458     if (numSensors > maxSensorsPerLUN)
1459     {
1460         lunsAndDynamicPopulation |= 2;
1461     }
1462     if (numSensors >= (maxSensorsPerLUN * 2))
1463     {
1464         lunsAndDynamicPopulation |= 8;
1465     }
1466     if (numSensors > maxIPMISensors)
1467     {
1468         // error
1469         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1470     }
1471 
1472     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1473                                  sdrLastAdd);
1474 }
1475 
1476 /* end sensor commands */
1477 
1478 /* storage commands */
1479 
1480 ipmi::RspType<uint8_t,  // sdr version
1481               uint16_t, // record count
1482               uint16_t, // free space
1483               uint32_t, // most recent addition
1484               uint32_t, // most recent erase
1485               uint8_t   // operationSupport
1486               >
1487     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1488 {
1489     auto& sensorTree = getSensorTree();
1490     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
1491     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
1492     {
1493         return ipmi::responseResponseError();
1494     }
1495 
1496     size_t fruCount = 0;
1497     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1498     if (ret != ipmi::ccSuccess)
1499     {
1500         return ipmi::response(ret);
1501     }
1502 
1503     uint16_t recordCount =
1504         sensorTree.size() + fruCount + ipmi::storage::type12Count;
1505 
1506     uint8_t operationSupport = static_cast<uint8_t>(
1507         SdrRepositoryInfoOps::overflow); // write not supported
1508 
1509     operationSupport |=
1510         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1511     operationSupport |= static_cast<uint8_t>(
1512         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1513     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1514                                  unspecifiedFreeSpace, sdrLastAdd,
1515                                  sdrLastRemove, operationSupport);
1516 }
1517 
1518 /** @brief implements the get SDR allocation info command
1519  *
1520  *  @returns IPMI completion code plus response data
1521  *   - allocUnits    - Number of possible allocation units
1522  *   - allocUnitSize - Allocation unit size in bytes.
1523  *   - allocUnitFree - Number of free allocation units
1524  *   - allocUnitLargestFree - Largest free block in allocation units
1525  *   - maxRecordSize    - Maximum record size in allocation units.
1526  */
1527 ipmi::RspType<uint16_t, // allocUnits
1528               uint16_t, // allocUnitSize
1529               uint16_t, // allocUnitFree
1530               uint16_t, // allocUnitLargestFree
1531               uint8_t   // maxRecordSize
1532               >
1533     ipmiStorageGetSDRAllocationInfo()
1534 {
1535     // 0000h unspecified number of alloc units
1536     constexpr uint16_t allocUnits = 0;
1537 
1538     constexpr uint16_t allocUnitFree = 0;
1539     constexpr uint16_t allocUnitLargestFree = 0;
1540     // only allow one block at a time
1541     constexpr uint8_t maxRecordSize = 1;
1542 
1543     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1544                                  allocUnitLargestFree, maxRecordSize);
1545 }
1546 
1547 /** @brief implements the reserve SDR command
1548  *  @returns IPMI completion code plus response data
1549  *   - sdrReservationID
1550  */
1551 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1552 {
1553     sdrReservationID++;
1554     if (sdrReservationID == 0)
1555     {
1556         sdrReservationID++;
1557     }
1558 
1559     return ipmi::responseSuccess(sdrReservationID);
1560 }
1561 
1562 ipmi::RspType<uint16_t,            // next record ID
1563               std::vector<uint8_t> // payload
1564               >
1565     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1566                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1567 {
1568     size_t fruCount = 0;
1569     // reservation required for partial reads with non zero offset into
1570     // record
1571     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1572     {
1573         phosphor::logging::log<phosphor::logging::level::ERR>(
1574             "ipmiStorageGetSDR: responseInvalidReservationId");
1575         return ipmi::responseInvalidReservationId();
1576     }
1577     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1578     if (ret != ipmi::ccSuccess)
1579     {
1580         phosphor::logging::log<phosphor::logging::level::ERR>(
1581             "ipmiStorageGetSDR: getFruSdrCount error");
1582         return ipmi::response(ret);
1583     }
1584 
1585     auto& sensorTree = getSensorTree();
1586     size_t lastRecord =
1587         sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
1588     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1589 
1590     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
1591     {
1592         phosphor::logging::log<phosphor::logging::level::ERR>(
1593             "ipmiStorageGetSDR: getSensorSubtree error");
1594         return ipmi::responseResponseError();
1595     }
1596 
1597     std::vector<uint8_t> record;
1598     if (getSensorDataRecord(ctx, record, recordID))
1599     {
1600         phosphor::logging::log<phosphor::logging::level::ERR>(
1601             "ipmiStorageGetSDR: fail to get SDR");
1602         return ipmi::responseInvalidFieldRequest();
1603     }
1604     get_sdr::SensorDataRecordHeader* hdr =
1605         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
1606     if (!hdr)
1607     {
1608         phosphor::logging::log<phosphor::logging::level::ERR>(
1609             "ipmiStorageGetSDR: record header is null");
1610         return ipmi::responseSuccess(nextRecordId, record);
1611     }
1612 
1613     size_t sdrLength =
1614         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1615     if (sdrLength < (offset + bytesToRead))
1616     {
1617         bytesToRead = sdrLength - offset;
1618     }
1619 
1620     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1621     if (!respStart)
1622     {
1623         phosphor::logging::log<phosphor::logging::level::ERR>(
1624             "ipmiStorageGetSDR: record is null");
1625         return ipmi::responseSuccess(nextRecordId, record);
1626     }
1627 
1628     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1629 
1630     return ipmi::responseSuccess(nextRecordId, recordData);
1631 }
1632 /* end storage commands */
1633 
1634 void registerSensorFunctions()
1635 {
1636     // <Platform Event>
1637     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1638                           ipmi::sensor_event::cmdPlatformEvent,
1639                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1640 
1641 #ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1642     // <Set Sensor Reading and Event Status>
1643     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1644                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1645                           ipmi::Privilege::Operator, ipmiSetSensorReading);
1646 #endif
1647 
1648     // <Get Sensor Reading>
1649     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1650                           ipmi::sensor_event::cmdGetSensorReading,
1651                           ipmi::Privilege::User, ipmiSenGetSensorReading);
1652 
1653     // <Get Sensor Threshold>
1654     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1655                           ipmi::sensor_event::cmdGetSensorThreshold,
1656                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1657 
1658     // <Set Sensor Threshold>
1659     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1660                           ipmi::sensor_event::cmdSetSensorThreshold,
1661                           ipmi::Privilege::Operator,
1662                           ipmiSenSetSensorThresholds);
1663 
1664     // <Get Sensor Event Enable>
1665     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1666                           ipmi::sensor_event::cmdGetSensorEventEnable,
1667                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1668 
1669     // <Get Sensor Event Status>
1670     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1671                           ipmi::sensor_event::cmdGetSensorEventStatus,
1672                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1673 
1674     // register all storage commands for both Sensor and Storage command
1675     // versions
1676 
1677     // <Get SDR Repository Info>
1678     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1679                           ipmi::storage::cmdGetSdrRepositoryInfo,
1680                           ipmi::Privilege::User,
1681                           ipmiStorageGetSDRRepositoryInfo);
1682 
1683     // <Get Device SDR Info>
1684     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1685                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
1686                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1687 
1688     // <Get SDR Allocation Info>
1689     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1690                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1691                           ipmi::Privilege::User,
1692                           ipmiStorageGetSDRAllocationInfo);
1693 
1694     // <Reserve SDR Repo>
1695     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1696                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1697                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1698 
1699     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1700                           ipmi::storage::cmdReserveSdrRepository,
1701                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1702 
1703     // <Get Sdr>
1704     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1705                           ipmi::sensor_event::cmdGetDeviceSdr,
1706                           ipmi::Privilege::User, ipmiStorageGetSDR);
1707 
1708     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1709                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1710                           ipmiStorageGetSDR);
1711 }
1712 } // namespace ipmi
1713