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