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