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