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