1 /*
2 // Copyright (c) 2017 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "dbus-sdr/sensorcommands.hpp"
18 
19 #include "dbus-sdr/sdrutils.hpp"
20 #include "dbus-sdr/sensorutils.hpp"
21 #include "dbus-sdr/storagecommands.hpp"
22 #include "entity_map_json.hpp"
23 
24 #include <algorithm>
25 #include <array>
26 #include <boost/algorithm/string.hpp>
27 #include <boost/container/flat_map.hpp>
28 #include <chrono>
29 #include <cmath>
30 #include <cstring>
31 #include <iostream>
32 #include <ipmid/api.hpp>
33 #include <ipmid/types.hpp>
34 #include <ipmid/utils.hpp>
35 #include <map>
36 #include <memory>
37 #include <optional>
38 #include <phosphor-logging/log.hpp>
39 #include <sdbusplus/bus.hpp>
40 #include <stdexcept>
41 #include <string>
42 #include <user_channel/channel_layer.hpp>
43 #include <utility>
44 #include <variant>
45 
46 #ifdef FEATURE_HYBRID_SENSORS
47 
48 #include "sensordatahandler.hpp"
49 namespace ipmi
50 {
51 namespace sensor
52 {
53 extern const IdInfoMap sensors;
54 } // namespace sensor
55 } // namespace ipmi
56 #endif
57 
58 constexpr std::array<const char*, 7> suffixes = {
59     "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
60     "_Output_Power",   "_Input_Power",   "_Temperature"};
61 namespace ipmi
62 {
63 
64 using phosphor::logging::entry;
65 using phosphor::logging::level;
66 using phosphor::logging::log;
67 
68 static constexpr int sensorMapUpdatePeriod = 10;
69 static constexpr int sensorMapSdrUpdatePeriod = 60;
70 
71 // BMC I2C address is generally at 0x20
72 static constexpr uint8_t bmcI2CAddr = 0x20;
73 
74 constexpr size_t maxSDRTotalSize =
75     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
76 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
77 
78 static uint16_t sdrReservationID;
79 static uint32_t sdrLastAdd = noTimestamp;
80 static uint32_t sdrLastRemove = noTimestamp;
81 static constexpr size_t lastRecordIndex = 0xFFFF;
82 
83 // The IPMI spec defines four Logical Units (LUN), each capable of supporting
84 // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
85 // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
86 // number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
87 // location.
88 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
89 
90 static constexpr size_t lun0MaxSensorNum = 0xfe;
91 static constexpr size_t lun1MaxSensorNum = 0x1fe;
92 static constexpr size_t lun3MaxSensorNum = 0x3fe;
93 static constexpr int GENERAL_ERROR = -1;
94 
95 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
96 
97 // Specify the comparison required to sort and find char* map objects
98 struct CmpStr
99 {
100     bool operator()(const char* a, const char* b) const
101     {
102         return std::strcmp(a, b) < 0;
103     }
104 };
105 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
106     sensorUnits{{{"temperature", SensorUnits::degreesC},
107                  {"voltage", SensorUnits::volts},
108                  {"current", SensorUnits::amps},
109                  {"fan_tach", SensorUnits::rpm},
110                  {"power", SensorUnits::watts}}};
111 
112 void registerSensorFunctions() __attribute__((constructor));
113 
114 static sdbusplus::bus::match::match sensorAdded(
115     *getSdBus(),
116     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
117     "sensors/'",
118     [](sdbusplus::message::message& m) {
119         getSensorTree().clear();
120         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
121                          std::chrono::system_clock::now().time_since_epoch())
122                          .count();
123     });
124 
125 static sdbusplus::bus::match::match sensorRemoved(
126     *getSdBus(),
127     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
128     "sensors/'",
129     [](sdbusplus::message::message& m) {
130         getSensorTree().clear();
131         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
132                             std::chrono::system_clock::now().time_since_epoch())
133                             .count();
134     });
135 
136 // this keeps track of deassertions for sensor event status command. A
137 // deasertion can only happen if an assertion was seen first.
138 static boost::container::flat_map<
139     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
140     thresholdDeassertMap;
141 
142 static sdbusplus::bus::match::match thresholdChanged(
143     *getSdBus(),
144     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
145     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
146     [](sdbusplus::message::message& m) {
147         boost::container::flat_map<std::string, std::variant<bool, double>>
148             values;
149         m.read(std::string(), values);
150 
151         auto findAssert =
152             std::find_if(values.begin(), values.end(), [](const auto& pair) {
153                 return pair.first.find("Alarm") != std::string::npos;
154             });
155         if (findAssert != values.end())
156         {
157             auto ptr = std::get_if<bool>(&(findAssert->second));
158             if (ptr == nullptr)
159             {
160                 phosphor::logging::log<phosphor::logging::level::ERR>(
161                     "thresholdChanged: Assert non bool");
162                 return;
163             }
164             if (*ptr)
165             {
166                 phosphor::logging::log<phosphor::logging::level::INFO>(
167                     "thresholdChanged: Assert",
168                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
169                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
170             }
171             else
172             {
173                 auto& value =
174                     thresholdDeassertMap[m.get_path()][findAssert->first];
175                 if (value)
176                 {
177                     phosphor::logging::log<phosphor::logging::level::INFO>(
178                         "thresholdChanged: deassert",
179                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
180                     value = *ptr;
181                 }
182             }
183         }
184     });
185 
186 namespace sensor
187 {
188 static constexpr const char* vrInterface =
189     "xyz.openbmc_project.Control.VoltageRegulatorMode";
190 static constexpr const char* sensorInterface =
191     "xyz.openbmc_project.Sensor.Value";
192 } // namespace sensor
193 
194 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
195                             double& min)
196 {
197     max = 127;
198     min = -128;
199 
200     auto sensorObject = sensorMap.find(sensor::sensorInterface);
201     auto critical =
202         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
203     auto warning =
204         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
205 
206     if (sensorObject != sensorMap.end())
207     {
208         auto maxMap = sensorObject->second.find("MaxValue");
209         auto minMap = sensorObject->second.find("MinValue");
210 
211         if (maxMap != sensorObject->second.end())
212         {
213             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
214         }
215         if (minMap != sensorObject->second.end())
216         {
217             min = std::visit(VariantToDoubleVisitor(), minMap->second);
218         }
219     }
220     if (critical != sensorMap.end())
221     {
222         auto lower = critical->second.find("CriticalLow");
223         auto upper = critical->second.find("CriticalHigh");
224         if (lower != critical->second.end())
225         {
226             double value = std::visit(VariantToDoubleVisitor(), lower->second);
227             if (std::isfinite(value))
228             {
229                 min = std::min(value, min);
230             }
231         }
232         if (upper != critical->second.end())
233         {
234             double value = std::visit(VariantToDoubleVisitor(), upper->second);
235             if (std::isfinite(value))
236             {
237                 max = std::max(value, max);
238             }
239         }
240     }
241     if (warning != sensorMap.end())
242     {
243 
244         auto lower = warning->second.find("WarningLow");
245         auto upper = warning->second.find("WarningHigh");
246         if (lower != warning->second.end())
247         {
248             double value = std::visit(VariantToDoubleVisitor(), lower->second);
249             if (std::isfinite(value))
250             {
251                 min = std::min(value, min);
252             }
253         }
254         if (upper != warning->second.end())
255         {
256             double value = std::visit(VariantToDoubleVisitor(), upper->second);
257             if (std::isfinite(value))
258             {
259                 max = std::max(value, max);
260             }
261         }
262     }
263 }
264 
265 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
266                          std::string sensorPath, DbusInterfaceMap& sensorMap,
267                          int updatePeriod = sensorMapUpdatePeriod)
268 {
269 #ifdef FEATURE_HYBRID_SENSORS
270     if (auto sensor = findStaticSensor(sensorPath);
271         sensor != ipmi::sensor::sensors.end() &&
272         getSensorEventTypeFromPath(sensorPath) !=
273             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
274     {
275         // If the incoming sensor is a discrete sensor, it might fail in
276         // getManagedObjects(), return true, and use its own getFunc to get
277         // value.
278         return true;
279     }
280 #endif
281 
282     static boost::container::flat_map<
283         std::string, std::chrono::time_point<std::chrono::steady_clock>>
284         updateTimeMap;
285 
286     auto updateFind = updateTimeMap.find(sensorConnection);
287     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
288     if (updateFind != updateTimeMap.end())
289     {
290         lastUpdate = updateFind->second;
291     }
292 
293     auto now = std::chrono::steady_clock::now();
294 
295     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
296             .count() > updatePeriod)
297     {
298         ObjectValueTree managedObjects;
299         boost::system::error_code ec = getManagedObjects(
300             ctx, sensorConnection.c_str(), "/", managedObjects);
301         if (ec)
302         {
303             phosphor::logging::log<phosphor::logging::level::ERR>(
304                 "GetMangagedObjects for getSensorMap failed",
305                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
306 
307             return false;
308         }
309 
310         SensorCache[sensorConnection] = managedObjects;
311         // Update time after finish building the map which allow the
312         // data to be cached for updatePeriod plus the build time.
313         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
314     }
315     auto connection = SensorCache.find(sensorConnection);
316     if (connection == SensorCache.end())
317     {
318         return false;
319     }
320     auto path = connection->second.find(sensorPath);
321     if (path == connection->second.end())
322     {
323         return false;
324     }
325     sensorMap = path->second;
326 
327     return true;
328 }
329 
330 namespace sensor
331 {
332 // Read VR profiles from sensor(daemon) interface
333 static std::optional<std::vector<std::string>>
334     getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
335 {
336     // get VR mode profiles from Supported Interface
337     auto supportedProperty = object.find("Supported");
338     if (supportedProperty == object.end() ||
339         object.find("Selected") == object.end())
340     {
341         phosphor::logging::log<phosphor::logging::level::ERR>(
342             "Missing the required Supported and Selected properties");
343         return std::nullopt;
344     }
345 
346     const auto profilesPtr =
347         std::get_if<std::vector<std::string>>(&supportedProperty->second);
348 
349     if (profilesPtr == nullptr)
350     {
351         phosphor::logging::log<phosphor::logging::level::ERR>(
352             "property is not array of string");
353         return std::nullopt;
354     }
355     return *profilesPtr;
356 }
357 
358 // Calculate VR Mode from input IPMI discrete event bytes
359 static std::optional<std::string>
360     calculateVRMode(uint15_t assertOffset,
361                     const ipmi::DbusInterfaceMap::mapped_type& VRObject)
362 {
363     // get VR mode profiles from Supported Interface
364     auto profiles = getSupportedVrProfiles(VRObject);
365     if (!profiles)
366     {
367         return std::nullopt;
368     }
369 
370     // interpret IPMI cmd bits into profiles' index
371     long unsigned int index = 0;
372     // only one bit should be set and the highest bit should not be used.
373     if (assertOffset == 0 || assertOffset == (1u << 15) ||
374         (assertOffset & (assertOffset - 1)))
375     {
376         phosphor::logging::log<phosphor::logging::level::ERR>(
377             "IPMI cmd format incorrect",
378 
379             phosphor::logging::entry("BYTES=%#02x",
380                                      static_cast<uint16_t>(assertOffset)));
381         return std::nullopt;
382     }
383 
384     while (assertOffset != 1)
385     {
386         assertOffset >>= 1;
387         index++;
388     }
389 
390     if (index >= profiles->size())
391     {
392         phosphor::logging::log<phosphor::logging::level::ERR>(
393             "profile index out of boundary");
394         return std::nullopt;
395     }
396 
397     return profiles->at(index);
398 }
399 
400 // Calculate sensor value from IPMI reading byte
401 static std::optional<double>
402     calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
403                    const ipmi::DbusInterfaceMap::mapped_type& valueObject)
404 {
405     if (valueObject.find("Value") == valueObject.end())
406     {
407         phosphor::logging::log<phosphor::logging::level::ERR>(
408             "Missing the required Value property");
409         return std::nullopt;
410     }
411 
412     double max = 0;
413     double min = 0;
414     getSensorMaxMin(sensorMap, max, min);
415 
416     int16_t mValue = 0;
417     int16_t bValue = 0;
418     int8_t rExp = 0;
419     int8_t bExp = 0;
420     bool bSigned = false;
421 
422     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
423     {
424         return std::nullopt;
425     }
426 
427     double value = bSigned ? ((int8_t)reading) : reading;
428 
429     value *= ((double)mValue);
430     value += ((double)bValue) * std::pow(10.0, bExp);
431     value *= std::pow(10.0, rExp);
432 
433     return value;
434 }
435 
436 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
437 // if it is too long.
438 std::string parseSdrIdFromPath(const std::string& path)
439 {
440     std::string name;
441     size_t nameStart = path.rfind("/");
442     if (nameStart != std::string::npos)
443     {
444         name = path.substr(nameStart + 1, std::string::npos - nameStart);
445     }
446 
447     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
448     {
449         // try to not truncate by replacing common words
450         for (const auto& suffix : suffixes)
451         {
452             if (boost::ends_with(name, suffix))
453             {
454                 boost::replace_all(name, suffix, "");
455                 break;
456             }
457         }
458         if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
459         {
460             name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
461         }
462     }
463     std::replace(name.begin(), name.end(), '_', ' ');
464     return name;
465 }
466 
467 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
468                       const std::string& path,
469                       const ipmi::DbusInterfaceMap::mapped_type& object,
470                       std::bitset<16>& assertions)
471 {
472     auto profiles = sensor::getSupportedVrProfiles(object);
473     if (!profiles)
474     {
475         return false;
476     }
477     ipmi::Value modeVariant;
478 
479     auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
480                               "Selected", modeVariant);
481     if (ec)
482     {
483         log<level::ERR>("Failed to get property",
484                         entry("PROPERTY=%s", "Selected"),
485                         entry("PATH=%s", path.c_str()),
486                         entry("INTERFACE=%s", sensor::sensorInterface),
487                         entry("WHAT=%s", ec.message().c_str()));
488         return false;
489     }
490 
491     auto mode = std::get_if<std::string>(&modeVariant);
492     if (mode == nullptr)
493     {
494         log<level::ERR>("property is not a string",
495                         entry("PROPERTY=%s", "Selected"),
496                         entry("PATH=%s", path.c_str()),
497                         entry("INTERFACE=%s", sensor::sensorInterface));
498         return false;
499     }
500 
501     auto itr = std::find(profiles->begin(), profiles->end(), *mode);
502     if (itr == profiles->end())
503     {
504         using namespace phosphor::logging;
505         log<level::ERR>("VR mode doesn't match any of its profiles",
506                         entry("PATH=%s", path.c_str()));
507         return false;
508     }
509     std::size_t index =
510         static_cast<std::size_t>(std::distance(profiles->begin(), itr));
511 
512     // map index to reponse event assertion bit.
513     if (index < 8)
514     {
515         assertions.set(1u << index);
516     }
517     else if (index < 15)
518     {
519         assertions.set(1u << (index - 8));
520     }
521     else
522     {
523         log<level::ERR>("VR profile index reaches max assertion bit",
524                         entry("PATH=%s", path.c_str()),
525                         entry("INDEX=%uz", index));
526         return false;
527     }
528     if constexpr (debug)
529     {
530         std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
531                   << " mode is: [" << index << "] " << *mode << std::endl;
532     }
533     return true;
534 }
535 } // namespace sensor
536 
537 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
538                                      ipmi::message::Payload& p)
539 {
540     constexpr const uint8_t validEnvmRev = 0x04;
541     constexpr const uint8_t lastSensorType = 0x2C;
542     constexpr const uint8_t oemReserved = 0xC0;
543 
544     uint8_t sysgeneratorID = 0;
545     uint8_t evmRev = 0;
546     uint8_t sensorType = 0;
547     uint8_t sensorNum = 0;
548     uint8_t eventType = 0;
549     uint8_t eventData1 = 0;
550     std::optional<uint8_t> eventData2 = 0;
551     std::optional<uint8_t> eventData3 = 0;
552     [[maybe_unused]] uint16_t generatorID = 0;
553     ipmi::ChannelInfo chInfo;
554 
555     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
556     {
557         phosphor::logging::log<phosphor::logging::level::ERR>(
558             "Failed to get Channel Info",
559             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
560         return ipmi::responseUnspecifiedError();
561     }
562 
563     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
564         ipmi::EChannelMediumType::systemInterface)
565     {
566 
567         p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
568                  eventData1, eventData2, eventData3);
569         // Refer to IPMI Spec Table 32: SEL Event Records
570         generatorID = (ctx->channel << 12) // Channel
571                       | (0x0 << 10)        // Reserved
572                       | (0x0 << 8)         // 0x0 for sys-soft ID
573                       | ((sysgeneratorID << 1) | 0x1);
574     }
575     else
576     {
577 
578         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
579                  eventData2, eventData3);
580         // Refer to IPMI Spec Table 32: SEL Event Records
581         generatorID = (ctx->channel << 12)      // Channel
582                       | (0x0 << 10)             // Reserved
583                       | ((ctx->lun & 0x3) << 8) // Lun
584                       | (ctx->rqSA << 1);
585     }
586 
587     if (!p.fullyUnpacked())
588     {
589         return ipmi::responseReqDataLenInvalid();
590     }
591 
592     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
593     if (evmRev != validEnvmRev)
594     {
595         return ipmi::responseInvalidFieldRequest();
596     }
597     if ((sensorType > lastSensorType) && (sensorType < oemReserved))
598     {
599         return ipmi::responseInvalidFieldRequest();
600     }
601 
602     return ipmi::responseSuccess();
603 }
604 
605 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
606                                      uint8_t sensorNumber, uint8_t operation,
607                                      uint8_t reading, uint15_t assertOffset,
608                                      bool resvd1, uint15_t deassertOffset,
609                                      bool resvd2, uint8_t eventData1,
610                                      uint8_t eventData2, uint8_t eventData3)
611 {
612     std::string connection;
613     std::string path;
614     std::vector<std::string> interfaces;
615 
616     ipmi::Cc status =
617         getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
618     if (status)
619     {
620         return ipmi::response(status);
621     }
622 
623     // we can tell the sensor type by its interface type
624     if (std::find(interfaces.begin(), interfaces.end(),
625                   sensor::sensorInterface) != interfaces.end())
626     {
627         DbusInterfaceMap sensorMap;
628         if (!getSensorMap(ctx, connection, path, sensorMap))
629         {
630             return ipmi::responseResponseError();
631         }
632         auto sensorObject = sensorMap.find(sensor::sensorInterface);
633         if (sensorObject == sensorMap.end())
634         {
635             return ipmi::responseResponseError();
636         }
637 
638         // Only allow external SetSensor if write permission granted
639         if (!details::sdrWriteTable.getWritePermission(sensorNumber))
640         {
641             return ipmi::responseResponseError();
642         }
643 
644         auto value =
645             sensor::calculateValue(reading, sensorMap, sensorObject->second);
646         if (!value)
647         {
648             return ipmi::responseResponseError();
649         }
650 
651         if constexpr (debug)
652         {
653             phosphor::logging::log<phosphor::logging::level::INFO>(
654                 "IPMI SET_SENSOR",
655                 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
656                 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
657                 phosphor::logging::entry("VALUE=%f", *value));
658         }
659 
660         boost::system::error_code ec =
661             setDbusProperty(ctx, connection, path, sensor::sensorInterface,
662                             "Value", ipmi::Value(*value));
663 
664         // setDbusProperty intended to resolve dbus exception/rc within the
665         // function but failed to achieve that. Catch exception in the ipmi
666         // callback functions for now (e.g. ipmiSetSensorReading).
667         if (ec)
668         {
669             using namespace phosphor::logging;
670             log<level::ERR>("Failed to set property",
671                             entry("PROPERTY=%s", "Value"),
672                             entry("PATH=%s", path.c_str()),
673                             entry("INTERFACE=%s", sensor::sensorInterface),
674                             entry("WHAT=%s", ec.message().c_str()));
675             return ipmi::responseResponseError();
676         }
677         return ipmi::responseSuccess();
678     }
679 
680     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
681         interfaces.end())
682     {
683         DbusInterfaceMap sensorMap;
684         if (!getSensorMap(ctx, connection, path, sensorMap))
685         {
686             return ipmi::responseResponseError();
687         }
688         auto sensorObject = sensorMap.find(sensor::vrInterface);
689         if (sensorObject == sensorMap.end())
690         {
691             return ipmi::responseResponseError();
692         }
693 
694         // VR sensors are treated as a special case and we will not check the
695         // write permission for VR sensors, since they always deemed writable
696         // and permission table are not applied to VR sensors.
697         auto vrMode =
698             sensor::calculateVRMode(assertOffset, sensorObject->second);
699         if (!vrMode)
700         {
701             return ipmi::responseResponseError();
702         }
703         boost::system::error_code ec = setDbusProperty(
704             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
705         // setDbusProperty intended to resolve dbus exception/rc within the
706         // function but failed to achieve that. Catch exception in the ipmi
707         // callback functions for now (e.g. ipmiSetSensorReading).
708         if (ec)
709         {
710             using namespace phosphor::logging;
711             log<level::ERR>("Failed to set property",
712                             entry("PROPERTY=%s", "Selected"),
713                             entry("PATH=%s", path.c_str()),
714                             entry("INTERFACE=%s", sensor::sensorInterface),
715                             entry("WHAT=%s", ec.message().c_str()));
716             return ipmi::responseResponseError();
717         }
718         return ipmi::responseSuccess();
719     }
720 
721     phosphor::logging::log<phosphor::logging::level::ERR>(
722         "unknown sensor type",
723         phosphor::logging::entry("PATH=%s", path.c_str()));
724     return ipmi::responseResponseError();
725 }
726 
727 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
728     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
729 {
730     std::string connection;
731     std::string path;
732 
733     if (sensnum == reservedSensorNumber)
734     {
735         return ipmi::responseInvalidFieldRequest();
736     }
737 
738     auto status = getSensorConnection(ctx, sensnum, connection, path);
739     if (status)
740     {
741         return ipmi::response(status);
742     }
743 
744 #ifdef FEATURE_HYBRID_SENSORS
745     if (auto sensor = findStaticSensor(path);
746         sensor != ipmi::sensor::sensors.end() &&
747         getSensorEventTypeFromPath(path) !=
748             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
749     {
750         if (ipmi::sensor::Mutability::Read !=
751             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
752         {
753             return ipmi::responseIllegalCommand();
754         }
755 
756         uint8_t operation;
757         try
758         {
759             ipmi::sensor::GetSensorResponse getResponse =
760                 sensor->second.getFunc(sensor->second);
761 
762             if (getResponse.readingOrStateUnavailable)
763             {
764                 operation |= static_cast<uint8_t>(
765                     IPMISensorReadingByte2::readingStateUnavailable);
766             }
767             if (getResponse.scanningEnabled)
768             {
769                 operation |= static_cast<uint8_t>(
770                     IPMISensorReadingByte2::sensorScanningEnable);
771             }
772             if (getResponse.allEventMessagesEnabled)
773             {
774                 operation |= static_cast<uint8_t>(
775                     IPMISensorReadingByte2::eventMessagesEnable);
776             }
777             return ipmi::responseSuccess(
778                 getResponse.reading, operation,
779                 getResponse.thresholdLevelsStates,
780                 getResponse.discreteReadingSensorStates);
781         }
782         catch (const std::exception& e)
783         {
784             operation |= static_cast<uint8_t>(
785                 IPMISensorReadingByte2::readingStateUnavailable);
786             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
787         }
788     }
789 #endif
790 
791     DbusInterfaceMap sensorMap;
792     if (!getSensorMap(ctx, connection, path, sensorMap))
793     {
794         return ipmi::responseResponseError();
795     }
796     auto sensorObject = sensorMap.find(sensor::sensorInterface);
797 
798     if (sensorObject == sensorMap.end() ||
799         sensorObject->second.find("Value") == sensorObject->second.end())
800     {
801         return ipmi::responseResponseError();
802     }
803     auto& valueVariant = sensorObject->second["Value"];
804     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
805 
806     double max = 0;
807     double min = 0;
808     getSensorMaxMin(sensorMap, max, min);
809 
810     int16_t mValue = 0;
811     int16_t bValue = 0;
812     int8_t rExp = 0;
813     int8_t bExp = 0;
814     bool bSigned = false;
815 
816     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
817     {
818         return ipmi::responseResponseError();
819     }
820 
821     uint8_t value =
822         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
823     uint8_t operation =
824         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
825     operation |=
826         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
827     bool notReading = std::isnan(reading);
828 
829     if (!notReading)
830     {
831         auto availableObject =
832             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
833         if (availableObject != sensorMap.end())
834         {
835             auto findAvailable = availableObject->second.find("Available");
836             if (findAvailable != availableObject->second.end())
837             {
838                 bool* available = std::get_if<bool>(&(findAvailable->second));
839                 if (available && !(*available))
840                 {
841                     notReading = true;
842                 }
843             }
844         }
845     }
846 
847     if (notReading)
848     {
849         operation |= static_cast<uint8_t>(
850             IPMISensorReadingByte2::readingStateUnavailable);
851     }
852 
853     if constexpr (details::enableInstrumentation)
854     {
855         int byteValue;
856         if (bSigned)
857         {
858             byteValue = static_cast<int>(static_cast<int8_t>(value));
859         }
860         else
861         {
862             byteValue = static_cast<int>(static_cast<uint8_t>(value));
863         }
864 
865         // Keep stats on the reading just obtained, even if it is "NaN"
866         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
867         {
868             // This is the first reading, show the coefficients
869             double step = (max - min) / 255.0;
870             std::cerr << "IPMI sensor "
871                       << details::sdrStatsTable.getName(sensnum)
872                       << ": Range min=" << min << " max=" << max
873                       << ", step=" << step
874                       << ", Coefficients mValue=" << static_cast<int>(mValue)
875                       << " rExp=" << static_cast<int>(rExp)
876                       << " bValue=" << static_cast<int>(bValue)
877                       << " bExp=" << static_cast<int>(bExp)
878                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
879         }
880     }
881 
882     uint8_t thresholds = 0;
883 
884     auto warningObject =
885         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
886     if (warningObject != sensorMap.end())
887     {
888         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
889         auto alarmLow = warningObject->second.find("WarningAlarmLow");
890         if (alarmHigh != warningObject->second.end())
891         {
892             if (std::get<bool>(alarmHigh->second))
893             {
894                 thresholds |= static_cast<uint8_t>(
895                     IPMISensorReadingByte3::upperNonCritical);
896             }
897         }
898         if (alarmLow != warningObject->second.end())
899         {
900             if (std::get<bool>(alarmLow->second))
901             {
902                 thresholds |= static_cast<uint8_t>(
903                     IPMISensorReadingByte3::lowerNonCritical);
904             }
905         }
906     }
907 
908     auto criticalObject =
909         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
910     if (criticalObject != sensorMap.end())
911     {
912         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
913         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
914         if (alarmHigh != criticalObject->second.end())
915         {
916             if (std::get<bool>(alarmHigh->second))
917             {
918                 thresholds |=
919                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
920             }
921         }
922         if (alarmLow != criticalObject->second.end())
923         {
924             if (std::get<bool>(alarmLow->second))
925             {
926                 thresholds |=
927                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
928             }
929         }
930     }
931 
932     // no discrete as of today so optional byte is never returned
933     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
934 }
935 
936 /** @brief implements the Set Sensor threshold command
937  *  @param sensorNumber        - sensor number
938  *  @param lowerNonCriticalThreshMask
939  *  @param lowerCriticalThreshMask
940  *  @param lowerNonRecovThreshMask
941  *  @param upperNonCriticalThreshMask
942  *  @param upperCriticalThreshMask
943  *  @param upperNonRecovThreshMask
944  *  @param reserved
945  *  @param lowerNonCritical    - lower non-critical threshold
946  *  @param lowerCritical       - Lower critical threshold
947  *  @param lowerNonRecoverable - Lower non recovarable threshold
948  *  @param upperNonCritical    - Upper non-critical threshold
949  *  @param upperCritical       - Upper critical
950  *  @param upperNonRecoverable - Upper Non-recoverable
951  *
952  *  @returns IPMI completion code
953  */
954 ipmi::RspType<> ipmiSenSetSensorThresholds(
955     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
956     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
957     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
958     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
959     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
960     uint8_t upperNonCritical, uint8_t upperCritical,
961     uint8_t upperNonRecoverable)
962 {
963     if (sensorNum == reservedSensorNumber || reserved)
964     {
965         return ipmi::responseInvalidFieldRequest();
966     }
967 
968     // lower nc and upper nc not suppported on any sensor
969     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
970     {
971         return ipmi::responseInvalidFieldRequest();
972     }
973 
974     // if none of the threshold mask are set, nothing to do
975     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
976           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
977           upperCriticalThreshMask | upperNonRecovThreshMask))
978     {
979         return ipmi::responseSuccess();
980     }
981 
982     std::string connection;
983     std::string path;
984 
985     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
986     if (status)
987     {
988         return ipmi::response(status);
989     }
990     DbusInterfaceMap sensorMap;
991     if (!getSensorMap(ctx, connection, path, sensorMap))
992     {
993         return ipmi::responseResponseError();
994     }
995 
996     double max = 0;
997     double min = 0;
998     getSensorMaxMin(sensorMap, max, min);
999 
1000     int16_t mValue = 0;
1001     int16_t bValue = 0;
1002     int8_t rExp = 0;
1003     int8_t bExp = 0;
1004     bool bSigned = false;
1005 
1006     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1007     {
1008         return ipmi::responseResponseError();
1009     }
1010 
1011     // store a vector of property name, value to set, and interface
1012     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1013 
1014     // define the indexes of the tuple
1015     constexpr uint8_t propertyName = 0;
1016     constexpr uint8_t thresholdValue = 1;
1017     constexpr uint8_t interface = 2;
1018     // verifiy all needed fields are present
1019     if (lowerCriticalThreshMask || upperCriticalThreshMask)
1020     {
1021         auto findThreshold =
1022             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1023         if (findThreshold == sensorMap.end())
1024         {
1025             return ipmi::responseInvalidFieldRequest();
1026         }
1027         if (lowerCriticalThreshMask)
1028         {
1029             auto findLower = findThreshold->second.find("CriticalLow");
1030             if (findLower == findThreshold->second.end())
1031             {
1032                 return ipmi::responseInvalidFieldRequest();
1033             }
1034             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1035                                          findThreshold->first);
1036         }
1037         if (upperCriticalThreshMask)
1038         {
1039             auto findUpper = findThreshold->second.find("CriticalHigh");
1040             if (findUpper == findThreshold->second.end())
1041             {
1042                 return ipmi::responseInvalidFieldRequest();
1043             }
1044             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1045                                          findThreshold->first);
1046         }
1047     }
1048     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1049     {
1050         auto findThreshold =
1051             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1052         if (findThreshold == sensorMap.end())
1053         {
1054             return ipmi::responseInvalidFieldRequest();
1055         }
1056         if (lowerNonCriticalThreshMask)
1057         {
1058             auto findLower = findThreshold->second.find("WarningLow");
1059             if (findLower == findThreshold->second.end())
1060             {
1061                 return ipmi::responseInvalidFieldRequest();
1062             }
1063             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1064                                          findThreshold->first);
1065         }
1066         if (upperNonCriticalThreshMask)
1067         {
1068             auto findUpper = findThreshold->second.find("WarningHigh");
1069             if (findUpper == findThreshold->second.end())
1070             {
1071                 return ipmi::responseInvalidFieldRequest();
1072             }
1073             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1074                                          findThreshold->first);
1075         }
1076     }
1077     for (const auto& property : thresholdsToSet)
1078     {
1079         // from section 36.3 in the IPMI Spec, assume all linear
1080         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1081                              (bValue * std::pow(10.0, bExp))) *
1082                             std::pow(10.0, rExp);
1083         setDbusProperty(
1084             *getSdBus(), connection, path, std::get<interface>(property),
1085             std::get<propertyName>(property), ipmi::Value(valueToSet));
1086     }
1087     return ipmi::responseSuccess();
1088 }
1089 
1090 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1091 {
1092     IPMIThresholds resp;
1093     auto warningInterface =
1094         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1095     auto criticalInterface =
1096         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1097 
1098     if ((warningInterface != sensorMap.end()) ||
1099         (criticalInterface != sensorMap.end()))
1100     {
1101         auto sensorPair = sensorMap.find(sensor::sensorInterface);
1102 
1103         if (sensorPair == sensorMap.end())
1104         {
1105             // should not have been able to find a sensor not implementing
1106             // the sensor object
1107             throw std::runtime_error("Invalid sensor map");
1108         }
1109 
1110         double max = 0;
1111         double min = 0;
1112         getSensorMaxMin(sensorMap, max, min);
1113 
1114         int16_t mValue = 0;
1115         int16_t bValue = 0;
1116         int8_t rExp = 0;
1117         int8_t bExp = 0;
1118         bool bSigned = false;
1119 
1120         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1121         {
1122             throw std::runtime_error("Invalid sensor atrributes");
1123         }
1124         if (warningInterface != sensorMap.end())
1125         {
1126             auto& warningMap = warningInterface->second;
1127 
1128             auto warningHigh = warningMap.find("WarningHigh");
1129             auto warningLow = warningMap.find("WarningLow");
1130 
1131             if (warningHigh != warningMap.end())
1132             {
1133 
1134                 double value =
1135                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1136                 if (std::isfinite(value))
1137                 {
1138                     resp.warningHigh = scaleIPMIValueFromDouble(
1139                         value, mValue, rExp, bValue, bExp, bSigned);
1140                 }
1141             }
1142             if (warningLow != warningMap.end())
1143             {
1144                 double value =
1145                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1146                 if (std::isfinite(value))
1147                 {
1148                     resp.warningLow = scaleIPMIValueFromDouble(
1149                         value, mValue, rExp, bValue, bExp, bSigned);
1150                 }
1151             }
1152         }
1153         if (criticalInterface != sensorMap.end())
1154         {
1155             auto& criticalMap = criticalInterface->second;
1156 
1157             auto criticalHigh = criticalMap.find("CriticalHigh");
1158             auto criticalLow = criticalMap.find("CriticalLow");
1159 
1160             if (criticalHigh != criticalMap.end())
1161             {
1162                 double value =
1163                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1164                 if (std::isfinite(value))
1165                 {
1166                     resp.criticalHigh = scaleIPMIValueFromDouble(
1167                         value, mValue, rExp, bValue, bExp, bSigned);
1168                 }
1169             }
1170             if (criticalLow != criticalMap.end())
1171             {
1172                 double value =
1173                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1174                 if (std::isfinite(value))
1175                 {
1176                     resp.criticalLow = scaleIPMIValueFromDouble(
1177                         value, mValue, rExp, bValue, bExp, bSigned);
1178                 }
1179             }
1180         }
1181     }
1182     return resp;
1183 }
1184 
1185 ipmi::RspType<uint8_t, // readable
1186               uint8_t, // lowerNCrit
1187               uint8_t, // lowerCrit
1188               uint8_t, // lowerNrecoverable
1189               uint8_t, // upperNC
1190               uint8_t, // upperCrit
1191               uint8_t> // upperNRecoverable
1192     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1193 {
1194     std::string connection;
1195     std::string path;
1196 
1197     if (sensorNumber == reservedSensorNumber)
1198     {
1199         return ipmi::responseInvalidFieldRequest();
1200     }
1201 
1202     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1203     if (status)
1204     {
1205         return ipmi::response(status);
1206     }
1207 
1208     DbusInterfaceMap sensorMap;
1209     if (!getSensorMap(ctx, connection, path, sensorMap))
1210     {
1211         return ipmi::responseResponseError();
1212     }
1213 
1214     IPMIThresholds thresholdData;
1215     try
1216     {
1217         thresholdData = getIPMIThresholds(sensorMap);
1218     }
1219     catch (const std::exception&)
1220     {
1221         return ipmi::responseResponseError();
1222     }
1223 
1224     uint8_t readable = 0;
1225     uint8_t lowerNC = 0;
1226     uint8_t lowerCritical = 0;
1227     uint8_t lowerNonRecoverable = 0;
1228     uint8_t upperNC = 0;
1229     uint8_t upperCritical = 0;
1230     uint8_t upperNonRecoverable = 0;
1231 
1232     if (thresholdData.warningHigh)
1233     {
1234         readable |=
1235             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1236         upperNC = *thresholdData.warningHigh;
1237     }
1238     if (thresholdData.warningLow)
1239     {
1240         readable |=
1241             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1242         lowerNC = *thresholdData.warningLow;
1243     }
1244 
1245     if (thresholdData.criticalHigh)
1246     {
1247         readable |=
1248             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1249         upperCritical = *thresholdData.criticalHigh;
1250     }
1251     if (thresholdData.criticalLow)
1252     {
1253         readable |=
1254             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1255         lowerCritical = *thresholdData.criticalLow;
1256     }
1257 
1258     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1259                                  lowerNonRecoverable, upperNC, upperCritical,
1260                                  upperNonRecoverable);
1261 }
1262 
1263 /** @brief implements the get Sensor event enable command
1264  *  @param sensorNumber - sensor number
1265  *
1266  *  @returns IPMI completion code plus response data
1267  *   - enabled               - Sensor Event messages
1268  *   - assertionEnabledLsb   - Assertion event messages
1269  *   - assertionEnabledMsb   - Assertion event messages
1270  *   - deassertionEnabledLsb - Deassertion event messages
1271  *   - deassertionEnabledMsb - Deassertion event messages
1272  */
1273 
1274 ipmi::RspType<uint8_t, // enabled
1275               uint8_t, // assertionEnabledLsb
1276               uint8_t, // assertionEnabledMsb
1277               uint8_t, // deassertionEnabledLsb
1278               uint8_t> // deassertionEnabledMsb
1279     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1280 {
1281     std::string connection;
1282     std::string path;
1283 
1284     uint8_t enabled = 0;
1285     uint8_t assertionEnabledLsb = 0;
1286     uint8_t assertionEnabledMsb = 0;
1287     uint8_t deassertionEnabledLsb = 0;
1288     uint8_t deassertionEnabledMsb = 0;
1289 
1290     if (sensorNum == reservedSensorNumber)
1291     {
1292         return ipmi::responseInvalidFieldRequest();
1293     }
1294 
1295     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1296     if (status)
1297     {
1298         return ipmi::response(status);
1299     }
1300 
1301 #ifdef FEATURE_HYBRID_SENSORS
1302     if (auto sensor = findStaticSensor(path);
1303         sensor != ipmi::sensor::sensors.end() &&
1304         getSensorEventTypeFromPath(path) !=
1305             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1306     {
1307         enabled = static_cast<uint8_t>(
1308             IPMISensorEventEnableByte2::sensorScanningEnable);
1309         uint16_t assertionEnabled = 0;
1310         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1311                                       ->second.begin()
1312                                       ->second.second)
1313         {
1314             assertionEnabled |= (1 << offsetValMap.first);
1315         }
1316         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1317         assertionEnabledMsb =
1318             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1319 
1320         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1321                                      assertionEnabledMsb, deassertionEnabledLsb,
1322                                      deassertionEnabledMsb);
1323     }
1324 #endif
1325 
1326     DbusInterfaceMap sensorMap;
1327     if (!getSensorMap(ctx, connection, path, sensorMap))
1328     {
1329         return ipmi::responseResponseError();
1330     }
1331 
1332     auto warningInterface =
1333         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1334     auto criticalInterface =
1335         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1336     if ((warningInterface != sensorMap.end()) ||
1337         (criticalInterface != sensorMap.end()))
1338     {
1339         enabled = static_cast<uint8_t>(
1340             IPMISensorEventEnableByte2::sensorScanningEnable);
1341         if (warningInterface != sensorMap.end())
1342         {
1343             auto& warningMap = warningInterface->second;
1344 
1345             auto warningHigh = warningMap.find("WarningHigh");
1346             auto warningLow = warningMap.find("WarningLow");
1347             if (warningHigh != warningMap.end())
1348             {
1349                 assertionEnabledLsb |= static_cast<uint8_t>(
1350                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1351                 deassertionEnabledLsb |= static_cast<uint8_t>(
1352                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1353             }
1354             if (warningLow != warningMap.end())
1355             {
1356                 assertionEnabledLsb |= static_cast<uint8_t>(
1357                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1358                 deassertionEnabledLsb |= static_cast<uint8_t>(
1359                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1360             }
1361         }
1362         if (criticalInterface != sensorMap.end())
1363         {
1364             auto& criticalMap = criticalInterface->second;
1365 
1366             auto criticalHigh = criticalMap.find("CriticalHigh");
1367             auto criticalLow = criticalMap.find("CriticalLow");
1368 
1369             if (criticalHigh != criticalMap.end())
1370             {
1371                 assertionEnabledMsb |= static_cast<uint8_t>(
1372                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1373                 deassertionEnabledMsb |= static_cast<uint8_t>(
1374                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1375             }
1376             if (criticalLow != criticalMap.end())
1377             {
1378                 assertionEnabledLsb |= static_cast<uint8_t>(
1379                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1380                 deassertionEnabledLsb |= static_cast<uint8_t>(
1381                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1382             }
1383         }
1384     }
1385 
1386     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1387                                  assertionEnabledMsb, deassertionEnabledLsb,
1388                                  deassertionEnabledMsb);
1389 }
1390 
1391 /** @brief implements the get Sensor event status command
1392  *  @param sensorNumber - sensor number, FFh = reserved
1393  *
1394  *  @returns IPMI completion code plus response data
1395  *   - sensorEventStatus - Sensor Event messages state
1396  *   - assertions        - Assertion event messages
1397  *   - deassertions      - Deassertion event messages
1398  */
1399 ipmi::RspType<uint8_t,         // sensorEventStatus
1400               std::bitset<16>, // assertions
1401               std::bitset<16>  // deassertion
1402               >
1403     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1404 {
1405     if (sensorNum == reservedSensorNumber)
1406     {
1407         return ipmi::responseInvalidFieldRequest();
1408     }
1409 
1410     std::string connection;
1411     std::string path;
1412     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1413     if (status)
1414     {
1415         phosphor::logging::log<phosphor::logging::level::ERR>(
1416             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1417             phosphor::logging::entry("SENSOR=%d", sensorNum));
1418         return ipmi::response(status);
1419     }
1420 
1421 #ifdef FEATURE_HYBRID_SENSORS
1422     if (auto sensor = findStaticSensor(path);
1423         sensor != ipmi::sensor::sensors.end() &&
1424         getSensorEventTypeFromPath(path) !=
1425             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1426     {
1427         auto response = ipmi::sensor::get::mapDbusToAssertion(
1428             sensor->second, path, sensor->second.sensorInterface);
1429         std::bitset<16> assertions;
1430         // deassertions are not used.
1431         std::bitset<16> deassertions = 0;
1432         uint8_t sensorEventStatus;
1433         if (response.readingOrStateUnavailable)
1434         {
1435             sensorEventStatus |= static_cast<uint8_t>(
1436                 IPMISensorReadingByte2::readingStateUnavailable);
1437         }
1438         if (response.scanningEnabled)
1439         {
1440             sensorEventStatus |= static_cast<uint8_t>(
1441                 IPMISensorReadingByte2::sensorScanningEnable);
1442         }
1443         if (response.allEventMessagesEnabled)
1444         {
1445             sensorEventStatus |= static_cast<uint8_t>(
1446                 IPMISensorReadingByte2::eventMessagesEnable);
1447         }
1448         assertions |= response.discreteReadingSensorStates << 8;
1449         assertions |= response.thresholdLevelsStates;
1450         return ipmi::responseSuccess(sensorEventStatus, assertions,
1451                                      deassertions);
1452     }
1453 #endif
1454 
1455     DbusInterfaceMap sensorMap;
1456     if (!getSensorMap(ctx, connection, path, sensorMap))
1457     {
1458         phosphor::logging::log<phosphor::logging::level::ERR>(
1459             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1460             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1461         return ipmi::responseResponseError();
1462     }
1463 
1464     uint8_t sensorEventStatus =
1465         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1466     std::bitset<16> assertions = 0;
1467     std::bitset<16> deassertions = 0;
1468 
1469     // handle VR typed sensor
1470     auto vrInterface = sensorMap.find(sensor::vrInterface);
1471     if (vrInterface != sensorMap.end())
1472     {
1473         if (!sensor::getVrEventStatus(ctx, connection, path,
1474                                       vrInterface->second, assertions))
1475         {
1476             return ipmi::responseResponseError();
1477         }
1478 
1479         // both Event Message and Sensor Scanning are disable for VR.
1480         sensorEventStatus = 0;
1481         return ipmi::responseSuccess(sensorEventStatus, assertions,
1482                                      deassertions);
1483     }
1484 
1485     auto warningInterface =
1486         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1487     auto criticalInterface =
1488         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1489 
1490     std::optional<bool> criticalDeassertHigh =
1491         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1492     std::optional<bool> criticalDeassertLow =
1493         thresholdDeassertMap[path]["CriticalAlarmLow"];
1494     std::optional<bool> warningDeassertHigh =
1495         thresholdDeassertMap[path]["WarningAlarmHigh"];
1496     std::optional<bool> warningDeassertLow =
1497         thresholdDeassertMap[path]["WarningAlarmLow"];
1498 
1499     if (criticalDeassertHigh && !*criticalDeassertHigh)
1500     {
1501         deassertions.set(static_cast<size_t>(
1502             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1503     }
1504     if (criticalDeassertLow && !*criticalDeassertLow)
1505     {
1506         deassertions.set(static_cast<size_t>(
1507             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1508     }
1509     if (warningDeassertHigh && !*warningDeassertHigh)
1510     {
1511         deassertions.set(static_cast<size_t>(
1512             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1513     }
1514     if (warningDeassertLow && !*warningDeassertLow)
1515     {
1516         deassertions.set(static_cast<size_t>(
1517             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1518     }
1519     if ((warningInterface != sensorMap.end()) ||
1520         (criticalInterface != sensorMap.end()))
1521     {
1522         sensorEventStatus = static_cast<size_t>(
1523             IPMISensorEventEnableByte2::eventMessagesEnable);
1524         if (warningInterface != sensorMap.end())
1525         {
1526             auto& warningMap = warningInterface->second;
1527 
1528             auto warningHigh = warningMap.find("WarningAlarmHigh");
1529             auto warningLow = warningMap.find("WarningAlarmLow");
1530             auto warningHighAlarm = false;
1531             auto warningLowAlarm = false;
1532 
1533             if (warningHigh != warningMap.end())
1534             {
1535                 warningHighAlarm = std::get<bool>(warningHigh->second);
1536             }
1537             if (warningLow != warningMap.end())
1538             {
1539                 warningLowAlarm = std::get<bool>(warningLow->second);
1540             }
1541             if (warningHighAlarm)
1542             {
1543                 assertions.set(
1544                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1545                                             upperNonCriticalGoingHigh));
1546             }
1547             if (warningLowAlarm)
1548             {
1549                 assertions.set(
1550                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1551                                             lowerNonCriticalGoingLow));
1552             }
1553         }
1554         if (criticalInterface != sensorMap.end())
1555         {
1556             auto& criticalMap = criticalInterface->second;
1557 
1558             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1559             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1560             auto criticalHighAlarm = false;
1561             auto criticalLowAlarm = false;
1562 
1563             if (criticalHigh != criticalMap.end())
1564             {
1565                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1566             }
1567             if (criticalLow != criticalMap.end())
1568             {
1569                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1570             }
1571             if (criticalHighAlarm)
1572             {
1573                 assertions.set(
1574                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1575                                             upperCriticalGoingHigh));
1576             }
1577             if (criticalLowAlarm)
1578             {
1579                 assertions.set(static_cast<size_t>(
1580                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1581             }
1582         }
1583     }
1584 
1585     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1586 }
1587 
1588 // Construct a type 1 SDR for threshold sensor.
1589 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1590                                  get_sdr::SensorDataFullRecord& record)
1591 {
1592     get_sdr::header::set_record_id(
1593         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1594 
1595     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1596     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1597 
1598     record.header.sdr_version = ipmiSdrVersion;
1599     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1600     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1601                                   sizeof(get_sdr::SensorDataRecordHeader);
1602     record.key.owner_id = bmcI2CAddr;
1603     record.key.owner_lun = lun;
1604     record.key.sensor_number = sensornumber;
1605 }
1606 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1607                         uint16_t recordID, const std::string& service,
1608                         const std::string& path,
1609                         get_sdr::SensorDataFullRecord& record)
1610 {
1611     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1612     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1613 
1614     DbusInterfaceMap sensorMap;
1615     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1616     {
1617         phosphor::logging::log<phosphor::logging::level::ERR>(
1618             "Failed to update sensor map for threshold sensor",
1619             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1620             phosphor::logging::entry("PATH=%s", path.c_str()));
1621         return false;
1622     }
1623 
1624     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1625     record.body.sensor_type = getSensorTypeFromPath(path);
1626     std::string type = getSensorTypeStringFromPath(path);
1627     auto typeCstr = type.c_str();
1628     auto findUnits = sensorUnits.find(typeCstr);
1629     if (findUnits != sensorUnits.end())
1630     {
1631         record.body.sensor_units_2_base =
1632             static_cast<uint8_t>(findUnits->second);
1633     } // else default 0x0 unspecified
1634 
1635     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1636 
1637     auto sensorObject = sensorMap.find(sensor::sensorInterface);
1638     if (sensorObject == sensorMap.end())
1639     {
1640         phosphor::logging::log<phosphor::logging::level::ERR>(
1641             "getSensorDataRecord: sensorObject error");
1642         return false;
1643     }
1644 
1645     uint8_t entityId = 0;
1646     uint8_t entityInstance = 0x01;
1647 
1648     // follow the association chain to get the parent board's entityid and
1649     // entityInstance
1650     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1651 
1652     record.body.entity_id = entityId;
1653     record.body.entity_instance = entityInstance;
1654 
1655     double max = 0;
1656     double min = 0;
1657     getSensorMaxMin(sensorMap, max, min);
1658 
1659     int16_t mValue = 0;
1660     int8_t rExp = 0;
1661     int16_t bValue = 0;
1662     int8_t bExp = 0;
1663     bool bSigned = false;
1664 
1665     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1666     {
1667         phosphor::logging::log<phosphor::logging::level::ERR>(
1668             "getSensorDataRecord: getSensorAttributes error");
1669         return false;
1670     }
1671 
1672     // The record.body is a struct SensorDataFullRecordBody
1673     // from sensorhandler.hpp in phosphor-ipmi-host.
1674     // The meaning of these bits appears to come from
1675     // table 43.1 of the IPMI spec.
1676     // The above 5 sensor attributes are stuffed in as follows:
1677     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1678     // Byte 22-24 are for other purposes
1679     // Byte 25 = MMMMMMMM = LSB of M
1680     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1681     // Byte 27 = BBBBBBBB = LSB of B
1682     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1683     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1684     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1685 
1686     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1687     record.body.m_lsb = mValue & 0xFF;
1688 
1689     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1690     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1691 
1692     // move the smallest bit of the MSB into place (bit 9)
1693     // the MSbs are bits 7:8 in m_msb_and_tolerance
1694     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1695 
1696     record.body.b_lsb = bValue & 0xFF;
1697 
1698     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1699     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1700 
1701     // move the smallest bit of the MSB into place (bit 9)
1702     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1703     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1704 
1705     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1706     uint8_t rExpBits = rExp & 0x07;
1707 
1708     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1709     uint8_t bExpBits = bExp & 0x07;
1710 
1711     // move rExp and bExp into place
1712     record.body.r_b_exponents =
1713         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1714 
1715     // Set the analog reading byte interpretation accordingly
1716     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1717 
1718     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1719     // These seem redundant, but derivable from the above 5 attributes
1720     // Original comment said "todo fill out rest of units"
1721 
1722     // populate sensor name from path
1723     auto name = sensor::parseSdrIdFromPath(path);
1724     record.body.id_string_info = name.size();
1725     std::strncpy(record.body.id_string, name.c_str(),
1726                  sizeof(record.body.id_string));
1727 
1728     // Remember the sensor name, as determined for this sensor number
1729     details::sdrStatsTable.updateName(sensornumber, name);
1730 
1731     bool sensorSettable = false;
1732     auto mutability =
1733         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1734     if (mutability != sensorMap.end())
1735     {
1736         sensorSettable =
1737             mappedVariant<bool>(mutability->second, "Mutable", false);
1738     }
1739     get_sdr::body::init_settable_state(sensorSettable, &record.body);
1740 
1741     // Grant write permission to sensors deemed externally settable
1742     details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
1743 
1744     IPMIThresholds thresholdData;
1745     try
1746     {
1747         thresholdData = getIPMIThresholds(sensorMap);
1748     }
1749     catch (const std::exception&)
1750     {
1751         phosphor::logging::log<phosphor::logging::level::ERR>(
1752             "getSensorDataRecord: getIPMIThresholds error");
1753         return false;
1754     }
1755 
1756     if (thresholdData.criticalHigh)
1757     {
1758         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1759         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1760             IPMISensorEventEnableThresholds::criticalThreshold);
1761         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1762             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1763         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1764             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1765         record.body.discrete_reading_setting_mask[0] |=
1766             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1767     }
1768     if (thresholdData.warningHigh)
1769     {
1770         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1771         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1772             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1773         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1774             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1775         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1776             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1777         record.body.discrete_reading_setting_mask[0] |=
1778             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1779     }
1780     if (thresholdData.criticalLow)
1781     {
1782         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1783         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1784             IPMISensorEventEnableThresholds::criticalThreshold);
1785         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1786             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1787         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1788             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1789         record.body.discrete_reading_setting_mask[0] |=
1790             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1791     }
1792     if (thresholdData.warningLow)
1793     {
1794         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1795         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1796             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1797         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1798             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1799         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1800             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1801         record.body.discrete_reading_setting_mask[0] |=
1802             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1803     }
1804 
1805     // everything that is readable is setable
1806     record.body.discrete_reading_setting_mask[1] =
1807         record.body.discrete_reading_setting_mask[0];
1808     return true;
1809 }
1810 
1811 #ifdef FEATURE_HYBRID_SENSORS
1812 // Construct a type 1 SDR for discrete Sensor typed sensor.
1813 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1814                               uint16_t recordID,
1815                               ipmi::sensor::IdInfoMap::const_iterator sensor,
1816                               get_sdr::SensorDataFullRecord& record)
1817 {
1818     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1819 
1820     record.body.entity_id = sensor->second.entityType;
1821     record.body.sensor_type = sensor->second.sensorType;
1822     record.body.event_reading_type = sensor->second.sensorReadingType;
1823     record.body.entity_instance = sensor->second.instance;
1824     if (ipmi::sensor::Mutability::Write ==
1825         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1826     {
1827         get_sdr::body::init_settable_state(true, &(record.body));
1828     }
1829 
1830     auto id_string = sensor->second.sensorName;
1831 
1832     if (id_string.empty())
1833     {
1834         id_string = sensor->second.sensorNameFunc(sensor->second);
1835     }
1836 
1837     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1838     {
1839         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1840                                      &(record.body));
1841     }
1842     else
1843     {
1844         get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1845     }
1846     std::strncpy(record.body.id_string, id_string.c_str(),
1847                  get_sdr::body::get_id_strlen(&(record.body)));
1848 }
1849 #endif
1850 
1851 // Construct type 3 SDR header and key (for VR and other discrete sensors)
1852 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1853                                 get_sdr::SensorDataEventRecord& record)
1854 {
1855     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1856     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1857 
1858     get_sdr::header::set_record_id(
1859         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1860 
1861     record.header.sdr_version = ipmiSdrVersion;
1862     record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1863     record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1864                                   sizeof(get_sdr::SensorDataRecordHeader);
1865     record.key.owner_id = bmcI2CAddr;
1866     record.key.owner_lun = lun;
1867     record.key.sensor_number = sensornumber;
1868 
1869     record.body.entity_id = 0x00;
1870     record.body.entity_instance = 0x01;
1871 }
1872 
1873 // Construct a type 3 SDR for VR typed sensor(daemon).
1874 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1875                     uint16_t recordID, const std::string& service,
1876                     const std::string& path,
1877                     get_sdr::SensorDataEventRecord& record)
1878 {
1879     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1880     constructEventSdrHeaderKey(sensorNum, recordID, record);
1881 
1882     DbusInterfaceMap sensorMap;
1883     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1884     {
1885         phosphor::logging::log<phosphor::logging::level::ERR>(
1886             "Failed to update sensor map for VR sensor",
1887             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1888             phosphor::logging::entry("PATH=%s", path.c_str()));
1889         return false;
1890     }
1891     // follow the association chain to get the parent board's entityid and
1892     // entityInstance
1893     updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1894                               record.body.entity_instance);
1895 
1896     // Sensor type is hardcoded as a module/board type instead of parsing from
1897     // sensor path. This is because VR control is allocated in an independent
1898     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1899     // types.
1900     static constexpr const uint8_t module_board_type = 0x15;
1901     record.body.sensor_type = module_board_type;
1902     record.body.event_reading_type = 0x00;
1903 
1904     record.body.sensor_record_sharing_1 = 0x00;
1905     record.body.sensor_record_sharing_2 = 0x00;
1906 
1907     // populate sensor name from path
1908     auto name = sensor::parseSdrIdFromPath(path);
1909     int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1910     record.body.id_string_info = nameSize;
1911     std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1912     std::memcpy(record.body.id_string, name.c_str(), nameSize);
1913 
1914     // Remember the sensor name, as determined for this sensor number
1915     details::sdrStatsTable.updateName(sensornumber, name);
1916 
1917     return true;
1918 }
1919 
1920 static inline uint16_t getNumberOfSensors()
1921 {
1922     return std::min(getSensorTree().size(), maxIPMISensors);
1923 }
1924 
1925 static int
1926     getSensorDataRecord(ipmi::Context::ptr ctx,
1927                         std::vector<uint8_t>& recordData, uint16_t recordID,
1928                         uint8_t readBytes = std::numeric_limits<uint8_t>::max())
1929 {
1930     size_t fruCount = 0;
1931     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1932     if (ret != ipmi::ccSuccess)
1933     {
1934         phosphor::logging::log<phosphor::logging::level::ERR>(
1935             "getSensorDataRecord: getFruSdrCount error");
1936         return GENERAL_ERROR;
1937     }
1938 
1939     const auto& entityRecords =
1940         ipmi::sensor::EntityInfoMapContainer::getContainer()
1941             ->getIpmiEntityRecords();
1942     size_t entityCount = entityRecords.size();
1943 
1944     size_t lastRecord = getNumberOfSensors() + fruCount +
1945                         ipmi::storage::type12Count + entityCount - 1;
1946     if (recordID == lastRecordIndex)
1947     {
1948         recordID = lastRecord;
1949     }
1950     if (recordID > lastRecord)
1951     {
1952         phosphor::logging::log<phosphor::logging::level::ERR>(
1953             "getSensorDataRecord: recordID > lastRecord error");
1954         return GENERAL_ERROR;
1955     }
1956 
1957     if (recordID >= getNumberOfSensors())
1958     {
1959         size_t sdrIndex = recordID - getNumberOfSensors();
1960 
1961         if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1962         {
1963             // handle type 8 entity map records
1964             ipmi::sensor::EntityInfoMap::const_iterator entity =
1965                 entityRecords.find(static_cast<uint8_t>(
1966                     sdrIndex - fruCount - ipmi::storage::type12Count));
1967             if (entity == entityRecords.end())
1968             {
1969                 return IPMI_CC_SENSOR_INVALID;
1970             }
1971             recordData = ipmi::storage::getType8SDRs(entity, recordID);
1972         }
1973         else if (sdrIndex >= fruCount)
1974         {
1975             // handle type 12 hardcoded records
1976             size_t type12Index = sdrIndex - fruCount;
1977             if (type12Index >= ipmi::storage::type12Count)
1978             {
1979                 phosphor::logging::log<phosphor::logging::level::ERR>(
1980                     "getSensorDataRecord: type12Index error");
1981                 return GENERAL_ERROR;
1982             }
1983             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1984         }
1985         else
1986         {
1987             // handle fru records
1988             get_sdr::SensorDataFruRecord data;
1989             ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
1990             if (ret != IPMI_CC_OK)
1991             {
1992                 return GENERAL_ERROR;
1993             }
1994             data.header.record_id_msb = recordID >> 8;
1995             data.header.record_id_lsb = recordID & 0xFF;
1996             recordData.insert(recordData.end(), (uint8_t*)&data,
1997                               ((uint8_t*)&data) + sizeof(data));
1998         }
1999 
2000         return 0;
2001     }
2002 
2003     // Perform a incremental scan of the SDR Record ID's and translate the
2004     // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2005     // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2006     // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2007     // which has special meaning.
2008     std::string connection;
2009     std::string path;
2010     std::vector<std::string> interfaces;
2011     uint16_t sensNumFromRecID{recordID};
2012     if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
2013     {
2014         // LUN 0 has one reserved sensor number. Compensate here by adding one
2015         // to the record ID
2016         sensNumFromRecID = recordID + 1;
2017         ctx->lun = 1;
2018     }
2019     else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
2020     {
2021         // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2022         // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2023         // rules governing its use.
2024         sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
2025         ctx->lun = 3;
2026     }
2027 
2028     auto status =
2029         getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2030                             connection, path, &interfaces);
2031     if (status)
2032     {
2033         phosphor::logging::log<phosphor::logging::level::ERR>(
2034             "getSensorDataRecord: getSensorConnection error");
2035         return GENERAL_ERROR;
2036     }
2037     uint16_t sensorNum = getSensorNumberFromPath(path);
2038     // Return an error on LUN 2 assingments, and any sensor number beyond the
2039     // range of LUN 3
2040     if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2041         (sensorNum > lun3MaxSensorNum))
2042     {
2043         phosphor::logging::log<phosphor::logging::level::ERR>(
2044             "getSensorDataRecord: invalidSensorNumber");
2045         return GENERAL_ERROR;
2046     }
2047     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2048     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2049 
2050     if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2051         (lun != ctx->lun))
2052     {
2053         phosphor::logging::log<phosphor::logging::level::ERR>(
2054             "getSensorDataRecord: sensor record mismatch");
2055         return GENERAL_ERROR;
2056     }
2057 
2058     // Construct full record (SDR type 1) for the threshold sensors
2059     if (std::find(interfaces.begin(), interfaces.end(),
2060                   sensor::sensorInterface) != interfaces.end())
2061     {
2062         get_sdr::SensorDataFullRecord record = {0};
2063 
2064         // If the request doesn't read SDR body, construct only header and key
2065         // part to avoid additional DBus transaction.
2066         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2067         {
2068             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2069         }
2070         else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2071                                      record))
2072         {
2073             return GENERAL_ERROR;
2074         }
2075 
2076         recordData.insert(recordData.end(), (uint8_t*)&record,
2077                           ((uint8_t*)&record) + sizeof(record));
2078 
2079         return 0;
2080     }
2081 
2082 #ifdef FEATURE_HYBRID_SENSORS
2083     if (auto sensor = findStaticSensor(path);
2084         sensor != ipmi::sensor::sensors.end() &&
2085         getSensorEventTypeFromPath(path) !=
2086             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2087     {
2088         get_sdr::SensorDataFullRecord record = {0};
2089 
2090         // If the request doesn't read SDR body, construct only header and key
2091         // part to avoid additional DBus transaction.
2092         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2093         {
2094             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2095         }
2096         else
2097         {
2098             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2099         }
2100 
2101         recordData.insert(recordData.end(), (uint8_t*)&record,
2102                           ((uint8_t*)&record) + sizeof(record));
2103 
2104         return 0;
2105     }
2106 #endif
2107 
2108     // Contruct SDR type 3 record for VR sensor (daemon)
2109     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2110         interfaces.end())
2111     {
2112         get_sdr::SensorDataEventRecord record = {0};
2113 
2114         // If the request doesn't read SDR body, construct only header and key
2115         // part to avoid additional DBus transaction.
2116         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2117         {
2118             constructEventSdrHeaderKey(sensorNum, recordID, record);
2119         }
2120         else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2121                                  record))
2122         {
2123             return GENERAL_ERROR;
2124         }
2125         recordData.insert(recordData.end(), (uint8_t*)&record,
2126                           ((uint8_t*)&record) + sizeof(record));
2127     }
2128 
2129     return 0;
2130 }
2131 
2132 /** @brief implements the get SDR Info command
2133  *  @param count - Operation
2134  *
2135  *  @returns IPMI completion code plus response data
2136  *   - sdrCount - sensor/SDR count
2137  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2138  */
2139 static ipmi::RspType<uint8_t, // respcount
2140                      uint8_t, // dynamic population flags
2141                      uint32_t // last time a sensor was added
2142                      >
2143     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2144                                std::optional<uint8_t> count)
2145 {
2146     auto& sensorTree = getSensorTree();
2147     uint8_t sdrCount = 0;
2148     uint16_t recordID = 0;
2149     std::vector<uint8_t> record;
2150     // Sensors are dynamically allocated, and there is at least one LUN
2151     uint8_t lunsAndDynamicPopulation = 0x80;
2152     constexpr uint8_t getSdrCount = 0x01;
2153     constexpr uint8_t getSensorCount = 0x00;
2154 
2155     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2156     {
2157         return ipmi::responseResponseError();
2158     }
2159     uint16_t numSensors = getNumberOfSensors();
2160     if (count.value_or(0) == getSdrCount)
2161     {
2162         // Count the number of Type 1 SDR entries assigned to the LUN
2163         while (!getSensorDataRecord(ctx, record, recordID++))
2164         {
2165             get_sdr::SensorDataRecordHeader* hdr =
2166                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
2167                     record.data());
2168             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2169             {
2170                 get_sdr::SensorDataFullRecord* recordData =
2171                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
2172                         record.data());
2173                 if (ctx->lun == recordData->key.owner_lun)
2174                 {
2175                     sdrCount++;
2176                 }
2177             }
2178         }
2179     }
2180     else if (count.value_or(0) == getSensorCount)
2181     {
2182         // Return the number of sensors attached to the LUN
2183         if ((ctx->lun == 0) && (numSensors > 0))
2184         {
2185             sdrCount =
2186                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2187         }
2188         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2189         {
2190             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2191                            ? maxSensorsPerLUN
2192                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2193         }
2194         else if (ctx->lun == 3)
2195         {
2196             if (numSensors <= maxIPMISensors)
2197             {
2198                 sdrCount =
2199                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2200             }
2201             else
2202             {
2203                 // error
2204                 throw std::out_of_range(
2205                     "Maximum number of IPMI sensors exceeded.");
2206             }
2207         }
2208     }
2209     else
2210     {
2211         return ipmi::responseInvalidFieldRequest();
2212     }
2213 
2214     // Get Sensor count. This returns the number of sensors
2215     if (numSensors > 0)
2216     {
2217         lunsAndDynamicPopulation |= 1;
2218     }
2219     if (numSensors > maxSensorsPerLUN)
2220     {
2221         lunsAndDynamicPopulation |= 2;
2222     }
2223     if (numSensors >= (maxSensorsPerLUN * 2))
2224     {
2225         lunsAndDynamicPopulation |= 8;
2226     }
2227     if (numSensors > maxIPMISensors)
2228     {
2229         // error
2230         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2231     }
2232 
2233     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2234                                  sdrLastAdd);
2235 }
2236 
2237 /* end sensor commands */
2238 
2239 /* storage commands */
2240 
2241 ipmi::RspType<uint8_t,  // sdr version
2242               uint16_t, // record count
2243               uint16_t, // free space
2244               uint32_t, // most recent addition
2245               uint32_t, // most recent erase
2246               uint8_t   // operationSupport
2247               >
2248     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2249 {
2250     auto& sensorTree = getSensorTree();
2251     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2252     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2253     {
2254         return ipmi::responseResponseError();
2255     }
2256 
2257     size_t fruCount = 0;
2258     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2259     if (ret != ipmi::ccSuccess)
2260     {
2261         return ipmi::response(ret);
2262     }
2263 
2264     uint16_t recordCount =
2265         getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
2266 
2267     uint8_t operationSupport = static_cast<uint8_t>(
2268         SdrRepositoryInfoOps::overflow); // write not supported
2269 
2270     operationSupport |=
2271         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2272     operationSupport |= static_cast<uint8_t>(
2273         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2274     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2275                                  unspecifiedFreeSpace, sdrLastAdd,
2276                                  sdrLastRemove, operationSupport);
2277 }
2278 
2279 /** @brief implements the get SDR allocation info command
2280  *
2281  *  @returns IPMI completion code plus response data
2282  *   - allocUnits    - Number of possible allocation units
2283  *   - allocUnitSize - Allocation unit size in bytes.
2284  *   - allocUnitFree - Number of free allocation units
2285  *   - allocUnitLargestFree - Largest free block in allocation units
2286  *   - maxRecordSize    - Maximum record size in allocation units.
2287  */
2288 ipmi::RspType<uint16_t, // allocUnits
2289               uint16_t, // allocUnitSize
2290               uint16_t, // allocUnitFree
2291               uint16_t, // allocUnitLargestFree
2292               uint8_t   // maxRecordSize
2293               >
2294     ipmiStorageGetSDRAllocationInfo()
2295 {
2296     // 0000h unspecified number of alloc units
2297     constexpr uint16_t allocUnits = 0;
2298 
2299     constexpr uint16_t allocUnitFree = 0;
2300     constexpr uint16_t allocUnitLargestFree = 0;
2301     // only allow one block at a time
2302     constexpr uint8_t maxRecordSize = 1;
2303 
2304     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2305                                  allocUnitLargestFree, maxRecordSize);
2306 }
2307 
2308 /** @brief implements the reserve SDR command
2309  *  @returns IPMI completion code plus response data
2310  *   - sdrReservationID
2311  */
2312 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2313 {
2314     sdrReservationID++;
2315     if (sdrReservationID == 0)
2316     {
2317         sdrReservationID++;
2318     }
2319 
2320     return ipmi::responseSuccess(sdrReservationID);
2321 }
2322 
2323 ipmi::RspType<uint16_t,            // next record ID
2324               std::vector<uint8_t> // payload
2325               >
2326     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2327                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2328 {
2329     size_t fruCount = 0;
2330     // reservation required for partial reads with non zero offset into
2331     // record
2332     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2333     {
2334         phosphor::logging::log<phosphor::logging::level::ERR>(
2335             "ipmiStorageGetSDR: responseInvalidReservationId");
2336         return ipmi::responseInvalidReservationId();
2337     }
2338     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2339     if (ret != ipmi::ccSuccess)
2340     {
2341         phosphor::logging::log<phosphor::logging::level::ERR>(
2342             "ipmiStorageGetSDR: getFruSdrCount error");
2343         return ipmi::response(ret);
2344     }
2345 
2346     const auto& entityRecords =
2347         ipmi::sensor::EntityInfoMapContainer::getContainer()
2348             ->getIpmiEntityRecords();
2349     int entityCount = entityRecords.size();
2350 
2351     auto& sensorTree = getSensorTree();
2352     size_t lastRecord = getNumberOfSensors() + fruCount +
2353                         ipmi::storage::type12Count + entityCount - 1;
2354     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2355 
2356     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2357     {
2358         phosphor::logging::log<phosphor::logging::level::ERR>(
2359             "ipmiStorageGetSDR: getSensorSubtree error");
2360         return ipmi::responseResponseError();
2361     }
2362 
2363     std::vector<uint8_t> record;
2364     if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
2365     {
2366         phosphor::logging::log<phosphor::logging::level::ERR>(
2367             "ipmiStorageGetSDR: fail to get SDR");
2368         return ipmi::responseInvalidFieldRequest();
2369     }
2370     get_sdr::SensorDataRecordHeader* hdr =
2371         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2372     if (!hdr)
2373     {
2374         phosphor::logging::log<phosphor::logging::level::ERR>(
2375             "ipmiStorageGetSDR: record header is null");
2376         return ipmi::responseSuccess(nextRecordId, record);
2377     }
2378 
2379     size_t sdrLength =
2380         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2381     if (sdrLength < (offset + bytesToRead))
2382     {
2383         bytesToRead = sdrLength - offset;
2384     }
2385 
2386     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2387     if (!respStart)
2388     {
2389         phosphor::logging::log<phosphor::logging::level::ERR>(
2390             "ipmiStorageGetSDR: record is null");
2391         return ipmi::responseSuccess(nextRecordId, record);
2392     }
2393 
2394     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2395 
2396     return ipmi::responseSuccess(nextRecordId, recordData);
2397 }
2398 /* end storage commands */
2399 
2400 void registerSensorFunctions()
2401 {
2402     // <Platform Event>
2403     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2404                           ipmi::sensor_event::cmdPlatformEvent,
2405                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2406 
2407     // <Set Sensor Reading and Event Status>
2408     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2409                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2410                           ipmi::Privilege::Operator, ipmiSetSensorReading);
2411 
2412     // <Get Sensor Reading>
2413     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2414                           ipmi::sensor_event::cmdGetSensorReading,
2415                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2416 
2417     // <Get Sensor Threshold>
2418     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2419                           ipmi::sensor_event::cmdGetSensorThreshold,
2420                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2421 
2422     // <Set Sensor Threshold>
2423     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2424                           ipmi::sensor_event::cmdSetSensorThreshold,
2425                           ipmi::Privilege::Operator,
2426                           ipmiSenSetSensorThresholds);
2427 
2428     // <Get Sensor Event Enable>
2429     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2430                           ipmi::sensor_event::cmdGetSensorEventEnable,
2431                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2432 
2433     // <Get Sensor Event Status>
2434     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2435                           ipmi::sensor_event::cmdGetSensorEventStatus,
2436                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2437 
2438     // register all storage commands for both Sensor and Storage command
2439     // versions
2440 
2441     // <Get SDR Repository Info>
2442     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2443                           ipmi::storage::cmdGetSdrRepositoryInfo,
2444                           ipmi::Privilege::User,
2445                           ipmiStorageGetSDRRepositoryInfo);
2446 
2447     // <Get Device SDR Info>
2448     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2449                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2450                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2451 
2452     // <Get SDR Allocation Info>
2453     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2454                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2455                           ipmi::Privilege::User,
2456                           ipmiStorageGetSDRAllocationInfo);
2457 
2458     // <Reserve SDR Repo>
2459     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2460                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2461                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2462 
2463     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2464                           ipmi::storage::cmdReserveSdrRepository,
2465                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2466 
2467     // <Get Sdr>
2468     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2469                           ipmi::sensor_event::cmdGetDeviceSdr,
2470                           ipmi::Privilege::User, ipmiStorageGetSDR);
2471 
2472     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2473                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2474                           ipmiStorageGetSDR);
2475 }
2476 } // namespace ipmi
2477