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