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