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