xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sensorcommands.cpp (revision d950f410b71e0ac4650519eb5bf7ea683e98684d)
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 "dbus-sdr/sensorcommands.hpp"
18 
19 #include "dbus-sdr/sdrutils.hpp"
20 #include "dbus-sdr/sensorutils.hpp"
21 #include "dbus-sdr/storagecommands.hpp"
22 
23 #include <algorithm>
24 #include <array>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/container/flat_map.hpp>
27 #include <chrono>
28 #include <cmath>
29 #include <cstring>
30 #include <iostream>
31 #include <ipmid/api.hpp>
32 #include <ipmid/types.hpp>
33 #include <ipmid/utils.hpp>
34 #include <map>
35 #include <memory>
36 #include <optional>
37 #include <phosphor-logging/log.hpp>
38 #include <sdbusplus/bus.hpp>
39 #include <stdexcept>
40 #include <string>
41 #include <utility>
42 #include <variant>
43 
44 #ifdef FEATURE_HYBRID_SENSORS
45 
46 #include "sensordatahandler.hpp"
47 namespace ipmi
48 {
49 namespace sensor
50 {
51 extern const IdInfoMap sensors;
52 } // namespace sensor
53 } // namespace ipmi
54 #endif
55 
56 constexpr std::array<const char*, 7> suffixes = {
57     "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
58     "_Output_Power",   "_Input_Power",   "_Temperature"};
59 namespace ipmi
60 {
61 
62 using phosphor::logging::entry;
63 using phosphor::logging::level;
64 using phosphor::logging::log;
65 
66 static constexpr int sensorMapUpdatePeriod = 10;
67 static constexpr int sensorMapSdrUpdatePeriod = 60;
68 
69 // BMC I2C address is generally at 0x20
70 static constexpr uint8_t bmcI2CAddr = 0x20;
71 
72 constexpr size_t maxSDRTotalSize =
73     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
74 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
75 
76 static uint16_t sdrReservationID;
77 static uint32_t sdrLastAdd = noTimestamp;
78 static uint32_t sdrLastRemove = noTimestamp;
79 static constexpr size_t lastRecordIndex = 0xFFFF;
80 static constexpr int GENERAL_ERROR = -1;
81 
82 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
83 
84 // Specify the comparison required to sort and find char* map objects
85 struct CmpStr
86 {
87     bool operator()(const char* a, const char* b) const
88     {
89         return std::strcmp(a, b) < 0;
90     }
91 };
92 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
93     sensorUnits{{{"temperature", SensorUnits::degreesC},
94                  {"voltage", SensorUnits::volts},
95                  {"current", SensorUnits::amps},
96                  {"fan_tach", SensorUnits::rpm},
97                  {"power", SensorUnits::watts}}};
98 
99 void registerSensorFunctions() __attribute__((constructor));
100 
101 static sdbusplus::bus::match::match sensorAdded(
102     *getSdBus(),
103     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
104     "sensors/'",
105     [](sdbusplus::message::message& m) {
106         getSensorTree().clear();
107         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
108                          std::chrono::system_clock::now().time_since_epoch())
109                          .count();
110     });
111 
112 static sdbusplus::bus::match::match sensorRemoved(
113     *getSdBus(),
114     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
115     "sensors/'",
116     [](sdbusplus::message::message& m) {
117         getSensorTree().clear();
118         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
119                             std::chrono::system_clock::now().time_since_epoch())
120                             .count();
121     });
122 
123 // this keeps track of deassertions for sensor event status command. A
124 // deasertion can only happen if an assertion was seen first.
125 static boost::container::flat_map<
126     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
127     thresholdDeassertMap;
128 
129 static sdbusplus::bus::match::match thresholdChanged(
130     *getSdBus(),
131     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
132     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
133     [](sdbusplus::message::message& m) {
134         boost::container::flat_map<std::string, std::variant<bool, double>>
135             values;
136         m.read(std::string(), values);
137 
138         auto findAssert =
139             std::find_if(values.begin(), values.end(), [](const auto& pair) {
140                 return pair.first.find("Alarm") != std::string::npos;
141             });
142         if (findAssert != values.end())
143         {
144             auto ptr = std::get_if<bool>(&(findAssert->second));
145             if (ptr == nullptr)
146             {
147                 phosphor::logging::log<phosphor::logging::level::ERR>(
148                     "thresholdChanged: Assert non bool");
149                 return;
150             }
151             if (*ptr)
152             {
153                 phosphor::logging::log<phosphor::logging::level::INFO>(
154                     "thresholdChanged: Assert",
155                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
156                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
157             }
158             else
159             {
160                 auto& value =
161                     thresholdDeassertMap[m.get_path()][findAssert->first];
162                 if (value)
163                 {
164                     phosphor::logging::log<phosphor::logging::level::INFO>(
165                         "thresholdChanged: deassert",
166                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
167                     value = *ptr;
168                 }
169             }
170         }
171     });
172 
173 namespace sensor
174 {
175 static constexpr const char* vrInterface =
176     "xyz.openbmc_project.Control.VoltageRegulatorMode";
177 static constexpr const char* sensorInterface =
178     "xyz.openbmc_project.Sensor.Value";
179 } // namespace sensor
180 
181 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
182                             double& min)
183 {
184     max = 127;
185     min = -128;
186 
187     auto sensorObject = sensorMap.find(sensor::sensorInterface);
188     auto critical =
189         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
190     auto warning =
191         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
192 
193     if (sensorObject != sensorMap.end())
194     {
195         auto maxMap = sensorObject->second.find("MaxValue");
196         auto minMap = sensorObject->second.find("MinValue");
197 
198         if (maxMap != sensorObject->second.end())
199         {
200             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
201         }
202         if (minMap != sensorObject->second.end())
203         {
204             min = std::visit(VariantToDoubleVisitor(), minMap->second);
205         }
206     }
207     if (critical != sensorMap.end())
208     {
209         auto lower = critical->second.find("CriticalLow");
210         auto upper = critical->second.find("CriticalHigh");
211         if (lower != critical->second.end())
212         {
213             double value = std::visit(VariantToDoubleVisitor(), lower->second);
214             min = std::min(value, min);
215         }
216         if (upper != critical->second.end())
217         {
218             double value = std::visit(VariantToDoubleVisitor(), upper->second);
219             max = std::max(value, max);
220         }
221     }
222     if (warning != sensorMap.end())
223     {
224 
225         auto lower = warning->second.find("WarningLow");
226         auto upper = warning->second.find("WarningHigh");
227         if (lower != warning->second.end())
228         {
229             double value = std::visit(VariantToDoubleVisitor(), lower->second);
230             min = std::min(value, min);
231         }
232         if (upper != warning->second.end())
233         {
234             double value = std::visit(VariantToDoubleVisitor(), upper->second);
235             max = std::max(value, max);
236         }
237     }
238 }
239 
240 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
241                          std::string sensorPath, DbusInterfaceMap& sensorMap,
242                          int updatePeriod = sensorMapUpdatePeriod)
243 {
244 #ifdef FEATURE_HYBRID_SENSORS
245     if (auto sensor = findStaticSensor(sensorPath);
246         sensor != ipmi::sensor::sensors.end() &&
247         getSensorEventTypeFromPath(sensorPath) !=
248             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
249     {
250         // If the incoming sensor is a discrete sensor, it might fail in
251         // getManagedObjects(), return true, and use its own getFunc to get
252         // value.
253         return true;
254     }
255 #endif
256 
257     static boost::container::flat_map<
258         std::string, std::chrono::time_point<std::chrono::steady_clock>>
259         updateTimeMap;
260 
261     auto updateFind = updateTimeMap.find(sensorConnection);
262     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
263     if (updateFind != updateTimeMap.end())
264     {
265         lastUpdate = updateFind->second;
266     }
267 
268     auto now = std::chrono::steady_clock::now();
269 
270     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
271             .count() > updatePeriod)
272     {
273         ObjectValueTree managedObjects;
274         boost::system::error_code ec = getManagedObjects(
275             ctx, sensorConnection.c_str(), "/", managedObjects);
276         if (ec)
277         {
278             phosphor::logging::log<phosphor::logging::level::ERR>(
279                 "GetMangagedObjects for getSensorMap failed",
280                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
281 
282             return false;
283         }
284 
285         SensorCache[sensorConnection] = managedObjects;
286         // Update time after finish building the map which allow the
287         // data to be cached for updatePeriod plus the build time.
288         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
289     }
290     auto connection = SensorCache.find(sensorConnection);
291     if (connection == SensorCache.end())
292     {
293         return false;
294     }
295     auto path = connection->second.find(sensorPath);
296     if (path == connection->second.end())
297     {
298         return false;
299     }
300     sensorMap = path->second;
301 
302     return true;
303 }
304 
305 namespace sensor
306 {
307 // Read VR profiles from sensor(daemon) interface
308 static std::optional<std::vector<std::string>>
309     getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
310 {
311     // get VR mode profiles from Supported Interface
312     auto supportedProperty = object.find("Supported");
313     if (supportedProperty == object.end() ||
314         object.find("Selected") == object.end())
315     {
316         phosphor::logging::log<phosphor::logging::level::ERR>(
317             "Missing the required Supported and Selected properties");
318         return std::nullopt;
319     }
320 
321     const auto profilesPtr =
322         std::get_if<std::vector<std::string>>(&supportedProperty->second);
323 
324     if (profilesPtr == nullptr)
325     {
326         phosphor::logging::log<phosphor::logging::level::ERR>(
327             "property is not array of string");
328         return std::nullopt;
329     }
330     return *profilesPtr;
331 }
332 
333 // Calculate VR Mode from input IPMI discrete event bytes
334 static std::optional<std::string>
335     calculateVRMode(uint15_t assertOffset,
336                     const ipmi::DbusInterfaceMap::mapped_type& VRObject)
337 {
338     // get VR mode profiles from Supported Interface
339     auto profiles = getSupportedVrProfiles(VRObject);
340     if (!profiles)
341     {
342         return std::nullopt;
343     }
344 
345     // interpret IPMI cmd bits into profiles' index
346     long unsigned int index = 0;
347     // only one bit should be set and the highest bit should not be used.
348     if (assertOffset == 0 || assertOffset == (1u << 15) ||
349         (assertOffset & (assertOffset - 1)))
350     {
351         phosphor::logging::log<phosphor::logging::level::ERR>(
352             "IPMI cmd format incorrect",
353 
354             phosphor::logging::entry("BYTES=%#02x",
355                                      static_cast<uint16_t>(assertOffset)));
356         return std::nullopt;
357     }
358 
359     while (assertOffset != 1)
360     {
361         assertOffset >>= 1;
362         index++;
363     }
364 
365     if (index >= profiles->size())
366     {
367         phosphor::logging::log<phosphor::logging::level::ERR>(
368             "profile index out of boundary");
369         return std::nullopt;
370     }
371 
372     return profiles->at(index);
373 }
374 
375 // Calculate sensor value from IPMI reading byte
376 static std::optional<double>
377     calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
378                    const ipmi::DbusInterfaceMap::mapped_type& valueObject)
379 {
380     if (valueObject.find("Value") == valueObject.end())
381     {
382         phosphor::logging::log<phosphor::logging::level::ERR>(
383             "Missing the required Value property");
384         return std::nullopt;
385     }
386 
387     double max = 0;
388     double min = 0;
389     getSensorMaxMin(sensorMap, max, min);
390 
391     int16_t mValue = 0;
392     int16_t bValue = 0;
393     int8_t rExp = 0;
394     int8_t bExp = 0;
395     bool bSigned = false;
396 
397     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
398     {
399         return std::nullopt;
400     }
401 
402     double value = bSigned ? ((int8_t)reading) : reading;
403 
404     value *= ((double)mValue);
405     value += ((double)bValue) * std::pow(10.0, bExp);
406     value *= std::pow(10.0, rExp);
407 
408     return value;
409 }
410 
411 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
412 // if it is too long.
413 std::string parseSdrIdFromPath(const std::string& path)
414 {
415     std::string name;
416     size_t nameStart = path.rfind("/");
417     if (nameStart != std::string::npos)
418     {
419         name = path.substr(nameStart + 1, std::string::npos - nameStart);
420     }
421 
422     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
423     {
424         // try to not truncate by replacing common words
425         for (const auto& suffix : suffixes)
426         {
427             if (boost::ends_with(name, suffix))
428             {
429                 boost::replace_all(name, suffix, "");
430                 break;
431             }
432         }
433         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
434     }
435     std::replace(name.begin(), name.end(), '_', ' ');
436     return name;
437 }
438 
439 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
440                       const std::string& path,
441                       const ipmi::DbusInterfaceMap::mapped_type& object,
442                       std::bitset<16>& assertions)
443 {
444     auto profiles = sensor::getSupportedVrProfiles(object);
445     if (!profiles)
446     {
447         return false;
448     }
449     ipmi::Value modeVariant;
450 
451     auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
452                               "Selected", modeVariant);
453     if (ec)
454     {
455         log<level::ERR>("Failed to get property",
456                         entry("PROPERTY=%s", "Selected"),
457                         entry("PATH=%s", path.c_str()),
458                         entry("INTERFACE=%s", sensor::sensorInterface),
459                         entry("WHAT=%s", ec.message().c_str()));
460         return false;
461     }
462 
463     auto mode = std::get_if<std::string>(&modeVariant);
464     if (mode == nullptr)
465     {
466         log<level::ERR>("property is not a string",
467                         entry("PROPERTY=%s", "Selected"),
468                         entry("PATH=%s", path.c_str()),
469                         entry("INTERFACE=%s", sensor::sensorInterface));
470         return false;
471     }
472 
473     auto itr = std::find(profiles->begin(), profiles->end(), *mode);
474     if (itr == profiles->end())
475     {
476         using namespace phosphor::logging;
477         log<level::ERR>("VR mode doesn't match any of its profiles",
478                         entry("PATH=%s", path.c_str()));
479         return false;
480     }
481     std::size_t index =
482         static_cast<std::size_t>(std::distance(profiles->begin(), itr));
483 
484     // map index to reponse event assertion bit.
485     if (index < 8)
486     {
487         assertions.set(1u << index);
488     }
489     else if (index < 15)
490     {
491         assertions.set(1u << (index - 8));
492     }
493     else
494     {
495         log<level::ERR>("VR profile index reaches max assertion bit",
496                         entry("PATH=%s", path.c_str()),
497                         entry("INDEX=%uz", index));
498         return false;
499     }
500     if constexpr (debug)
501     {
502         std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
503                   << " mode is: [" << index << "] " << *mode << std::endl;
504     }
505     return true;
506 }
507 } // namespace sensor
508 
509 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
510                                      uint8_t sensorType, uint8_t sensorNum,
511                                      uint8_t eventType, uint8_t eventData1,
512                                      std::optional<uint8_t> eventData2,
513                                      std::optional<uint8_t> eventData3)
514 {
515     return ipmi::responseSuccess();
516 }
517 
518 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
519                                      uint8_t sensorNumber, uint8_t operation,
520                                      uint8_t reading, uint15_t assertOffset,
521                                      bool resvd1, uint15_t deassertOffset,
522                                      bool resvd2, uint8_t eventData1,
523                                      uint8_t eventData2, uint8_t eventData3)
524 {
525     std::string connection;
526     std::string path;
527     std::vector<std::string> interfaces;
528 
529     ipmi::Cc status =
530         getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
531     if (status)
532     {
533         return ipmi::response(status);
534     }
535 
536     // we can tell the sensor type by its interface type
537     if (std::find(interfaces.begin(), interfaces.end(),
538                   sensor::sensorInterface) != interfaces.end())
539     {
540         DbusInterfaceMap sensorMap;
541         if (!getSensorMap(ctx, connection, path, sensorMap))
542         {
543             return ipmi::responseResponseError();
544         }
545         auto sensorObject = sensorMap.find(sensor::sensorInterface);
546         if (sensorObject == sensorMap.end())
547         {
548             return ipmi::responseResponseError();
549         }
550 
551         // Only allow external SetSensor if write permission granted
552         if (!details::sdrWriteTable.getWritePermission(sensorNumber))
553         {
554             return ipmi::responseResponseError();
555         }
556 
557         auto value =
558             sensor::calculateValue(reading, sensorMap, sensorObject->second);
559         if (!value)
560         {
561             return ipmi::responseResponseError();
562         }
563 
564         if constexpr (debug)
565         {
566             phosphor::logging::log<phosphor::logging::level::INFO>(
567                 "IPMI SET_SENSOR",
568                 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
569                 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
570                 phosphor::logging::entry("VALUE=%f", *value));
571         }
572 
573         boost::system::error_code ec =
574             setDbusProperty(ctx, connection, path, sensor::sensorInterface,
575                             "Value", ipmi::Value(*value));
576 
577         // setDbusProperty intended to resolve dbus exception/rc within the
578         // function but failed to achieve that. Catch exception in the ipmi
579         // callback functions for now (e.g. ipmiSetSensorReading).
580         if (ec)
581         {
582             using namespace phosphor::logging;
583             log<level::ERR>("Failed to set property",
584                             entry("PROPERTY=%s", "Value"),
585                             entry("PATH=%s", path.c_str()),
586                             entry("INTERFACE=%s", sensor::sensorInterface),
587                             entry("WHAT=%s", ec.message().c_str()));
588             return ipmi::responseResponseError();
589         }
590         return ipmi::responseSuccess();
591     }
592 
593     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
594         interfaces.end())
595     {
596         DbusInterfaceMap sensorMap;
597         if (!getSensorMap(ctx, connection, path, sensorMap))
598         {
599             return ipmi::responseResponseError();
600         }
601         auto sensorObject = sensorMap.find(sensor::vrInterface);
602         if (sensorObject == sensorMap.end())
603         {
604             return ipmi::responseResponseError();
605         }
606 
607         // VR sensors are treated as a special case and we will not check the
608         // write permission for VR sensors, since they always deemed writable
609         // and permission table are not applied to VR sensors.
610         auto vrMode =
611             sensor::calculateVRMode(assertOffset, sensorObject->second);
612         if (!vrMode)
613         {
614             return ipmi::responseResponseError();
615         }
616         boost::system::error_code ec = setDbusProperty(
617             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
618         // setDbusProperty intended to resolve dbus exception/rc within the
619         // function but failed to achieve that. Catch exception in the ipmi
620         // callback functions for now (e.g. ipmiSetSensorReading).
621         if (ec)
622         {
623             using namespace phosphor::logging;
624             log<level::ERR>("Failed to set property",
625                             entry("PROPERTY=%s", "Selected"),
626                             entry("PATH=%s", path.c_str()),
627                             entry("INTERFACE=%s", sensor::sensorInterface),
628                             entry("WHAT=%s", ec.message().c_str()));
629             return ipmi::responseResponseError();
630         }
631         return ipmi::responseSuccess();
632     }
633 
634     phosphor::logging::log<phosphor::logging::level::ERR>(
635         "unknown sensor type",
636         phosphor::logging::entry("PATH=%s", path.c_str()));
637     return ipmi::responseResponseError();
638 }
639 
640 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
641     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
642 {
643     std::string connection;
644     std::string path;
645 
646     auto status = getSensorConnection(ctx, sensnum, connection, path);
647     if (status)
648     {
649         return ipmi::response(status);
650     }
651 
652 #ifdef FEATURE_HYBRID_SENSORS
653     if (auto sensor = findStaticSensor(path);
654         sensor != ipmi::sensor::sensors.end() &&
655         getSensorEventTypeFromPath(path) !=
656             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
657     {
658         if (ipmi::sensor::Mutability::Read !=
659             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
660         {
661             return ipmi::responseIllegalCommand();
662         }
663 
664         uint8_t operation;
665         try
666         {
667             ipmi::sensor::GetSensorResponse getResponse =
668                 sensor->second.getFunc(sensor->second);
669 
670             if (getResponse.readingOrStateUnavailable)
671             {
672                 operation |= static_cast<uint8_t>(
673                     IPMISensorReadingByte2::readingStateUnavailable);
674             }
675             if (getResponse.scanningEnabled)
676             {
677                 operation |= static_cast<uint8_t>(
678                     IPMISensorReadingByte2::sensorScanningEnable);
679             }
680             if (getResponse.allEventMessagesEnabled)
681             {
682                 operation |= static_cast<uint8_t>(
683                     IPMISensorReadingByte2::eventMessagesEnable);
684             }
685             return ipmi::responseSuccess(
686                 getResponse.reading, operation,
687                 getResponse.thresholdLevelsStates,
688                 getResponse.discreteReadingSensorStates);
689         }
690         catch (const std::exception& e)
691         {
692             operation |= static_cast<uint8_t>(
693                 IPMISensorReadingByte2::readingStateUnavailable);
694             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
695         }
696     }
697 #endif
698 
699     DbusInterfaceMap sensorMap;
700     if (!getSensorMap(ctx, connection, path, sensorMap))
701     {
702         return ipmi::responseResponseError();
703     }
704     auto sensorObject = sensorMap.find(sensor::sensorInterface);
705 
706     if (sensorObject == sensorMap.end() ||
707         sensorObject->second.find("Value") == sensorObject->second.end())
708     {
709         return ipmi::responseResponseError();
710     }
711     auto& valueVariant = sensorObject->second["Value"];
712     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
713 
714     double max = 0;
715     double min = 0;
716     getSensorMaxMin(sensorMap, max, min);
717 
718     int16_t mValue = 0;
719     int16_t bValue = 0;
720     int8_t rExp = 0;
721     int8_t bExp = 0;
722     bool bSigned = false;
723 
724     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
725     {
726         return ipmi::responseResponseError();
727     }
728 
729     uint8_t value =
730         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
731     uint8_t operation =
732         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
733     operation |=
734         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
735     bool notReading = std::isnan(reading);
736 
737     if (!notReading)
738     {
739         auto availableObject =
740             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
741         if (availableObject != sensorMap.end())
742         {
743             auto findAvailable = availableObject->second.find("Available");
744             if (findAvailable != availableObject->second.end())
745             {
746                 bool* available = std::get_if<bool>(&(findAvailable->second));
747                 if (available && !(*available))
748                 {
749                     notReading = true;
750                 }
751             }
752         }
753     }
754 
755     if (notReading)
756     {
757         operation |= static_cast<uint8_t>(
758             IPMISensorReadingByte2::readingStateUnavailable);
759     }
760 
761     if constexpr (details::enableInstrumentation)
762     {
763         int byteValue;
764         if (bSigned)
765         {
766             byteValue = static_cast<int>(static_cast<int8_t>(value));
767         }
768         else
769         {
770             byteValue = static_cast<int>(static_cast<uint8_t>(value));
771         }
772 
773         // Keep stats on the reading just obtained, even if it is "NaN"
774         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
775         {
776             // This is the first reading, show the coefficients
777             double step = (max - min) / 255.0;
778             std::cerr << "IPMI sensor "
779                       << details::sdrStatsTable.getName(sensnum)
780                       << ": Range min=" << min << " max=" << max
781                       << ", step=" << step
782                       << ", Coefficients mValue=" << static_cast<int>(mValue)
783                       << " rExp=" << static_cast<int>(rExp)
784                       << " bValue=" << static_cast<int>(bValue)
785                       << " bExp=" << static_cast<int>(bExp)
786                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
787         }
788     }
789 
790     uint8_t thresholds = 0;
791 
792     auto warningObject =
793         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
794     if (warningObject != sensorMap.end())
795     {
796         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
797         auto alarmLow = warningObject->second.find("WarningAlarmLow");
798         if (alarmHigh != warningObject->second.end())
799         {
800             if (std::get<bool>(alarmHigh->second))
801             {
802                 thresholds |= static_cast<uint8_t>(
803                     IPMISensorReadingByte3::upperNonCritical);
804             }
805         }
806         if (alarmLow != warningObject->second.end())
807         {
808             if (std::get<bool>(alarmLow->second))
809             {
810                 thresholds |= static_cast<uint8_t>(
811                     IPMISensorReadingByte3::lowerNonCritical);
812             }
813         }
814     }
815 
816     auto criticalObject =
817         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
818     if (criticalObject != sensorMap.end())
819     {
820         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
821         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
822         if (alarmHigh != criticalObject->second.end())
823         {
824             if (std::get<bool>(alarmHigh->second))
825             {
826                 thresholds |=
827                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
828             }
829         }
830         if (alarmLow != criticalObject->second.end())
831         {
832             if (std::get<bool>(alarmLow->second))
833             {
834                 thresholds |=
835                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
836             }
837         }
838     }
839 
840     // no discrete as of today so optional byte is never returned
841     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
842 }
843 
844 /** @brief implements the Set Sensor threshold command
845  *  @param sensorNumber        - sensor number
846  *  @param lowerNonCriticalThreshMask
847  *  @param lowerCriticalThreshMask
848  *  @param lowerNonRecovThreshMask
849  *  @param upperNonCriticalThreshMask
850  *  @param upperCriticalThreshMask
851  *  @param upperNonRecovThreshMask
852  *  @param reserved
853  *  @param lowerNonCritical    - lower non-critical threshold
854  *  @param lowerCritical       - Lower critical threshold
855  *  @param lowerNonRecoverable - Lower non recovarable threshold
856  *  @param upperNonCritical    - Upper non-critical threshold
857  *  @param upperCritical       - Upper critical
858  *  @param upperNonRecoverable - Upper Non-recoverable
859  *
860  *  @returns IPMI completion code
861  */
862 ipmi::RspType<> ipmiSenSetSensorThresholds(
863     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
864     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
865     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
866     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
867     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
868     uint8_t upperNonCritical, uint8_t upperCritical,
869     uint8_t upperNonRecoverable)
870 {
871     if (reserved)
872     {
873         return ipmi::responseInvalidFieldRequest();
874     }
875 
876     // lower nc and upper nc not suppported on any sensor
877     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
878     {
879         return ipmi::responseInvalidFieldRequest();
880     }
881 
882     // if none of the threshold mask are set, nothing to do
883     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
884           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
885           upperCriticalThreshMask | upperNonRecovThreshMask))
886     {
887         return ipmi::responseSuccess();
888     }
889 
890     std::string connection;
891     std::string path;
892 
893     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
894     if (status)
895     {
896         return ipmi::response(status);
897     }
898     DbusInterfaceMap sensorMap;
899     if (!getSensorMap(ctx, connection, path, sensorMap))
900     {
901         return ipmi::responseResponseError();
902     }
903 
904     double max = 0;
905     double min = 0;
906     getSensorMaxMin(sensorMap, max, min);
907 
908     int16_t mValue = 0;
909     int16_t bValue = 0;
910     int8_t rExp = 0;
911     int8_t bExp = 0;
912     bool bSigned = false;
913 
914     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
915     {
916         return ipmi::responseResponseError();
917     }
918 
919     // store a vector of property name, value to set, and interface
920     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
921 
922     // define the indexes of the tuple
923     constexpr uint8_t propertyName = 0;
924     constexpr uint8_t thresholdValue = 1;
925     constexpr uint8_t interface = 2;
926     // verifiy all needed fields are present
927     if (lowerCriticalThreshMask || upperCriticalThreshMask)
928     {
929         auto findThreshold =
930             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
931         if (findThreshold == sensorMap.end())
932         {
933             return ipmi::responseInvalidFieldRequest();
934         }
935         if (lowerCriticalThreshMask)
936         {
937             auto findLower = findThreshold->second.find("CriticalLow");
938             if (findLower == findThreshold->second.end())
939             {
940                 return ipmi::responseInvalidFieldRequest();
941             }
942             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
943                                          findThreshold->first);
944         }
945         if (upperCriticalThreshMask)
946         {
947             auto findUpper = findThreshold->second.find("CriticalHigh");
948             if (findUpper == findThreshold->second.end())
949             {
950                 return ipmi::responseInvalidFieldRequest();
951             }
952             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
953                                          findThreshold->first);
954         }
955     }
956     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
957     {
958         auto findThreshold =
959             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
960         if (findThreshold == sensorMap.end())
961         {
962             return ipmi::responseInvalidFieldRequest();
963         }
964         if (lowerNonCriticalThreshMask)
965         {
966             auto findLower = findThreshold->second.find("WarningLow");
967             if (findLower == findThreshold->second.end())
968             {
969                 return ipmi::responseInvalidFieldRequest();
970             }
971             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
972                                          findThreshold->first);
973         }
974         if (upperNonCriticalThreshMask)
975         {
976             auto findUpper = findThreshold->second.find("WarningHigh");
977             if (findUpper == findThreshold->second.end())
978             {
979                 return ipmi::responseInvalidFieldRequest();
980             }
981             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
982                                          findThreshold->first);
983         }
984     }
985     for (const auto& property : thresholdsToSet)
986     {
987         // from section 36.3 in the IPMI Spec, assume all linear
988         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
989                              (bValue * std::pow(10.0, bExp))) *
990                             std::pow(10.0, rExp);
991         setDbusProperty(
992             *getSdBus(), connection, path, std::get<interface>(property),
993             std::get<propertyName>(property), ipmi::Value(valueToSet));
994     }
995     return ipmi::responseSuccess();
996 }
997 
998 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
999 {
1000     IPMIThresholds resp;
1001     auto warningInterface =
1002         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1003     auto criticalInterface =
1004         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1005 
1006     if ((warningInterface != sensorMap.end()) ||
1007         (criticalInterface != sensorMap.end()))
1008     {
1009         auto sensorPair = sensorMap.find(sensor::sensorInterface);
1010 
1011         if (sensorPair == sensorMap.end())
1012         {
1013             // should not have been able to find a sensor not implementing
1014             // the sensor object
1015             throw std::runtime_error("Invalid sensor map");
1016         }
1017 
1018         double max = 0;
1019         double min = 0;
1020         getSensorMaxMin(sensorMap, max, min);
1021 
1022         int16_t mValue = 0;
1023         int16_t bValue = 0;
1024         int8_t rExp = 0;
1025         int8_t bExp = 0;
1026         bool bSigned = false;
1027 
1028         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1029         {
1030             throw std::runtime_error("Invalid sensor atrributes");
1031         }
1032         if (warningInterface != sensorMap.end())
1033         {
1034             auto& warningMap = warningInterface->second;
1035 
1036             auto warningHigh = warningMap.find("WarningHigh");
1037             auto warningLow = warningMap.find("WarningLow");
1038 
1039             if (warningHigh != warningMap.end())
1040             {
1041 
1042                 double value =
1043                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1044                 resp.warningHigh = scaleIPMIValueFromDouble(
1045                     value, mValue, rExp, bValue, bExp, bSigned);
1046             }
1047             if (warningLow != warningMap.end())
1048             {
1049                 double value =
1050                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1051                 resp.warningLow = scaleIPMIValueFromDouble(
1052                     value, mValue, rExp, bValue, bExp, bSigned);
1053             }
1054         }
1055         if (criticalInterface != sensorMap.end())
1056         {
1057             auto& criticalMap = criticalInterface->second;
1058 
1059             auto criticalHigh = criticalMap.find("CriticalHigh");
1060             auto criticalLow = criticalMap.find("CriticalLow");
1061 
1062             if (criticalHigh != criticalMap.end())
1063             {
1064                 double value =
1065                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1066                 resp.criticalHigh = scaleIPMIValueFromDouble(
1067                     value, mValue, rExp, bValue, bExp, bSigned);
1068             }
1069             if (criticalLow != criticalMap.end())
1070             {
1071                 double value =
1072                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1073                 resp.criticalLow = scaleIPMIValueFromDouble(
1074                     value, mValue, rExp, bValue, bExp, bSigned);
1075             }
1076         }
1077     }
1078     return resp;
1079 }
1080 
1081 ipmi::RspType<uint8_t, // readable
1082               uint8_t, // lowerNCrit
1083               uint8_t, // lowerCrit
1084               uint8_t, // lowerNrecoverable
1085               uint8_t, // upperNC
1086               uint8_t, // upperCrit
1087               uint8_t> // upperNRecoverable
1088     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1089 {
1090     std::string connection;
1091     std::string path;
1092 
1093     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1094     if (status)
1095     {
1096         return ipmi::response(status);
1097     }
1098 
1099     DbusInterfaceMap sensorMap;
1100     if (!getSensorMap(ctx, connection, path, sensorMap))
1101     {
1102         return ipmi::responseResponseError();
1103     }
1104 
1105     IPMIThresholds thresholdData;
1106     try
1107     {
1108         thresholdData = getIPMIThresholds(sensorMap);
1109     }
1110     catch (const std::exception&)
1111     {
1112         return ipmi::responseResponseError();
1113     }
1114 
1115     uint8_t readable = 0;
1116     uint8_t lowerNC = 0;
1117     uint8_t lowerCritical = 0;
1118     uint8_t lowerNonRecoverable = 0;
1119     uint8_t upperNC = 0;
1120     uint8_t upperCritical = 0;
1121     uint8_t upperNonRecoverable = 0;
1122 
1123     if (thresholdData.warningHigh)
1124     {
1125         readable |=
1126             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1127         upperNC = *thresholdData.warningHigh;
1128     }
1129     if (thresholdData.warningLow)
1130     {
1131         readable |=
1132             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1133         lowerNC = *thresholdData.warningLow;
1134     }
1135 
1136     if (thresholdData.criticalHigh)
1137     {
1138         readable |=
1139             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1140         upperCritical = *thresholdData.criticalHigh;
1141     }
1142     if (thresholdData.criticalLow)
1143     {
1144         readable |=
1145             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1146         lowerCritical = *thresholdData.criticalLow;
1147     }
1148 
1149     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1150                                  lowerNonRecoverable, upperNC, upperCritical,
1151                                  upperNonRecoverable);
1152 }
1153 
1154 /** @brief implements the get Sensor event enable command
1155  *  @param sensorNumber - sensor number
1156  *
1157  *  @returns IPMI completion code plus response data
1158  *   - enabled               - Sensor Event messages
1159  *   - assertionEnabledLsb   - Assertion event messages
1160  *   - assertionEnabledMsb   - Assertion event messages
1161  *   - deassertionEnabledLsb - Deassertion event messages
1162  *   - deassertionEnabledMsb - Deassertion event messages
1163  */
1164 
1165 ipmi::RspType<uint8_t, // enabled
1166               uint8_t, // assertionEnabledLsb
1167               uint8_t, // assertionEnabledMsb
1168               uint8_t, // deassertionEnabledLsb
1169               uint8_t> // deassertionEnabledMsb
1170     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1171 {
1172     std::string connection;
1173     std::string path;
1174 
1175     uint8_t enabled = 0;
1176     uint8_t assertionEnabledLsb = 0;
1177     uint8_t assertionEnabledMsb = 0;
1178     uint8_t deassertionEnabledLsb = 0;
1179     uint8_t deassertionEnabledMsb = 0;
1180 
1181     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1182     if (status)
1183     {
1184         return ipmi::response(status);
1185     }
1186 
1187 #ifdef FEATURE_HYBRID_SENSORS
1188     if (auto sensor = findStaticSensor(path);
1189         sensor != ipmi::sensor::sensors.end() &&
1190         getSensorEventTypeFromPath(path) !=
1191             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1192     {
1193         enabled = static_cast<uint8_t>(
1194             IPMISensorEventEnableByte2::sensorScanningEnable);
1195         uint16_t assertionEnabled = 0;
1196         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1197                                       ->second.begin()
1198                                       ->second.second)
1199         {
1200             assertionEnabled |= (1 << offsetValMap.first);
1201         }
1202         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1203         assertionEnabledMsb =
1204             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1205 
1206         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1207                                      assertionEnabledMsb, deassertionEnabledLsb,
1208                                      deassertionEnabledMsb);
1209     }
1210 #endif
1211 
1212     DbusInterfaceMap sensorMap;
1213     if (!getSensorMap(ctx, connection, path, sensorMap))
1214     {
1215         return ipmi::responseResponseError();
1216     }
1217 
1218     auto warningInterface =
1219         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1220     auto criticalInterface =
1221         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1222     if ((warningInterface != sensorMap.end()) ||
1223         (criticalInterface != sensorMap.end()))
1224     {
1225         enabled = static_cast<uint8_t>(
1226             IPMISensorEventEnableByte2::sensorScanningEnable);
1227         if (warningInterface != sensorMap.end())
1228         {
1229             auto& warningMap = warningInterface->second;
1230 
1231             auto warningHigh = warningMap.find("WarningHigh");
1232             auto warningLow = warningMap.find("WarningLow");
1233             if (warningHigh != warningMap.end())
1234             {
1235                 assertionEnabledLsb |= static_cast<uint8_t>(
1236                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1237                 deassertionEnabledLsb |= static_cast<uint8_t>(
1238                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1239             }
1240             if (warningLow != warningMap.end())
1241             {
1242                 assertionEnabledLsb |= static_cast<uint8_t>(
1243                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1244                 deassertionEnabledLsb |= static_cast<uint8_t>(
1245                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1246             }
1247         }
1248         if (criticalInterface != sensorMap.end())
1249         {
1250             auto& criticalMap = criticalInterface->second;
1251 
1252             auto criticalHigh = criticalMap.find("CriticalHigh");
1253             auto criticalLow = criticalMap.find("CriticalLow");
1254 
1255             if (criticalHigh != criticalMap.end())
1256             {
1257                 assertionEnabledMsb |= static_cast<uint8_t>(
1258                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1259                 deassertionEnabledMsb |= static_cast<uint8_t>(
1260                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1261             }
1262             if (criticalLow != criticalMap.end())
1263             {
1264                 assertionEnabledLsb |= static_cast<uint8_t>(
1265                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1266                 deassertionEnabledLsb |= static_cast<uint8_t>(
1267                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1268             }
1269         }
1270     }
1271 
1272     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1273                                  assertionEnabledMsb, deassertionEnabledLsb,
1274                                  deassertionEnabledMsb);
1275 }
1276 
1277 /** @brief implements the get Sensor event status command
1278  *  @param sensorNumber - sensor number, FFh = reserved
1279  *
1280  *  @returns IPMI completion code plus response data
1281  *   - sensorEventStatus - Sensor Event messages state
1282  *   - assertions        - Assertion event messages
1283  *   - deassertions      - Deassertion event messages
1284  */
1285 ipmi::RspType<uint8_t,         // sensorEventStatus
1286               std::bitset<16>, // assertions
1287               std::bitset<16>  // deassertion
1288               >
1289     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1290 {
1291     if (sensorNum == reservedSensorNumber)
1292     {
1293         return ipmi::responseInvalidFieldRequest();
1294     }
1295 
1296     std::string connection;
1297     std::string path;
1298     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1299     if (status)
1300     {
1301         phosphor::logging::log<phosphor::logging::level::ERR>(
1302             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1303             phosphor::logging::entry("SENSOR=%d", sensorNum));
1304         return ipmi::response(status);
1305     }
1306 
1307 #ifdef FEATURE_HYBRID_SENSORS
1308     if (auto sensor = findStaticSensor(path);
1309         sensor != ipmi::sensor::sensors.end() &&
1310         getSensorEventTypeFromPath(path) !=
1311             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1312     {
1313         auto response = ipmi::sensor::get::mapDbusToAssertion(
1314             sensor->second, path, sensor->second.sensorInterface);
1315         std::bitset<16> assertions;
1316         // deassertions are not used.
1317         std::bitset<16> deassertions = 0;
1318         uint8_t sensorEventStatus;
1319         if (response.readingOrStateUnavailable)
1320         {
1321             sensorEventStatus |= static_cast<uint8_t>(
1322                 IPMISensorReadingByte2::readingStateUnavailable);
1323         }
1324         if (response.scanningEnabled)
1325         {
1326             sensorEventStatus |= static_cast<uint8_t>(
1327                 IPMISensorReadingByte2::sensorScanningEnable);
1328         }
1329         if (response.allEventMessagesEnabled)
1330         {
1331             sensorEventStatus |= static_cast<uint8_t>(
1332                 IPMISensorReadingByte2::eventMessagesEnable);
1333         }
1334         assertions |= response.discreteReadingSensorStates << 8;
1335         assertions |= response.thresholdLevelsStates;
1336         return ipmi::responseSuccess(sensorEventStatus, assertions,
1337                                      deassertions);
1338     }
1339 #endif
1340 
1341     DbusInterfaceMap sensorMap;
1342     if (!getSensorMap(ctx, connection, path, sensorMap))
1343     {
1344         phosphor::logging::log<phosphor::logging::level::ERR>(
1345             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1346             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1347         return ipmi::responseResponseError();
1348     }
1349 
1350     uint8_t sensorEventStatus =
1351         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1352     std::bitset<16> assertions = 0;
1353     std::bitset<16> deassertions = 0;
1354 
1355     // handle VR typed sensor
1356     auto vrInterface = sensorMap.find(sensor::vrInterface);
1357     if (vrInterface != sensorMap.end())
1358     {
1359         if (!sensor::getVrEventStatus(ctx, connection, path,
1360                                       vrInterface->second, assertions))
1361         {
1362             return ipmi::responseResponseError();
1363         }
1364 
1365         // both Event Message and Sensor Scanning are disable for VR.
1366         sensorEventStatus = 0;
1367         return ipmi::responseSuccess(sensorEventStatus, assertions,
1368                                      deassertions);
1369     }
1370 
1371     auto warningInterface =
1372         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1373     auto criticalInterface =
1374         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1375 
1376     std::optional<bool> criticalDeassertHigh =
1377         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1378     std::optional<bool> criticalDeassertLow =
1379         thresholdDeassertMap[path]["CriticalAlarmLow"];
1380     std::optional<bool> warningDeassertHigh =
1381         thresholdDeassertMap[path]["WarningAlarmHigh"];
1382     std::optional<bool> warningDeassertLow =
1383         thresholdDeassertMap[path]["WarningAlarmLow"];
1384 
1385     if (criticalDeassertHigh && !*criticalDeassertHigh)
1386     {
1387         deassertions.set(static_cast<size_t>(
1388             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1389     }
1390     if (criticalDeassertLow && !*criticalDeassertLow)
1391     {
1392         deassertions.set(static_cast<size_t>(
1393             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1394     }
1395     if (warningDeassertHigh && !*warningDeassertHigh)
1396     {
1397         deassertions.set(static_cast<size_t>(
1398             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1399     }
1400     if (warningDeassertLow && !*warningDeassertLow)
1401     {
1402         deassertions.set(static_cast<size_t>(
1403             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1404     }
1405     if ((warningInterface != sensorMap.end()) ||
1406         (criticalInterface != sensorMap.end()))
1407     {
1408         sensorEventStatus = static_cast<size_t>(
1409             IPMISensorEventEnableByte2::eventMessagesEnable);
1410         if (warningInterface != sensorMap.end())
1411         {
1412             auto& warningMap = warningInterface->second;
1413 
1414             auto warningHigh = warningMap.find("WarningAlarmHigh");
1415             auto warningLow = warningMap.find("WarningAlarmLow");
1416             auto warningHighAlarm = false;
1417             auto warningLowAlarm = false;
1418 
1419             if (warningHigh != warningMap.end())
1420             {
1421                 warningHighAlarm = std::get<bool>(warningHigh->second);
1422             }
1423             if (warningLow != warningMap.end())
1424             {
1425                 warningLowAlarm = std::get<bool>(warningLow->second);
1426             }
1427             if (warningHighAlarm)
1428             {
1429                 assertions.set(
1430                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1431                                             upperNonCriticalGoingHigh));
1432             }
1433             if (warningLowAlarm)
1434             {
1435                 assertions.set(
1436                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1437                                             lowerNonCriticalGoingLow));
1438             }
1439         }
1440         if (criticalInterface != sensorMap.end())
1441         {
1442             auto& criticalMap = criticalInterface->second;
1443 
1444             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1445             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1446             auto criticalHighAlarm = false;
1447             auto criticalLowAlarm = false;
1448 
1449             if (criticalHigh != criticalMap.end())
1450             {
1451                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1452             }
1453             if (criticalLow != criticalMap.end())
1454             {
1455                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1456             }
1457             if (criticalHighAlarm)
1458             {
1459                 assertions.set(
1460                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1461                                             upperCriticalGoingHigh));
1462             }
1463             if (criticalLowAlarm)
1464             {
1465                 assertions.set(static_cast<size_t>(
1466                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1467             }
1468         }
1469     }
1470 
1471     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1472 }
1473 
1474 // Construct a type 1 SDR for threshold sensor.
1475 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1476                                  get_sdr::SensorDataFullRecord& record)
1477 {
1478     get_sdr::header::set_record_id(
1479         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1480 
1481     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1482     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1483 
1484     record.header.sdr_version = ipmiSdrVersion;
1485     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1486     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1487                                   sizeof(get_sdr::SensorDataRecordHeader);
1488     record.key.owner_id = bmcI2CAddr;
1489     record.key.owner_lun = lun;
1490     record.key.sensor_number = sensornumber;
1491 }
1492 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1493                         uint16_t recordID, const std::string& service,
1494                         const std::string& path,
1495                         get_sdr::SensorDataFullRecord& record)
1496 {
1497     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1498     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1499 
1500     DbusInterfaceMap sensorMap;
1501     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1502     {
1503         phosphor::logging::log<phosphor::logging::level::ERR>(
1504             "Failed to update sensor map for threshold sensor",
1505             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1506             phosphor::logging::entry("PATH=%s", path.c_str()));
1507         return false;
1508     }
1509 
1510     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1511     record.body.sensor_type = getSensorTypeFromPath(path);
1512     std::string type = getSensorTypeStringFromPath(path);
1513     auto typeCstr = type.c_str();
1514     auto findUnits = sensorUnits.find(typeCstr);
1515     if (findUnits != sensorUnits.end())
1516     {
1517         record.body.sensor_units_2_base =
1518             static_cast<uint8_t>(findUnits->second);
1519     } // else default 0x0 unspecified
1520 
1521     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1522 
1523     auto sensorObject = sensorMap.find(sensor::sensorInterface);
1524     if (sensorObject == sensorMap.end())
1525     {
1526         phosphor::logging::log<phosphor::logging::level::ERR>(
1527             "getSensorDataRecord: sensorObject error");
1528         return false;
1529     }
1530 
1531     uint8_t entityId = 0;
1532     uint8_t entityInstance = 0x01;
1533 
1534     // follow the association chain to get the parent board's entityid and
1535     // entityInstance
1536     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1537 
1538     record.body.entity_id = entityId;
1539     record.body.entity_instance = entityInstance;
1540 
1541     double max = 0;
1542     double min = 0;
1543     getSensorMaxMin(sensorMap, max, min);
1544 
1545     int16_t mValue = 0;
1546     int8_t rExp = 0;
1547     int16_t bValue = 0;
1548     int8_t bExp = 0;
1549     bool bSigned = false;
1550 
1551     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1552     {
1553         phosphor::logging::log<phosphor::logging::level::ERR>(
1554             "getSensorDataRecord: getSensorAttributes error");
1555         return false;
1556     }
1557 
1558     // The record.body is a struct SensorDataFullRecordBody
1559     // from sensorhandler.hpp in phosphor-ipmi-host.
1560     // The meaning of these bits appears to come from
1561     // table 43.1 of the IPMI spec.
1562     // The above 5 sensor attributes are stuffed in as follows:
1563     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1564     // Byte 22-24 are for other purposes
1565     // Byte 25 = MMMMMMMM = LSB of M
1566     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1567     // Byte 27 = BBBBBBBB = LSB of B
1568     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1569     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1570     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1571 
1572     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1573     record.body.m_lsb = mValue & 0xFF;
1574 
1575     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1576     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1577 
1578     // move the smallest bit of the MSB into place (bit 9)
1579     // the MSbs are bits 7:8 in m_msb_and_tolerance
1580     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1581 
1582     record.body.b_lsb = bValue & 0xFF;
1583 
1584     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1585     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1586 
1587     // move the smallest bit of the MSB into place (bit 9)
1588     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1589     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1590 
1591     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1592     uint8_t rExpBits = rExp & 0x07;
1593 
1594     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1595     uint8_t bExpBits = bExp & 0x07;
1596 
1597     // move rExp and bExp into place
1598     record.body.r_b_exponents =
1599         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1600 
1601     // Set the analog reading byte interpretation accordingly
1602     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1603 
1604     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1605     // These seem redundant, but derivable from the above 5 attributes
1606     // Original comment said "todo fill out rest of units"
1607 
1608     // populate sensor name from path
1609     auto name = sensor::parseSdrIdFromPath(path);
1610     record.body.id_string_info = name.size();
1611     std::strncpy(record.body.id_string, name.c_str(),
1612                  sizeof(record.body.id_string));
1613 
1614     // Remember the sensor name, as determined for this sensor number
1615     details::sdrStatsTable.updateName(sensornumber, name);
1616 
1617     bool sensorSettable = false;
1618     auto mutability =
1619         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1620     if (mutability != sensorMap.end())
1621     {
1622         sensorSettable =
1623             mappedVariant<bool>(mutability->second, "Mutable", false);
1624     }
1625     get_sdr::body::init_settable_state(sensorSettable, &record.body);
1626 
1627     // Grant write permission to sensors deemed externally settable
1628     details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
1629 
1630     IPMIThresholds thresholdData;
1631     try
1632     {
1633         thresholdData = getIPMIThresholds(sensorMap);
1634     }
1635     catch (const std::exception&)
1636     {
1637         phosphor::logging::log<phosphor::logging::level::ERR>(
1638             "getSensorDataRecord: getIPMIThresholds error");
1639         return false;
1640     }
1641 
1642     if (thresholdData.criticalHigh)
1643     {
1644         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1645         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1646             IPMISensorEventEnableThresholds::criticalThreshold);
1647         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1648             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1649         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1650             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1651         record.body.discrete_reading_setting_mask[0] |=
1652             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1653     }
1654     if (thresholdData.warningHigh)
1655     {
1656         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1657         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1658             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1659         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1660             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1661         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1662             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1663         record.body.discrete_reading_setting_mask[0] |=
1664             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1665     }
1666     if (thresholdData.criticalLow)
1667     {
1668         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1669         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1670             IPMISensorEventEnableThresholds::criticalThreshold);
1671         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1672             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1673         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1674             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1675         record.body.discrete_reading_setting_mask[0] |=
1676             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1677     }
1678     if (thresholdData.warningLow)
1679     {
1680         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1681         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1682             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1683         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1684             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1685         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1686             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1687         record.body.discrete_reading_setting_mask[0] |=
1688             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1689     }
1690 
1691     // everything that is readable is setable
1692     record.body.discrete_reading_setting_mask[1] =
1693         record.body.discrete_reading_setting_mask[0];
1694     return true;
1695 }
1696 
1697 #ifdef FEATURE_HYBRID_SENSORS
1698 // Construct a type 1 SDR for discrete Sensor typed sensor.
1699 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1700                               uint16_t recordID,
1701                               ipmi::sensor::IdInfoMap::const_iterator sensor,
1702                               get_sdr::SensorDataFullRecord& record)
1703 {
1704     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1705 
1706     record.body.entity_id = sensor->second.entityType;
1707     record.body.sensor_type = sensor->second.sensorType;
1708     record.body.event_reading_type = sensor->second.sensorReadingType;
1709     record.body.entity_instance = sensor->second.instance;
1710     if (ipmi::sensor::Mutability::Write ==
1711         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1712     {
1713         get_sdr::body::init_settable_state(true, &(record.body));
1714     }
1715 
1716     auto id_string = sensor->second.sensorName;
1717 
1718     if (id_string.empty())
1719     {
1720         id_string = sensor->second.sensorNameFunc(sensor->second);
1721     }
1722 
1723     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1724     {
1725         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1726                                      &(record.body));
1727     }
1728     else
1729     {
1730         get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1731     }
1732     std::strncpy(record.body.id_string, id_string.c_str(),
1733                  get_sdr::body::get_id_strlen(&(record.body)));
1734 }
1735 #endif
1736 
1737 // Construct type 3 SDR header and key (for VR and other discrete sensors)
1738 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1739                                 get_sdr::SensorDataEventRecord& record)
1740 {
1741     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1742     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1743 
1744     get_sdr::header::set_record_id(
1745         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1746 
1747     record.header.sdr_version = ipmiSdrVersion;
1748     record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1749     record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1750                                   sizeof(get_sdr::SensorDataRecordHeader);
1751     record.key.owner_id = bmcI2CAddr;
1752     record.key.owner_lun = lun;
1753     record.key.sensor_number = sensornumber;
1754 
1755     record.body.entity_id = 0x00;
1756     record.body.entity_instance = 0x01;
1757 }
1758 
1759 // Construct a type 3 SDR for VR typed sensor(daemon).
1760 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1761                     uint16_t recordID, const std::string& service,
1762                     const std::string& path,
1763                     get_sdr::SensorDataEventRecord& record)
1764 {
1765     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1766     constructEventSdrHeaderKey(sensorNum, recordID, record);
1767 
1768     DbusInterfaceMap sensorMap;
1769     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1770     {
1771         phosphor::logging::log<phosphor::logging::level::ERR>(
1772             "Failed to update sensor map for VR sensor",
1773             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1774             phosphor::logging::entry("PATH=%s", path.c_str()));
1775         return false;
1776     }
1777     // follow the association chain to get the parent board's entityid and
1778     // entityInstance
1779     updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1780                               record.body.entity_instance);
1781 
1782     // Sensor type is hardcoded as a module/board type instead of parsing from
1783     // sensor path. This is because VR control is allocated in an independent
1784     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1785     // types.
1786     static constexpr const uint8_t module_board_type = 0x15;
1787     record.body.sensor_type = module_board_type;
1788     record.body.event_reading_type = 0x00;
1789 
1790     record.body.sensor_record_sharing_1 = 0x00;
1791     record.body.sensor_record_sharing_2 = 0x00;
1792 
1793     // populate sensor name from path
1794     auto name = sensor::parseSdrIdFromPath(path);
1795     int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1796     record.body.id_string_info = nameSize;
1797     std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1798     std::memcpy(record.body.id_string, name.c_str(), nameSize);
1799 
1800     // Remember the sensor name, as determined for this sensor number
1801     details::sdrStatsTable.updateName(sensornumber, name);
1802 
1803     return true;
1804 }
1805 
1806 static int
1807     getSensorDataRecord(ipmi::Context::ptr ctx,
1808                         std::vector<uint8_t>& recordData, uint16_t recordID,
1809                         uint8_t readBytes = std::numeric_limits<uint8_t>::max())
1810 {
1811     size_t fruCount = 0;
1812     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1813     if (ret != ipmi::ccSuccess)
1814     {
1815         phosphor::logging::log<phosphor::logging::level::ERR>(
1816             "getSensorDataRecord: getFruSdrCount error");
1817         return GENERAL_ERROR;
1818     }
1819 
1820     auto& sensorTree = getSensorTree();
1821     size_t lastRecord =
1822         sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1823     if (recordID == lastRecordIndex)
1824     {
1825         recordID = lastRecord;
1826     }
1827     if (recordID > lastRecord)
1828     {
1829         phosphor::logging::log<phosphor::logging::level::ERR>(
1830             "getSensorDataRecord: recordID > lastRecord error");
1831         return GENERAL_ERROR;
1832     }
1833 
1834     if (recordID >= sensorTree.size())
1835     {
1836         size_t fruIndex = recordID - sensorTree.size();
1837 
1838         if (fruIndex >= fruCount)
1839         {
1840             // handle type 12 hardcoded records
1841             size_t type12Index = fruIndex - fruCount;
1842             if (type12Index >= ipmi::storage::type12Count)
1843             {
1844                 phosphor::logging::log<phosphor::logging::level::ERR>(
1845                     "getSensorDataRecord: type12Index error");
1846                 return GENERAL_ERROR;
1847             }
1848             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1849         }
1850         else
1851         {
1852             // handle fru records
1853             get_sdr::SensorDataFruRecord data;
1854             ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1855             if (ret != IPMI_CC_OK)
1856             {
1857                 return GENERAL_ERROR;
1858             }
1859             data.header.record_id_msb = recordID >> 8;
1860             data.header.record_id_lsb = recordID & 0xFF;
1861             recordData.insert(recordData.end(), (uint8_t*)&data,
1862                               ((uint8_t*)&data) + sizeof(data));
1863         }
1864 
1865         return 0;
1866     }
1867 
1868     std::string connection;
1869     std::string path;
1870     std::vector<std::string> interfaces;
1871 
1872     auto status =
1873         getSensorConnection(ctx, recordID, connection, path, &interfaces);
1874     if (status)
1875     {
1876         phosphor::logging::log<phosphor::logging::level::ERR>(
1877             "getSensorDataRecord: getSensorConnection error");
1878         return GENERAL_ERROR;
1879     }
1880     uint16_t sensorNum = getSensorNumberFromPath(path);
1881     if (sensorNum == invalidSensorNumber)
1882     {
1883         phosphor::logging::log<phosphor::logging::level::ERR>(
1884             "getSensorDataRecord: invalidSensorNumber");
1885         return GENERAL_ERROR;
1886     }
1887 
1888     // Construct full record (SDR type 1) for the threshold sensors
1889     if (std::find(interfaces.begin(), interfaces.end(),
1890                   sensor::sensorInterface) != interfaces.end())
1891     {
1892         get_sdr::SensorDataFullRecord record = {0};
1893 
1894         // If the request doesn't read SDR body, construct only header and key
1895         // part to avoid additional DBus transaction.
1896         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1897         {
1898             constructSensorSdrHeaderKey(sensorNum, recordID, record);
1899         }
1900         else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1901                                      record))
1902         {
1903             return GENERAL_ERROR;
1904         }
1905 
1906         recordData.insert(recordData.end(), (uint8_t*)&record,
1907                           ((uint8_t*)&record) + sizeof(record));
1908 
1909         return 0;
1910     }
1911 
1912 #ifdef FEATURE_HYBRID_SENSORS
1913     if (auto sensor = findStaticSensor(path);
1914         sensor != ipmi::sensor::sensors.end() &&
1915         getSensorEventTypeFromPath(path) !=
1916             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1917     {
1918         get_sdr::SensorDataFullRecord record = {0};
1919 
1920         // If the request doesn't read SDR body, construct only header and key
1921         // part to avoid additional DBus transaction.
1922         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1923         {
1924             constructSensorSdrHeaderKey(sensorNum, recordID, record);
1925         }
1926         else
1927         {
1928             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1929         }
1930 
1931         recordData.insert(recordData.end(), (uint8_t*)&record,
1932                           ((uint8_t*)&record) + sizeof(record));
1933 
1934         return 0;
1935     }
1936 #endif
1937 
1938     // Contruct SDR type 3 record for VR sensor (daemon)
1939     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1940         interfaces.end())
1941     {
1942         get_sdr::SensorDataEventRecord record = {0};
1943 
1944         // If the request doesn't read SDR body, construct only header and key
1945         // part to avoid additional DBus transaction.
1946         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1947         {
1948             constructEventSdrHeaderKey(sensorNum, recordID, record);
1949         }
1950         else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1951                                  record))
1952         {
1953             return GENERAL_ERROR;
1954         }
1955         recordData.insert(recordData.end(), (uint8_t*)&record,
1956                           ((uint8_t*)&record) + sizeof(record));
1957     }
1958 
1959     return 0;
1960 }
1961 
1962 /** @brief implements the get SDR Info command
1963  *  @param count - Operation
1964  *
1965  *  @returns IPMI completion code plus response data
1966  *   - sdrCount - sensor/SDR count
1967  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1968  */
1969 static ipmi::RspType<uint8_t, // respcount
1970                      uint8_t, // dynamic population flags
1971                      uint32_t // last time a sensor was added
1972                      >
1973     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1974                                std::optional<uint8_t> count)
1975 {
1976     auto& sensorTree = getSensorTree();
1977     uint8_t sdrCount = 0;
1978     uint16_t recordID = 0;
1979     std::vector<uint8_t> record;
1980     // Sensors are dynamically allocated, and there is at least one LUN
1981     uint8_t lunsAndDynamicPopulation = 0x80;
1982     constexpr uint8_t getSdrCount = 0x01;
1983     constexpr uint8_t getSensorCount = 0x00;
1984 
1985     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1986     {
1987         return ipmi::responseResponseError();
1988     }
1989     uint16_t numSensors = sensorTree.size();
1990     if (count.value_or(0) == getSdrCount)
1991     {
1992         // Count the number of Type 1 SDR entries assigned to the LUN
1993         while (!getSensorDataRecord(ctx, record, recordID++))
1994         {
1995             get_sdr::SensorDataRecordHeader* hdr =
1996                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1997                     record.data());
1998             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1999             {
2000                 get_sdr::SensorDataFullRecord* recordData =
2001                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
2002                         record.data());
2003                 if (ctx->lun == recordData->key.owner_lun)
2004                 {
2005                     sdrCount++;
2006                 }
2007             }
2008         }
2009     }
2010     else if (count.value_or(0) == getSensorCount)
2011     {
2012         // Return the number of sensors attached to the LUN
2013         if ((ctx->lun == 0) && (numSensors > 0))
2014         {
2015             sdrCount =
2016                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2017         }
2018         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2019         {
2020             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2021                            ? maxSensorsPerLUN
2022                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2023         }
2024         else if (ctx->lun == 3)
2025         {
2026             if (numSensors <= maxIPMISensors)
2027             {
2028                 sdrCount =
2029                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2030             }
2031             else
2032             {
2033                 // error
2034                 throw std::out_of_range(
2035                     "Maximum number of IPMI sensors exceeded.");
2036             }
2037         }
2038     }
2039     else
2040     {
2041         return ipmi::responseInvalidFieldRequest();
2042     }
2043 
2044     // Get Sensor count. This returns the number of sensors
2045     if (numSensors > 0)
2046     {
2047         lunsAndDynamicPopulation |= 1;
2048     }
2049     if (numSensors > maxSensorsPerLUN)
2050     {
2051         lunsAndDynamicPopulation |= 2;
2052     }
2053     if (numSensors >= (maxSensorsPerLUN * 2))
2054     {
2055         lunsAndDynamicPopulation |= 8;
2056     }
2057     if (numSensors > maxIPMISensors)
2058     {
2059         // error
2060         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2061     }
2062 
2063     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2064                                  sdrLastAdd);
2065 }
2066 
2067 /* end sensor commands */
2068 
2069 /* storage commands */
2070 
2071 ipmi::RspType<uint8_t,  // sdr version
2072               uint16_t, // record count
2073               uint16_t, // free space
2074               uint32_t, // most recent addition
2075               uint32_t, // most recent erase
2076               uint8_t   // operationSupport
2077               >
2078     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2079 {
2080     auto& sensorTree = getSensorTree();
2081     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2082     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2083     {
2084         return ipmi::responseResponseError();
2085     }
2086 
2087     size_t fruCount = 0;
2088     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2089     if (ret != ipmi::ccSuccess)
2090     {
2091         return ipmi::response(ret);
2092     }
2093 
2094     uint16_t recordCount =
2095         sensorTree.size() + fruCount + ipmi::storage::type12Count;
2096 
2097     uint8_t operationSupport = static_cast<uint8_t>(
2098         SdrRepositoryInfoOps::overflow); // write not supported
2099 
2100     operationSupport |=
2101         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2102     operationSupport |= static_cast<uint8_t>(
2103         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2104     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2105                                  unspecifiedFreeSpace, sdrLastAdd,
2106                                  sdrLastRemove, operationSupport);
2107 }
2108 
2109 /** @brief implements the get SDR allocation info command
2110  *
2111  *  @returns IPMI completion code plus response data
2112  *   - allocUnits    - Number of possible allocation units
2113  *   - allocUnitSize - Allocation unit size in bytes.
2114  *   - allocUnitFree - Number of free allocation units
2115  *   - allocUnitLargestFree - Largest free block in allocation units
2116  *   - maxRecordSize    - Maximum record size in allocation units.
2117  */
2118 ipmi::RspType<uint16_t, // allocUnits
2119               uint16_t, // allocUnitSize
2120               uint16_t, // allocUnitFree
2121               uint16_t, // allocUnitLargestFree
2122               uint8_t   // maxRecordSize
2123               >
2124     ipmiStorageGetSDRAllocationInfo()
2125 {
2126     // 0000h unspecified number of alloc units
2127     constexpr uint16_t allocUnits = 0;
2128 
2129     constexpr uint16_t allocUnitFree = 0;
2130     constexpr uint16_t allocUnitLargestFree = 0;
2131     // only allow one block at a time
2132     constexpr uint8_t maxRecordSize = 1;
2133 
2134     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2135                                  allocUnitLargestFree, maxRecordSize);
2136 }
2137 
2138 /** @brief implements the reserve SDR command
2139  *  @returns IPMI completion code plus response data
2140  *   - sdrReservationID
2141  */
2142 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2143 {
2144     sdrReservationID++;
2145     if (sdrReservationID == 0)
2146     {
2147         sdrReservationID++;
2148     }
2149 
2150     return ipmi::responseSuccess(sdrReservationID);
2151 }
2152 
2153 ipmi::RspType<uint16_t,            // next record ID
2154               std::vector<uint8_t> // payload
2155               >
2156     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2157                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2158 {
2159     size_t fruCount = 0;
2160     // reservation required for partial reads with non zero offset into
2161     // record
2162     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2163     {
2164         phosphor::logging::log<phosphor::logging::level::ERR>(
2165             "ipmiStorageGetSDR: responseInvalidReservationId");
2166         return ipmi::responseInvalidReservationId();
2167     }
2168     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2169     if (ret != ipmi::ccSuccess)
2170     {
2171         phosphor::logging::log<phosphor::logging::level::ERR>(
2172             "ipmiStorageGetSDR: getFruSdrCount error");
2173         return ipmi::response(ret);
2174     }
2175 
2176     auto& sensorTree = getSensorTree();
2177     size_t lastRecord =
2178         sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
2179     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2180 
2181     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2182     {
2183         phosphor::logging::log<phosphor::logging::level::ERR>(
2184             "ipmiStorageGetSDR: getSensorSubtree error");
2185         return ipmi::responseResponseError();
2186     }
2187 
2188     std::vector<uint8_t> record;
2189     if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
2190     {
2191         phosphor::logging::log<phosphor::logging::level::ERR>(
2192             "ipmiStorageGetSDR: fail to get SDR");
2193         return ipmi::responseInvalidFieldRequest();
2194     }
2195     get_sdr::SensorDataRecordHeader* hdr =
2196         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2197     if (!hdr)
2198     {
2199         phosphor::logging::log<phosphor::logging::level::ERR>(
2200             "ipmiStorageGetSDR: record header is null");
2201         return ipmi::responseSuccess(nextRecordId, record);
2202     }
2203 
2204     size_t sdrLength =
2205         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2206     if (sdrLength < (offset + bytesToRead))
2207     {
2208         bytesToRead = sdrLength - offset;
2209     }
2210 
2211     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2212     if (!respStart)
2213     {
2214         phosphor::logging::log<phosphor::logging::level::ERR>(
2215             "ipmiStorageGetSDR: record is null");
2216         return ipmi::responseSuccess(nextRecordId, record);
2217     }
2218 
2219     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2220 
2221     return ipmi::responseSuccess(nextRecordId, recordData);
2222 }
2223 /* end storage commands */
2224 
2225 void registerSensorFunctions()
2226 {
2227     // <Platform Event>
2228     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2229                           ipmi::sensor_event::cmdPlatformEvent,
2230                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2231 
2232     // <Set Sensor Reading and Event Status>
2233     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2234                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2235                           ipmi::Privilege::Operator, ipmiSetSensorReading);
2236 
2237     // <Get Sensor Reading>
2238     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2239                           ipmi::sensor_event::cmdGetSensorReading,
2240                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2241 
2242     // <Get Sensor Threshold>
2243     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2244                           ipmi::sensor_event::cmdGetSensorThreshold,
2245                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2246 
2247     // <Set Sensor Threshold>
2248     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2249                           ipmi::sensor_event::cmdSetSensorThreshold,
2250                           ipmi::Privilege::Operator,
2251                           ipmiSenSetSensorThresholds);
2252 
2253     // <Get Sensor Event Enable>
2254     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2255                           ipmi::sensor_event::cmdGetSensorEventEnable,
2256                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2257 
2258     // <Get Sensor Event Status>
2259     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2260                           ipmi::sensor_event::cmdGetSensorEventStatus,
2261                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2262 
2263     // register all storage commands for both Sensor and Storage command
2264     // versions
2265 
2266     // <Get SDR Repository Info>
2267     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2268                           ipmi::storage::cmdGetSdrRepositoryInfo,
2269                           ipmi::Privilege::User,
2270                           ipmiStorageGetSDRRepositoryInfo);
2271 
2272     // <Get Device SDR Info>
2273     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2274                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2275                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2276 
2277     // <Get SDR Allocation Info>
2278     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2279                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2280                           ipmi::Privilege::User,
2281                           ipmiStorageGetSDRAllocationInfo);
2282 
2283     // <Reserve SDR Repo>
2284     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2285                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2286                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2287 
2288     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2289                           ipmi::storage::cmdReserveSdrRepository,
2290                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2291 
2292     // <Get Sdr>
2293     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2294                           ipmi::sensor_event::cmdGetDeviceSdr,
2295                           ipmi::Privilege::User, ipmiStorageGetSDR);
2296 
2297     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2298                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2299                           ipmiStorageGetSDR);
2300 }
2301 } // namespace ipmi
2302