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