xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sensorcommands.cpp (revision f0a89946f1e3ce2e8fe97525e6b057463aa2a169)
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         // Only allow external SetSensor if write permission granted
549         if (!details::sdrWriteTable.getWritePermission(sensorNumber))
550         {
551             return ipmi::responseResponseError();
552         }
553 
554         auto value =
555             sensor::calculateValue(reading, sensorMap, sensorObject->second);
556         if (!value)
557         {
558             return ipmi::responseResponseError();
559         }
560 
561         if constexpr (debug)
562         {
563             phosphor::logging::log<phosphor::logging::level::INFO>(
564                 "IPMI SET_SENSOR",
565                 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
566                 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
567                 phosphor::logging::entry("VALUE=%f", *value));
568         }
569 
570         boost::system::error_code ec =
571             setDbusProperty(ctx, connection, path, sensor::sensorInterface,
572                             "Value", ipmi::Value(*value));
573 
574         // setDbusProperty intended to resolve dbus exception/rc within the
575         // function but failed to achieve that. Catch exception in the ipmi
576         // callback functions for now (e.g. ipmiSetSensorReading).
577         if (ec)
578         {
579             using namespace phosphor::logging;
580             log<level::ERR>("Failed to set property",
581                             entry("PROPERTY=%s", "Value"),
582                             entry("PATH=%s", path.c_str()),
583                             entry("INTERFACE=%s", sensor::sensorInterface),
584                             entry("WHAT=%s", ec.message().c_str()));
585             return ipmi::responseResponseError();
586         }
587         return ipmi::responseSuccess();
588     }
589 
590     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
591         interfaces.end())
592     {
593         DbusInterfaceMap sensorMap;
594         if (!getSensorMap(ctx, connection, path, sensorMap))
595         {
596             return ipmi::responseResponseError();
597         }
598         auto sensorObject = sensorMap.find(sensor::vrInterface);
599         if (sensorObject == sensorMap.end())
600         {
601             return ipmi::responseResponseError();
602         }
603 
604         // VR sensors are treated as a special case and we will not check the
605         // write permission for VR sensors, since they always deemed writable
606         // and permission table are not applied to VR sensors.
607         auto vrMode =
608             sensor::calculateVRMode(assertOffset, sensorObject->second);
609         if (!vrMode)
610         {
611             return ipmi::responseResponseError();
612         }
613         boost::system::error_code ec = setDbusProperty(
614             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
615         // setDbusProperty intended to resolve dbus exception/rc within the
616         // function but failed to achieve that. Catch exception in the ipmi
617         // callback functions for now (e.g. ipmiSetSensorReading).
618         if (ec)
619         {
620             using namespace phosphor::logging;
621             log<level::ERR>("Failed to set property",
622                             entry("PROPERTY=%s", "Selected"),
623                             entry("PATH=%s", path.c_str()),
624                             entry("INTERFACE=%s", sensor::sensorInterface),
625                             entry("WHAT=%s", ec.message().c_str()));
626             return ipmi::responseResponseError();
627         }
628         return ipmi::responseSuccess();
629     }
630 
631     phosphor::logging::log<phosphor::logging::level::ERR>(
632         "unknown sensor type",
633         phosphor::logging::entry("PATH=%s", path.c_str()));
634     return ipmi::responseResponseError();
635 }
636 
637 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
638     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
639 {
640     std::string connection;
641     std::string path;
642 
643     auto status = getSensorConnection(ctx, sensnum, connection, path);
644     if (status)
645     {
646         return ipmi::response(status);
647     }
648 
649 #ifdef FEATURE_HYBRID_SENSORS
650     if (auto sensor = findStaticSensor(path);
651         sensor != ipmi::sensor::sensors.end() &&
652         getSensorEventTypeFromPath(path) !=
653             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
654     {
655         if (ipmi::sensor::Mutability::Read !=
656             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
657         {
658             return ipmi::responseIllegalCommand();
659         }
660 
661         uint8_t operation;
662         try
663         {
664             ipmi::sensor::GetSensorResponse getResponse =
665                 sensor->second.getFunc(sensor->second);
666 
667             if (getResponse.readingOrStateUnavailable)
668             {
669                 operation |= static_cast<uint8_t>(
670                     IPMISensorReadingByte2::readingStateUnavailable);
671             }
672             if (getResponse.scanningEnabled)
673             {
674                 operation |= static_cast<uint8_t>(
675                     IPMISensorReadingByte2::sensorScanningEnable);
676             }
677             if (getResponse.allEventMessagesEnabled)
678             {
679                 operation |= static_cast<uint8_t>(
680                     IPMISensorReadingByte2::eventMessagesEnable);
681             }
682             return ipmi::responseSuccess(
683                 getResponse.reading, operation,
684                 getResponse.thresholdLevelsStates,
685                 getResponse.discreteReadingSensorStates);
686         }
687         catch (const std::exception& e)
688         {
689             operation |= static_cast<uint8_t>(
690                 IPMISensorReadingByte2::readingStateUnavailable);
691             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
692         }
693     }
694 #endif
695 
696     DbusInterfaceMap sensorMap;
697     if (!getSensorMap(ctx, connection, path, sensorMap))
698     {
699         return ipmi::responseResponseError();
700     }
701     auto sensorObject = sensorMap.find(sensor::sensorInterface);
702 
703     if (sensorObject == sensorMap.end() ||
704         sensorObject->second.find("Value") == sensorObject->second.end())
705     {
706         return ipmi::responseResponseError();
707     }
708     auto& valueVariant = sensorObject->second["Value"];
709     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
710 
711     double max = 0;
712     double min = 0;
713     getSensorMaxMin(sensorMap, max, min);
714 
715     int16_t mValue = 0;
716     int16_t bValue = 0;
717     int8_t rExp = 0;
718     int8_t bExp = 0;
719     bool bSigned = false;
720 
721     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
722     {
723         return ipmi::responseResponseError();
724     }
725 
726     uint8_t value =
727         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
728     uint8_t operation =
729         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
730     operation |=
731         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
732     bool notReading = std::isnan(reading);
733 
734     if (!notReading)
735     {
736         auto availableObject =
737             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
738         if (availableObject != sensorMap.end())
739         {
740             auto findAvailable = availableObject->second.find("Available");
741             if (findAvailable != availableObject->second.end())
742             {
743                 bool* available = std::get_if<bool>(&(findAvailable->second));
744                 if (available && !(*available))
745                 {
746                     notReading = true;
747                 }
748             }
749         }
750     }
751 
752     if (notReading)
753     {
754         operation |= static_cast<uint8_t>(
755             IPMISensorReadingByte2::readingStateUnavailable);
756     }
757 
758     if constexpr (details::enableInstrumentation)
759     {
760         int byteValue;
761         if (bSigned)
762         {
763             byteValue = static_cast<int>(static_cast<int8_t>(value));
764         }
765         else
766         {
767             byteValue = static_cast<int>(static_cast<uint8_t>(value));
768         }
769 
770         // Keep stats on the reading just obtained, even if it is "NaN"
771         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
772         {
773             // This is the first reading, show the coefficients
774             double step = (max - min) / 255.0;
775             std::cerr << "IPMI sensor "
776                       << details::sdrStatsTable.getName(sensnum)
777                       << ": Range min=" << min << " max=" << max
778                       << ", step=" << step
779                       << ", Coefficients mValue=" << static_cast<int>(mValue)
780                       << " rExp=" << static_cast<int>(rExp)
781                       << " bValue=" << static_cast<int>(bValue)
782                       << " bExp=" << static_cast<int>(bExp)
783                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
784         }
785     }
786 
787     uint8_t thresholds = 0;
788 
789     auto warningObject =
790         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
791     if (warningObject != sensorMap.end())
792     {
793         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
794         auto alarmLow = warningObject->second.find("WarningAlarmLow");
795         if (alarmHigh != warningObject->second.end())
796         {
797             if (std::get<bool>(alarmHigh->second))
798             {
799                 thresholds |= static_cast<uint8_t>(
800                     IPMISensorReadingByte3::upperNonCritical);
801             }
802         }
803         if (alarmLow != warningObject->second.end())
804         {
805             if (std::get<bool>(alarmLow->second))
806             {
807                 thresholds |= static_cast<uint8_t>(
808                     IPMISensorReadingByte3::lowerNonCritical);
809             }
810         }
811     }
812 
813     auto criticalObject =
814         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
815     if (criticalObject != sensorMap.end())
816     {
817         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
818         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
819         if (alarmHigh != criticalObject->second.end())
820         {
821             if (std::get<bool>(alarmHigh->second))
822             {
823                 thresholds |=
824                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
825             }
826         }
827         if (alarmLow != criticalObject->second.end())
828         {
829             if (std::get<bool>(alarmLow->second))
830             {
831                 thresholds |=
832                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
833             }
834         }
835     }
836 
837     // no discrete as of today so optional byte is never returned
838     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
839 }
840 
841 /** @brief implements the Set Sensor threshold command
842  *  @param sensorNumber        - sensor number
843  *  @param lowerNonCriticalThreshMask
844  *  @param lowerCriticalThreshMask
845  *  @param lowerNonRecovThreshMask
846  *  @param upperNonCriticalThreshMask
847  *  @param upperCriticalThreshMask
848  *  @param upperNonRecovThreshMask
849  *  @param reserved
850  *  @param lowerNonCritical    - lower non-critical threshold
851  *  @param lowerCritical       - Lower critical threshold
852  *  @param lowerNonRecoverable - Lower non recovarable threshold
853  *  @param upperNonCritical    - Upper non-critical threshold
854  *  @param upperCritical       - Upper critical
855  *  @param upperNonRecoverable - Upper Non-recoverable
856  *
857  *  @returns IPMI completion code
858  */
859 ipmi::RspType<> ipmiSenSetSensorThresholds(
860     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
861     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
862     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
863     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
864     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
865     uint8_t upperNonCritical, uint8_t upperCritical,
866     uint8_t upperNonRecoverable)
867 {
868     if (reserved)
869     {
870         return ipmi::responseInvalidFieldRequest();
871     }
872 
873     // lower nc and upper nc not suppported on any sensor
874     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
875     {
876         return ipmi::responseInvalidFieldRequest();
877     }
878 
879     // if none of the threshold mask are set, nothing to do
880     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
881           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
882           upperCriticalThreshMask | upperNonRecovThreshMask))
883     {
884         return ipmi::responseSuccess();
885     }
886 
887     std::string connection;
888     std::string path;
889 
890     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
891     if (status)
892     {
893         return ipmi::response(status);
894     }
895     DbusInterfaceMap sensorMap;
896     if (!getSensorMap(ctx, connection, path, sensorMap))
897     {
898         return ipmi::responseResponseError();
899     }
900 
901     double max = 0;
902     double min = 0;
903     getSensorMaxMin(sensorMap, max, min);
904 
905     int16_t mValue = 0;
906     int16_t bValue = 0;
907     int8_t rExp = 0;
908     int8_t bExp = 0;
909     bool bSigned = false;
910 
911     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
912     {
913         return ipmi::responseResponseError();
914     }
915 
916     // store a vector of property name, value to set, and interface
917     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
918 
919     // define the indexes of the tuple
920     constexpr uint8_t propertyName = 0;
921     constexpr uint8_t thresholdValue = 1;
922     constexpr uint8_t interface = 2;
923     // verifiy all needed fields are present
924     if (lowerCriticalThreshMask || upperCriticalThreshMask)
925     {
926         auto findThreshold =
927             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
928         if (findThreshold == sensorMap.end())
929         {
930             return ipmi::responseInvalidFieldRequest();
931         }
932         if (lowerCriticalThreshMask)
933         {
934             auto findLower = findThreshold->second.find("CriticalLow");
935             if (findLower == findThreshold->second.end())
936             {
937                 return ipmi::responseInvalidFieldRequest();
938             }
939             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
940                                          findThreshold->first);
941         }
942         if (upperCriticalThreshMask)
943         {
944             auto findUpper = findThreshold->second.find("CriticalHigh");
945             if (findUpper == findThreshold->second.end())
946             {
947                 return ipmi::responseInvalidFieldRequest();
948             }
949             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
950                                          findThreshold->first);
951         }
952     }
953     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
954     {
955         auto findThreshold =
956             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
957         if (findThreshold == sensorMap.end())
958         {
959             return ipmi::responseInvalidFieldRequest();
960         }
961         if (lowerNonCriticalThreshMask)
962         {
963             auto findLower = findThreshold->second.find("WarningLow");
964             if (findLower == findThreshold->second.end())
965             {
966                 return ipmi::responseInvalidFieldRequest();
967             }
968             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
969                                          findThreshold->first);
970         }
971         if (upperNonCriticalThreshMask)
972         {
973             auto findUpper = findThreshold->second.find("WarningHigh");
974             if (findUpper == findThreshold->second.end())
975             {
976                 return ipmi::responseInvalidFieldRequest();
977             }
978             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
979                                          findThreshold->first);
980         }
981     }
982     for (const auto& property : thresholdsToSet)
983     {
984         // from section 36.3 in the IPMI Spec, assume all linear
985         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
986                              (bValue * std::pow(10.0, bExp))) *
987                             std::pow(10.0, rExp);
988         setDbusProperty(
989             *getSdBus(), connection, path, std::get<interface>(property),
990             std::get<propertyName>(property), ipmi::Value(valueToSet));
991     }
992     return ipmi::responseSuccess();
993 }
994 
995 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
996 {
997     IPMIThresholds resp;
998     auto warningInterface =
999         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1000     auto criticalInterface =
1001         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1002 
1003     if ((warningInterface != sensorMap.end()) ||
1004         (criticalInterface != sensorMap.end()))
1005     {
1006         auto sensorPair = sensorMap.find(sensor::sensorInterface);
1007 
1008         if (sensorPair == sensorMap.end())
1009         {
1010             // should not have been able to find a sensor not implementing
1011             // the sensor object
1012             throw std::runtime_error("Invalid sensor map");
1013         }
1014 
1015         double max = 0;
1016         double min = 0;
1017         getSensorMaxMin(sensorMap, max, min);
1018 
1019         int16_t mValue = 0;
1020         int16_t bValue = 0;
1021         int8_t rExp = 0;
1022         int8_t bExp = 0;
1023         bool bSigned = false;
1024 
1025         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1026         {
1027             throw std::runtime_error("Invalid sensor atrributes");
1028         }
1029         if (warningInterface != sensorMap.end())
1030         {
1031             auto& warningMap = warningInterface->second;
1032 
1033             auto warningHigh = warningMap.find("WarningHigh");
1034             auto warningLow = warningMap.find("WarningLow");
1035 
1036             if (warningHigh != warningMap.end())
1037             {
1038 
1039                 double value =
1040                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1041                 resp.warningHigh = scaleIPMIValueFromDouble(
1042                     value, mValue, rExp, bValue, bExp, bSigned);
1043             }
1044             if (warningLow != warningMap.end())
1045             {
1046                 double value =
1047                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1048                 resp.warningLow = scaleIPMIValueFromDouble(
1049                     value, mValue, rExp, bValue, bExp, bSigned);
1050             }
1051         }
1052         if (criticalInterface != sensorMap.end())
1053         {
1054             auto& criticalMap = criticalInterface->second;
1055 
1056             auto criticalHigh = criticalMap.find("CriticalHigh");
1057             auto criticalLow = criticalMap.find("CriticalLow");
1058 
1059             if (criticalHigh != criticalMap.end())
1060             {
1061                 double value =
1062                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1063                 resp.criticalHigh = scaleIPMIValueFromDouble(
1064                     value, mValue, rExp, bValue, bExp, bSigned);
1065             }
1066             if (criticalLow != criticalMap.end())
1067             {
1068                 double value =
1069                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1070                 resp.criticalLow = scaleIPMIValueFromDouble(
1071                     value, mValue, rExp, bValue, bExp, bSigned);
1072             }
1073         }
1074     }
1075     return resp;
1076 }
1077 
1078 ipmi::RspType<uint8_t, // readable
1079               uint8_t, // lowerNCrit
1080               uint8_t, // lowerCrit
1081               uint8_t, // lowerNrecoverable
1082               uint8_t, // upperNC
1083               uint8_t, // upperCrit
1084               uint8_t> // upperNRecoverable
1085     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1086 {
1087     std::string connection;
1088     std::string path;
1089 
1090     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1091     if (status)
1092     {
1093         return ipmi::response(status);
1094     }
1095 
1096     DbusInterfaceMap sensorMap;
1097     if (!getSensorMap(ctx, connection, path, sensorMap))
1098     {
1099         return ipmi::responseResponseError();
1100     }
1101 
1102     IPMIThresholds thresholdData;
1103     try
1104     {
1105         thresholdData = getIPMIThresholds(sensorMap);
1106     }
1107     catch (const std::exception&)
1108     {
1109         return ipmi::responseResponseError();
1110     }
1111 
1112     uint8_t readable = 0;
1113     uint8_t lowerNC = 0;
1114     uint8_t lowerCritical = 0;
1115     uint8_t lowerNonRecoverable = 0;
1116     uint8_t upperNC = 0;
1117     uint8_t upperCritical = 0;
1118     uint8_t upperNonRecoverable = 0;
1119 
1120     if (thresholdData.warningHigh)
1121     {
1122         readable |=
1123             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1124         upperNC = *thresholdData.warningHigh;
1125     }
1126     if (thresholdData.warningLow)
1127     {
1128         readable |=
1129             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1130         lowerNC = *thresholdData.warningLow;
1131     }
1132 
1133     if (thresholdData.criticalHigh)
1134     {
1135         readable |=
1136             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1137         upperCritical = *thresholdData.criticalHigh;
1138     }
1139     if (thresholdData.criticalLow)
1140     {
1141         readable |=
1142             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1143         lowerCritical = *thresholdData.criticalLow;
1144     }
1145 
1146     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1147                                  lowerNonRecoverable, upperNC, upperCritical,
1148                                  upperNonRecoverable);
1149 }
1150 
1151 /** @brief implements the get Sensor event enable command
1152  *  @param sensorNumber - sensor number
1153  *
1154  *  @returns IPMI completion code plus response data
1155  *   - enabled               - Sensor Event messages
1156  *   - assertionEnabledLsb   - Assertion event messages
1157  *   - assertionEnabledMsb   - Assertion event messages
1158  *   - deassertionEnabledLsb - Deassertion event messages
1159  *   - deassertionEnabledMsb - Deassertion event messages
1160  */
1161 
1162 ipmi::RspType<uint8_t, // enabled
1163               uint8_t, // assertionEnabledLsb
1164               uint8_t, // assertionEnabledMsb
1165               uint8_t, // deassertionEnabledLsb
1166               uint8_t> // deassertionEnabledMsb
1167     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1168 {
1169     std::string connection;
1170     std::string path;
1171 
1172     uint8_t enabled = 0;
1173     uint8_t assertionEnabledLsb = 0;
1174     uint8_t assertionEnabledMsb = 0;
1175     uint8_t deassertionEnabledLsb = 0;
1176     uint8_t deassertionEnabledMsb = 0;
1177 
1178     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1179     if (status)
1180     {
1181         return ipmi::response(status);
1182     }
1183 
1184 #ifdef FEATURE_HYBRID_SENSORS
1185     if (auto sensor = findStaticSensor(path);
1186         sensor != ipmi::sensor::sensors.end() &&
1187         getSensorEventTypeFromPath(path) !=
1188             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1189     {
1190         enabled = static_cast<uint8_t>(
1191             IPMISensorEventEnableByte2::sensorScanningEnable);
1192         uint16_t assertionEnabled = 0;
1193         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1194                                       ->second.begin()
1195                                       ->second.second)
1196         {
1197             assertionEnabled |= (1 << offsetValMap.first);
1198         }
1199         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1200         assertionEnabledMsb =
1201             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1202 
1203         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1204                                      assertionEnabledMsb, deassertionEnabledLsb,
1205                                      deassertionEnabledMsb);
1206     }
1207 #endif
1208 
1209     DbusInterfaceMap sensorMap;
1210     if (!getSensorMap(ctx, connection, path, sensorMap))
1211     {
1212         return ipmi::responseResponseError();
1213     }
1214 
1215     auto warningInterface =
1216         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1217     auto criticalInterface =
1218         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1219     if ((warningInterface != sensorMap.end()) ||
1220         (criticalInterface != sensorMap.end()))
1221     {
1222         enabled = static_cast<uint8_t>(
1223             IPMISensorEventEnableByte2::sensorScanningEnable);
1224         if (warningInterface != sensorMap.end())
1225         {
1226             auto& warningMap = warningInterface->second;
1227 
1228             auto warningHigh = warningMap.find("WarningHigh");
1229             auto warningLow = warningMap.find("WarningLow");
1230             if (warningHigh != warningMap.end())
1231             {
1232                 assertionEnabledLsb |= static_cast<uint8_t>(
1233                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1234                 deassertionEnabledLsb |= static_cast<uint8_t>(
1235                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1236             }
1237             if (warningLow != warningMap.end())
1238             {
1239                 assertionEnabledLsb |= static_cast<uint8_t>(
1240                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1241                 deassertionEnabledLsb |= static_cast<uint8_t>(
1242                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1243             }
1244         }
1245         if (criticalInterface != sensorMap.end())
1246         {
1247             auto& criticalMap = criticalInterface->second;
1248 
1249             auto criticalHigh = criticalMap.find("CriticalHigh");
1250             auto criticalLow = criticalMap.find("CriticalLow");
1251 
1252             if (criticalHigh != criticalMap.end())
1253             {
1254                 assertionEnabledMsb |= static_cast<uint8_t>(
1255                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1256                 deassertionEnabledMsb |= static_cast<uint8_t>(
1257                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1258             }
1259             if (criticalLow != criticalMap.end())
1260             {
1261                 assertionEnabledLsb |= static_cast<uint8_t>(
1262                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1263                 deassertionEnabledLsb |= static_cast<uint8_t>(
1264                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1265             }
1266         }
1267     }
1268 
1269     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1270                                  assertionEnabledMsb, deassertionEnabledLsb,
1271                                  deassertionEnabledMsb);
1272 }
1273 
1274 /** @brief implements the get Sensor event status command
1275  *  @param sensorNumber - sensor number, FFh = reserved
1276  *
1277  *  @returns IPMI completion code plus response data
1278  *   - sensorEventStatus - Sensor Event messages state
1279  *   - assertions        - Assertion event messages
1280  *   - deassertions      - Deassertion event messages
1281  */
1282 ipmi::RspType<uint8_t,         // sensorEventStatus
1283               std::bitset<16>, // assertions
1284               std::bitset<16>  // deassertion
1285               >
1286     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1287 {
1288     if (sensorNum == reservedSensorNumber)
1289     {
1290         return ipmi::responseInvalidFieldRequest();
1291     }
1292 
1293     std::string connection;
1294     std::string path;
1295     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1296     if (status)
1297     {
1298         phosphor::logging::log<phosphor::logging::level::ERR>(
1299             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1300             phosphor::logging::entry("SENSOR=%d", sensorNum));
1301         return ipmi::response(status);
1302     }
1303 
1304 #ifdef FEATURE_HYBRID_SENSORS
1305     if (auto sensor = findStaticSensor(path);
1306         sensor != ipmi::sensor::sensors.end() &&
1307         getSensorEventTypeFromPath(path) !=
1308             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1309     {
1310         auto response = ipmi::sensor::get::mapDbusToAssertion(
1311             sensor->second, path, sensor->second.sensorInterface);
1312         std::bitset<16> assertions;
1313         // deassertions are not used.
1314         std::bitset<16> deassertions = 0;
1315         uint8_t sensorEventStatus;
1316         if (response.readingOrStateUnavailable)
1317         {
1318             sensorEventStatus |= static_cast<uint8_t>(
1319                 IPMISensorReadingByte2::readingStateUnavailable);
1320         }
1321         if (response.scanningEnabled)
1322         {
1323             sensorEventStatus |= static_cast<uint8_t>(
1324                 IPMISensorReadingByte2::sensorScanningEnable);
1325         }
1326         if (response.allEventMessagesEnabled)
1327         {
1328             sensorEventStatus |= static_cast<uint8_t>(
1329                 IPMISensorReadingByte2::eventMessagesEnable);
1330         }
1331         assertions |= response.discreteReadingSensorStates << 8;
1332         assertions |= response.thresholdLevelsStates;
1333         return ipmi::responseSuccess(sensorEventStatus, assertions,
1334                                      deassertions);
1335     }
1336 #endif
1337 
1338     DbusInterfaceMap sensorMap;
1339     if (!getSensorMap(ctx, connection, path, sensorMap))
1340     {
1341         phosphor::logging::log<phosphor::logging::level::ERR>(
1342             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1343             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1344         return ipmi::responseResponseError();
1345     }
1346 
1347     uint8_t sensorEventStatus =
1348         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1349     std::bitset<16> assertions = 0;
1350     std::bitset<16> deassertions = 0;
1351 
1352     // handle VR typed sensor
1353     auto vrInterface = sensorMap.find(sensor::vrInterface);
1354     if (vrInterface != sensorMap.end())
1355     {
1356         if (!sensor::getVrEventStatus(ctx, connection, path,
1357                                       vrInterface->second, assertions))
1358         {
1359             return ipmi::responseResponseError();
1360         }
1361 
1362         // both Event Message and Sensor Scanning are disable for VR.
1363         sensorEventStatus = 0;
1364         return ipmi::responseSuccess(sensorEventStatus, assertions,
1365                                      deassertions);
1366     }
1367 
1368     auto warningInterface =
1369         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1370     auto criticalInterface =
1371         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1372 
1373     std::optional<bool> criticalDeassertHigh =
1374         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1375     std::optional<bool> criticalDeassertLow =
1376         thresholdDeassertMap[path]["CriticalAlarmLow"];
1377     std::optional<bool> warningDeassertHigh =
1378         thresholdDeassertMap[path]["WarningAlarmHigh"];
1379     std::optional<bool> warningDeassertLow =
1380         thresholdDeassertMap[path]["WarningAlarmLow"];
1381 
1382     if (criticalDeassertHigh && !*criticalDeassertHigh)
1383     {
1384         deassertions.set(static_cast<size_t>(
1385             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1386     }
1387     if (criticalDeassertLow && !*criticalDeassertLow)
1388     {
1389         deassertions.set(static_cast<size_t>(
1390             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1391     }
1392     if (warningDeassertHigh && !*warningDeassertHigh)
1393     {
1394         deassertions.set(static_cast<size_t>(
1395             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1396     }
1397     if (warningDeassertLow && !*warningDeassertLow)
1398     {
1399         deassertions.set(static_cast<size_t>(
1400             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1401     }
1402     if ((warningInterface != sensorMap.end()) ||
1403         (criticalInterface != sensorMap.end()))
1404     {
1405         sensorEventStatus = static_cast<size_t>(
1406             IPMISensorEventEnableByte2::eventMessagesEnable);
1407         if (warningInterface != sensorMap.end())
1408         {
1409             auto& warningMap = warningInterface->second;
1410 
1411             auto warningHigh = warningMap.find("WarningAlarmHigh");
1412             auto warningLow = warningMap.find("WarningAlarmLow");
1413             auto warningHighAlarm = false;
1414             auto warningLowAlarm = false;
1415 
1416             if (warningHigh != warningMap.end())
1417             {
1418                 warningHighAlarm = std::get<bool>(warningHigh->second);
1419             }
1420             if (warningLow != warningMap.end())
1421             {
1422                 warningLowAlarm = std::get<bool>(warningLow->second);
1423             }
1424             if (warningHighAlarm)
1425             {
1426                 assertions.set(
1427                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1428                                             upperNonCriticalGoingHigh));
1429             }
1430             if (warningLowAlarm)
1431             {
1432                 assertions.set(
1433                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1434                                             lowerNonCriticalGoingLow));
1435             }
1436         }
1437         if (criticalInterface != sensorMap.end())
1438         {
1439             auto& criticalMap = criticalInterface->second;
1440 
1441             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1442             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1443             auto criticalHighAlarm = false;
1444             auto criticalLowAlarm = false;
1445 
1446             if (criticalHigh != criticalMap.end())
1447             {
1448                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1449             }
1450             if (criticalLow != criticalMap.end())
1451             {
1452                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1453             }
1454             if (criticalHighAlarm)
1455             {
1456                 assertions.set(
1457                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1458                                             upperCriticalGoingHigh));
1459             }
1460             if (criticalLowAlarm)
1461             {
1462                 assertions.set(static_cast<size_t>(
1463                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1464             }
1465         }
1466     }
1467 
1468     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1469 }
1470 
1471 // Construct a type 1 SDR for threshold sensor.
1472 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1473                                  get_sdr::SensorDataFullRecord& record)
1474 {
1475     get_sdr::header::set_record_id(
1476         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1477 
1478     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1479     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1480 
1481     record.header.sdr_version = ipmiSdrVersion;
1482     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1483     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1484                                   sizeof(get_sdr::SensorDataRecordHeader);
1485     record.key.owner_id = bmcI2CAddr;
1486     record.key.owner_lun = lun;
1487     record.key.sensor_number = sensornumber;
1488 }
1489 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1490                         uint16_t recordID, const std::string& service,
1491                         const std::string& path,
1492                         get_sdr::SensorDataFullRecord& record)
1493 {
1494     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1495     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1496 
1497     DbusInterfaceMap sensorMap;
1498     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1499     {
1500         phosphor::logging::log<phosphor::logging::level::ERR>(
1501             "Failed to update sensor map for threshold sensor",
1502             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1503             phosphor::logging::entry("PATH=%s", path.c_str()));
1504         return false;
1505     }
1506 
1507     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1508     record.body.sensor_type = getSensorTypeFromPath(path);
1509     std::string type = getSensorTypeStringFromPath(path);
1510     auto typeCstr = type.c_str();
1511     auto findUnits = sensorUnits.find(typeCstr);
1512     if (findUnits != sensorUnits.end())
1513     {
1514         record.body.sensor_units_2_base =
1515             static_cast<uint8_t>(findUnits->second);
1516     } // else default 0x0 unspecified
1517 
1518     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1519 
1520     auto sensorObject = sensorMap.find(sensor::sensorInterface);
1521     if (sensorObject == sensorMap.end())
1522     {
1523         phosphor::logging::log<phosphor::logging::level::ERR>(
1524             "getSensorDataRecord: sensorObject error");
1525         return false;
1526     }
1527 
1528     uint8_t entityId = 0;
1529     uint8_t entityInstance = 0x01;
1530 
1531     // follow the association chain to get the parent board's entityid and
1532     // entityInstance
1533     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1534 
1535     record.body.entity_id = entityId;
1536     record.body.entity_instance = entityInstance;
1537 
1538     double max = 0;
1539     double min = 0;
1540     getSensorMaxMin(sensorMap, max, min);
1541 
1542     int16_t mValue = 0;
1543     int8_t rExp = 0;
1544     int16_t bValue = 0;
1545     int8_t bExp = 0;
1546     bool bSigned = false;
1547 
1548     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1549     {
1550         phosphor::logging::log<phosphor::logging::level::ERR>(
1551             "getSensorDataRecord: getSensorAttributes error");
1552         return false;
1553     }
1554 
1555     // The record.body is a struct SensorDataFullRecordBody
1556     // from sensorhandler.hpp in phosphor-ipmi-host.
1557     // The meaning of these bits appears to come from
1558     // table 43.1 of the IPMI spec.
1559     // The above 5 sensor attributes are stuffed in as follows:
1560     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1561     // Byte 22-24 are for other purposes
1562     // Byte 25 = MMMMMMMM = LSB of M
1563     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1564     // Byte 27 = BBBBBBBB = LSB of B
1565     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1566     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1567     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1568 
1569     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1570     record.body.m_lsb = mValue & 0xFF;
1571 
1572     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1573     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1574 
1575     // move the smallest bit of the MSB into place (bit 9)
1576     // the MSbs are bits 7:8 in m_msb_and_tolerance
1577     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1578 
1579     record.body.b_lsb = bValue & 0xFF;
1580 
1581     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1582     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1583 
1584     // move the smallest bit of the MSB into place (bit 9)
1585     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1586     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1587 
1588     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1589     uint8_t rExpBits = rExp & 0x07;
1590 
1591     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1592     uint8_t bExpBits = bExp & 0x07;
1593 
1594     // move rExp and bExp into place
1595     record.body.r_b_exponents =
1596         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1597 
1598     // Set the analog reading byte interpretation accordingly
1599     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1600 
1601     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1602     // These seem redundant, but derivable from the above 5 attributes
1603     // Original comment said "todo fill out rest of units"
1604 
1605     // populate sensor name from path
1606     auto name = sensor::parseSdrIdFromPath(path);
1607     record.body.id_string_info = name.size();
1608     std::strncpy(record.body.id_string, name.c_str(),
1609                  sizeof(record.body.id_string));
1610 
1611     // Remember the sensor name, as determined for this sensor number
1612     details::sdrStatsTable.updateName(sensornumber, name);
1613 
1614     bool sensorSettable = false;
1615     auto mutability =
1616         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1617     if (mutability != sensorMap.end())
1618     {
1619         sensorSettable =
1620             mappedVariant<bool>(mutability->second, "Mutable", false);
1621     }
1622     get_sdr::body::init_settable_state(sensorSettable, &record.body);
1623 
1624     // Grant write permission to sensors deemed externally settable
1625     details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
1626 
1627     IPMIThresholds thresholdData;
1628     try
1629     {
1630         thresholdData = getIPMIThresholds(sensorMap);
1631     }
1632     catch (const std::exception&)
1633     {
1634         phosphor::logging::log<phosphor::logging::level::ERR>(
1635             "getSensorDataRecord: getIPMIThresholds error");
1636         return false;
1637     }
1638 
1639     if (thresholdData.criticalHigh)
1640     {
1641         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1642         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1643             IPMISensorEventEnableThresholds::criticalThreshold);
1644         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1645             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1646         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1647             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1648         record.body.discrete_reading_setting_mask[0] |=
1649             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1650     }
1651     if (thresholdData.warningHigh)
1652     {
1653         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1654         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1655             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1656         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1657             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1658         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1659             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1660         record.body.discrete_reading_setting_mask[0] |=
1661             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1662     }
1663     if (thresholdData.criticalLow)
1664     {
1665         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1666         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1667             IPMISensorEventEnableThresholds::criticalThreshold);
1668         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1669             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1670         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1671             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1672         record.body.discrete_reading_setting_mask[0] |=
1673             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1674     }
1675     if (thresholdData.warningLow)
1676     {
1677         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1678         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1679             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1680         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1681             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1682         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1683             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1684         record.body.discrete_reading_setting_mask[0] |=
1685             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1686     }
1687 
1688     // everything that is readable is setable
1689     record.body.discrete_reading_setting_mask[1] =
1690         record.body.discrete_reading_setting_mask[0];
1691     return true;
1692 }
1693 
1694 #ifdef FEATURE_HYBRID_SENSORS
1695 // Construct a type 1 SDR for discrete Sensor typed sensor.
1696 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1697                               uint16_t recordID,
1698                               ipmi::sensor::IdInfoMap::const_iterator sensor,
1699                               get_sdr::SensorDataFullRecord& record)
1700 {
1701     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1702 
1703     record.body.entity_id = sensor->second.entityType;
1704     record.body.sensor_type = sensor->second.sensorType;
1705     record.body.event_reading_type = sensor->second.sensorReadingType;
1706     record.body.entity_instance = sensor->second.instance;
1707     if (ipmi::sensor::Mutability::Write ==
1708         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1709     {
1710         get_sdr::body::init_settable_state(true, &(record.body));
1711     }
1712 
1713     auto id_string = sensor->second.sensorName;
1714 
1715     if (id_string.empty())
1716     {
1717         id_string = sensor->second.sensorNameFunc(sensor->second);
1718     }
1719 
1720     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1721     {
1722         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1723                                      &(record.body));
1724     }
1725     else
1726     {
1727         get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1728     }
1729     std::strncpy(record.body.id_string, id_string.c_str(),
1730                  get_sdr::body::get_id_strlen(&(record.body)));
1731 }
1732 #endif
1733 
1734 // Construct type 3 SDR header and key (for VR and other discrete sensors)
1735 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1736                                 get_sdr::SensorDataEventRecord& record)
1737 {
1738     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1739     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1740 
1741     get_sdr::header::set_record_id(
1742         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1743 
1744     record.header.sdr_version = ipmiSdrVersion;
1745     record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1746     record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1747                                   sizeof(get_sdr::SensorDataRecordHeader);
1748     record.key.owner_id = bmcI2CAddr;
1749     record.key.owner_lun = lun;
1750     record.key.sensor_number = sensornumber;
1751 
1752     record.body.entity_id = 0x00;
1753     record.body.entity_instance = 0x01;
1754 }
1755 
1756 // Construct a type 3 SDR for VR typed sensor(daemon).
1757 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1758                     uint16_t recordID, const std::string& service,
1759                     const std::string& path,
1760                     get_sdr::SensorDataEventRecord& record)
1761 {
1762     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1763     constructEventSdrHeaderKey(sensorNum, recordID, record);
1764 
1765     DbusInterfaceMap sensorMap;
1766     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1767     {
1768         phosphor::logging::log<phosphor::logging::level::ERR>(
1769             "Failed to update sensor map for VR sensor",
1770             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1771             phosphor::logging::entry("PATH=%s", path.c_str()));
1772         return false;
1773     }
1774     // follow the association chain to get the parent board's entityid and
1775     // entityInstance
1776     updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1777                               record.body.entity_instance);
1778 
1779     // Sensor type is hardcoded as a module/board type instead of parsing from
1780     // sensor path. This is because VR control is allocated in an independent
1781     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1782     // types.
1783     static constexpr const uint8_t module_board_type = 0x15;
1784     record.body.sensor_type = module_board_type;
1785     record.body.event_reading_type = 0x00;
1786 
1787     record.body.sensor_record_sharing_1 = 0x00;
1788     record.body.sensor_record_sharing_2 = 0x00;
1789 
1790     // populate sensor name from path
1791     auto name = sensor::parseSdrIdFromPath(path);
1792     int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1793     record.body.id_string_info = nameSize;
1794     std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1795     std::memcpy(record.body.id_string, name.c_str(), nameSize);
1796 
1797     // Remember the sensor name, as determined for this sensor number
1798     details::sdrStatsTable.updateName(sensornumber, name);
1799 
1800     return true;
1801 }
1802 
1803 static int
1804     getSensorDataRecord(ipmi::Context::ptr ctx,
1805                         std::vector<uint8_t>& recordData, uint16_t recordID,
1806                         uint8_t readBytes = std::numeric_limits<uint8_t>::max())
1807 {
1808     size_t fruCount = 0;
1809     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1810     if (ret != ipmi::ccSuccess)
1811     {
1812         phosphor::logging::log<phosphor::logging::level::ERR>(
1813             "getSensorDataRecord: getFruSdrCount error");
1814         return GENERAL_ERROR;
1815     }
1816 
1817     auto& sensorTree = getSensorTree();
1818     size_t lastRecord =
1819         sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1820     if (recordID == lastRecordIndex)
1821     {
1822         recordID = lastRecord;
1823     }
1824     if (recordID > lastRecord)
1825     {
1826         phosphor::logging::log<phosphor::logging::level::ERR>(
1827             "getSensorDataRecord: recordID > lastRecord error");
1828         return GENERAL_ERROR;
1829     }
1830 
1831     if (recordID >= sensorTree.size())
1832     {
1833         size_t fruIndex = recordID - sensorTree.size();
1834 
1835         if (fruIndex >= fruCount)
1836         {
1837             // handle type 12 hardcoded records
1838             size_t type12Index = fruIndex - fruCount;
1839             if (type12Index >= ipmi::storage::type12Count)
1840             {
1841                 phosphor::logging::log<phosphor::logging::level::ERR>(
1842                     "getSensorDataRecord: type12Index error");
1843                 return GENERAL_ERROR;
1844             }
1845             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1846         }
1847         else
1848         {
1849             // handle fru records
1850             get_sdr::SensorDataFruRecord data;
1851             ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1852             if (ret != IPMI_CC_OK)
1853             {
1854                 return GENERAL_ERROR;
1855             }
1856             data.header.record_id_msb = recordID >> 8;
1857             data.header.record_id_lsb = recordID & 0xFF;
1858             recordData.insert(recordData.end(), (uint8_t*)&data,
1859                               ((uint8_t*)&data) + sizeof(data));
1860         }
1861 
1862         return 0;
1863     }
1864 
1865     std::string connection;
1866     std::string path;
1867     std::vector<std::string> interfaces;
1868 
1869     auto status =
1870         getSensorConnection(ctx, recordID, connection, path, &interfaces);
1871     if (status)
1872     {
1873         phosphor::logging::log<phosphor::logging::level::ERR>(
1874             "getSensorDataRecord: getSensorConnection error");
1875         return GENERAL_ERROR;
1876     }
1877     uint16_t sensorNum = getSensorNumberFromPath(path);
1878     if (sensorNum == invalidSensorNumber)
1879     {
1880         phosphor::logging::log<phosphor::logging::level::ERR>(
1881             "getSensorDataRecord: invalidSensorNumber");
1882         return GENERAL_ERROR;
1883     }
1884 
1885     // Construct full record (SDR type 1) for the threshold sensors
1886     if (std::find(interfaces.begin(), interfaces.end(),
1887                   sensor::sensorInterface) != interfaces.end())
1888     {
1889         get_sdr::SensorDataFullRecord record = {0};
1890 
1891         // If the request doesn't read SDR body, construct only header and key
1892         // part to avoid additional DBus transaction.
1893         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1894         {
1895             constructSensorSdrHeaderKey(sensorNum, recordID, record);
1896         }
1897         else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1898                                      record))
1899         {
1900             return GENERAL_ERROR;
1901         }
1902 
1903         recordData.insert(recordData.end(), (uint8_t*)&record,
1904                           ((uint8_t*)&record) + sizeof(record));
1905 
1906         return 0;
1907     }
1908 
1909 #ifdef FEATURE_HYBRID_SENSORS
1910     if (auto sensor = findStaticSensor(path);
1911         sensor != ipmi::sensor::sensors.end() &&
1912         getSensorEventTypeFromPath(path) !=
1913             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1914     {
1915         get_sdr::SensorDataFullRecord record = {0};
1916 
1917         // If the request doesn't read SDR body, construct only header and key
1918         // part to avoid additional DBus transaction.
1919         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1920         {
1921             constructSensorSdrHeaderKey(sensorNum, recordID, record);
1922         }
1923         else
1924         {
1925             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1926         }
1927 
1928         recordData.insert(recordData.end(), (uint8_t*)&record,
1929                           ((uint8_t*)&record) + sizeof(record));
1930 
1931         return 0;
1932     }
1933 #endif
1934 
1935     // Contruct SDR type 3 record for VR sensor (daemon)
1936     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1937         interfaces.end())
1938     {
1939         get_sdr::SensorDataEventRecord record = {0};
1940 
1941         // If the request doesn't read SDR body, construct only header and key
1942         // part to avoid additional DBus transaction.
1943         if (readBytes <= sizeof(record.header) + sizeof(record.key))
1944         {
1945             constructEventSdrHeaderKey(sensorNum, recordID, record);
1946         }
1947         else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1948                                  record))
1949         {
1950             return GENERAL_ERROR;
1951         }
1952         recordData.insert(recordData.end(), (uint8_t*)&record,
1953                           ((uint8_t*)&record) + sizeof(record));
1954     }
1955 
1956     return 0;
1957 }
1958 
1959 /** @brief implements the get SDR Info command
1960  *  @param count - Operation
1961  *
1962  *  @returns IPMI completion code plus response data
1963  *   - sdrCount - sensor/SDR count
1964  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1965  */
1966 static ipmi::RspType<uint8_t, // respcount
1967                      uint8_t, // dynamic population flags
1968                      uint32_t // last time a sensor was added
1969                      >
1970     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1971                                std::optional<uint8_t> count)
1972 {
1973     auto& sensorTree = getSensorTree();
1974     uint8_t sdrCount = 0;
1975     uint16_t recordID = 0;
1976     std::vector<uint8_t> record;
1977     // Sensors are dynamically allocated, and there is at least one LUN
1978     uint8_t lunsAndDynamicPopulation = 0x80;
1979     constexpr uint8_t getSdrCount = 0x01;
1980     constexpr uint8_t getSensorCount = 0x00;
1981 
1982     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1983     {
1984         return ipmi::responseResponseError();
1985     }
1986     uint16_t numSensors = sensorTree.size();
1987     if (count.value_or(0) == getSdrCount)
1988     {
1989         // Count the number of Type 1 SDR entries assigned to the LUN
1990         while (!getSensorDataRecord(ctx, record, recordID++))
1991         {
1992             get_sdr::SensorDataRecordHeader* hdr =
1993                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1994                     record.data());
1995             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1996             {
1997                 get_sdr::SensorDataFullRecord* recordData =
1998                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
1999                         record.data());
2000                 if (ctx->lun == recordData->key.owner_lun)
2001                 {
2002                     sdrCount++;
2003                 }
2004             }
2005         }
2006     }
2007     else if (count.value_or(0) == getSensorCount)
2008     {
2009         // Return the number of sensors attached to the LUN
2010         if ((ctx->lun == 0) && (numSensors > 0))
2011         {
2012             sdrCount =
2013                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2014         }
2015         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2016         {
2017             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2018                            ? maxSensorsPerLUN
2019                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2020         }
2021         else if (ctx->lun == 3)
2022         {
2023             if (numSensors <= maxIPMISensors)
2024             {
2025                 sdrCount =
2026                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2027             }
2028             else
2029             {
2030                 // error
2031                 throw std::out_of_range(
2032                     "Maximum number of IPMI sensors exceeded.");
2033             }
2034         }
2035     }
2036     else
2037     {
2038         return ipmi::responseInvalidFieldRequest();
2039     }
2040 
2041     // Get Sensor count. This returns the number of sensors
2042     if (numSensors > 0)
2043     {
2044         lunsAndDynamicPopulation |= 1;
2045     }
2046     if (numSensors > maxSensorsPerLUN)
2047     {
2048         lunsAndDynamicPopulation |= 2;
2049     }
2050     if (numSensors >= (maxSensorsPerLUN * 2))
2051     {
2052         lunsAndDynamicPopulation |= 8;
2053     }
2054     if (numSensors > maxIPMISensors)
2055     {
2056         // error
2057         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2058     }
2059 
2060     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2061                                  sdrLastAdd);
2062 }
2063 
2064 /* end sensor commands */
2065 
2066 /* storage commands */
2067 
2068 ipmi::RspType<uint8_t,  // sdr version
2069               uint16_t, // record count
2070               uint16_t, // free space
2071               uint32_t, // most recent addition
2072               uint32_t, // most recent erase
2073               uint8_t   // operationSupport
2074               >
2075     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2076 {
2077     auto& sensorTree = getSensorTree();
2078     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2079     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2080     {
2081         return ipmi::responseResponseError();
2082     }
2083 
2084     size_t fruCount = 0;
2085     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2086     if (ret != ipmi::ccSuccess)
2087     {
2088         return ipmi::response(ret);
2089     }
2090 
2091     uint16_t recordCount =
2092         sensorTree.size() + fruCount + ipmi::storage::type12Count;
2093 
2094     uint8_t operationSupport = static_cast<uint8_t>(
2095         SdrRepositoryInfoOps::overflow); // write not supported
2096 
2097     operationSupport |=
2098         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2099     operationSupport |= static_cast<uint8_t>(
2100         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2101     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2102                                  unspecifiedFreeSpace, sdrLastAdd,
2103                                  sdrLastRemove, operationSupport);
2104 }
2105 
2106 /** @brief implements the get SDR allocation info command
2107  *
2108  *  @returns IPMI completion code plus response data
2109  *   - allocUnits    - Number of possible allocation units
2110  *   - allocUnitSize - Allocation unit size in bytes.
2111  *   - allocUnitFree - Number of free allocation units
2112  *   - allocUnitLargestFree - Largest free block in allocation units
2113  *   - maxRecordSize    - Maximum record size in allocation units.
2114  */
2115 ipmi::RspType<uint16_t, // allocUnits
2116               uint16_t, // allocUnitSize
2117               uint16_t, // allocUnitFree
2118               uint16_t, // allocUnitLargestFree
2119               uint8_t   // maxRecordSize
2120               >
2121     ipmiStorageGetSDRAllocationInfo()
2122 {
2123     // 0000h unspecified number of alloc units
2124     constexpr uint16_t allocUnits = 0;
2125 
2126     constexpr uint16_t allocUnitFree = 0;
2127     constexpr uint16_t allocUnitLargestFree = 0;
2128     // only allow one block at a time
2129     constexpr uint8_t maxRecordSize = 1;
2130 
2131     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2132                                  allocUnitLargestFree, maxRecordSize);
2133 }
2134 
2135 /** @brief implements the reserve SDR command
2136  *  @returns IPMI completion code plus response data
2137  *   - sdrReservationID
2138  */
2139 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2140 {
2141     sdrReservationID++;
2142     if (sdrReservationID == 0)
2143     {
2144         sdrReservationID++;
2145     }
2146 
2147     return ipmi::responseSuccess(sdrReservationID);
2148 }
2149 
2150 ipmi::RspType<uint16_t,            // next record ID
2151               std::vector<uint8_t> // payload
2152               >
2153     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2154                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2155 {
2156     size_t fruCount = 0;
2157     // reservation required for partial reads with non zero offset into
2158     // record
2159     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2160     {
2161         phosphor::logging::log<phosphor::logging::level::ERR>(
2162             "ipmiStorageGetSDR: responseInvalidReservationId");
2163         return ipmi::responseInvalidReservationId();
2164     }
2165     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2166     if (ret != ipmi::ccSuccess)
2167     {
2168         phosphor::logging::log<phosphor::logging::level::ERR>(
2169             "ipmiStorageGetSDR: getFruSdrCount error");
2170         return ipmi::response(ret);
2171     }
2172 
2173     auto& sensorTree = getSensorTree();
2174     size_t lastRecord =
2175         sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
2176     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2177 
2178     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2179     {
2180         phosphor::logging::log<phosphor::logging::level::ERR>(
2181             "ipmiStorageGetSDR: getSensorSubtree error");
2182         return ipmi::responseResponseError();
2183     }
2184 
2185     std::vector<uint8_t> record;
2186     if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
2187     {
2188         phosphor::logging::log<phosphor::logging::level::ERR>(
2189             "ipmiStorageGetSDR: fail to get SDR");
2190         return ipmi::responseInvalidFieldRequest();
2191     }
2192     get_sdr::SensorDataRecordHeader* hdr =
2193         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2194     if (!hdr)
2195     {
2196         phosphor::logging::log<phosphor::logging::level::ERR>(
2197             "ipmiStorageGetSDR: record header is null");
2198         return ipmi::responseSuccess(nextRecordId, record);
2199     }
2200 
2201     size_t sdrLength =
2202         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2203     if (sdrLength < (offset + bytesToRead))
2204     {
2205         bytesToRead = sdrLength - offset;
2206     }
2207 
2208     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2209     if (!respStart)
2210     {
2211         phosphor::logging::log<phosphor::logging::level::ERR>(
2212             "ipmiStorageGetSDR: record is null");
2213         return ipmi::responseSuccess(nextRecordId, record);
2214     }
2215 
2216     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2217 
2218     return ipmi::responseSuccess(nextRecordId, recordData);
2219 }
2220 /* end storage commands */
2221 
2222 void registerSensorFunctions()
2223 {
2224     // <Platform Event>
2225     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2226                           ipmi::sensor_event::cmdPlatformEvent,
2227                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2228 
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 
2234     // <Get Sensor Reading>
2235     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2236                           ipmi::sensor_event::cmdGetSensorReading,
2237                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2238 
2239     // <Get Sensor Threshold>
2240     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2241                           ipmi::sensor_event::cmdGetSensorThreshold,
2242                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2243 
2244     // <Set Sensor Threshold>
2245     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2246                           ipmi::sensor_event::cmdSetSensorThreshold,
2247                           ipmi::Privilege::Operator,
2248                           ipmiSenSetSensorThresholds);
2249 
2250     // <Get Sensor Event Enable>
2251     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2252                           ipmi::sensor_event::cmdGetSensorEventEnable,
2253                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2254 
2255     // <Get Sensor Event Status>
2256     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2257                           ipmi::sensor_event::cmdGetSensorEventStatus,
2258                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2259 
2260     // register all storage commands for both Sensor and Storage command
2261     // versions
2262 
2263     // <Get SDR Repository Info>
2264     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2265                           ipmi::storage::cmdGetSdrRepositoryInfo,
2266                           ipmi::Privilege::User,
2267                           ipmiStorageGetSDRRepositoryInfo);
2268 
2269     // <Get Device SDR Info>
2270     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2271                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2272                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2273 
2274     // <Get SDR Allocation Info>
2275     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2276                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2277                           ipmi::Privilege::User,
2278                           ipmiStorageGetSDRAllocationInfo);
2279 
2280     // <Reserve SDR Repo>
2281     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2282                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2283                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2284 
2285     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2286                           ipmi::storage::cmdReserveSdrRepository,
2287                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2288 
2289     // <Get Sdr>
2290     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2291                           ipmi::sensor_event::cmdGetDeviceSdr,
2292                           ipmi::Privilege::User, ipmiStorageGetSDR);
2293 
2294     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2295                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2296                           ipmiStorageGetSDR);
2297 }
2298 } // namespace ipmi
2299