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