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