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