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