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