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