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