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