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