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