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 "sensorcommands.hpp"
18 
19 #include "commandutils.hpp"
20 #include "ipmi_to_redfish_hooks.hpp"
21 #include "sdrutils.hpp"
22 #include "sensorutils.hpp"
23 #include "storagecommands.hpp"
24 
25 #include <algorithm>
26 #include <array>
27 #include <boost/algorithm/string.hpp>
28 #include <boost/container/flat_map.hpp>
29 #include <chrono>
30 #include <cmath>
31 #include <cstring>
32 #include <iostream>
33 #include <ipmid/api.hpp>
34 #include <ipmid/utils.hpp>
35 #include <map>
36 #include <memory>
37 #include <optional>
38 #include <phosphor-logging/log.hpp>
39 #include <sdbusplus/bus.hpp>
40 #include <stdexcept>
41 #include <string>
42 #include <utility>
43 #include <variant>
44 
45 namespace ipmi
46 {
47 using ManagedObjectType =
48     std::map<sdbusplus::message::object_path,
49              std::map<std::string, std::map<std::string, DbusVariant>>>;
50 
51 static constexpr int sensorListUpdatePeriod = 10;
52 static constexpr int sensorMapUpdatePeriod = 2;
53 
54 constexpr size_t maxSDRTotalSize =
55     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
56 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
57 
58 static uint16_t sdrReservationID;
59 static uint32_t sdrLastAdd = noTimestamp;
60 static uint32_t sdrLastRemove = noTimestamp;
61 
62 SensorSubTree sensorTree;
63 static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
64 
65 // Specify the comparison required to sort and find char* map objects
66 struct CmpStr
67 {
68     bool operator()(const char *a, const char *b) const
69     {
70         return std::strcmp(a, b) < 0;
71     }
72 };
73 const static boost::container::flat_map<const char *, SensorUnits, CmpStr>
74     sensorUnits{{{"temperature", SensorUnits::degreesC},
75                  {"voltage", SensorUnits::volts},
76                  {"current", SensorUnits::amps},
77                  {"fan_tach", SensorUnits::rpm},
78                  {"power", SensorUnits::watts}}};
79 
80 void registerSensorFunctions() __attribute__((constructor));
81 
82 static sdbusplus::bus::match::match sensorAdded(
83     *getSdBus(),
84     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
85     "sensors/'",
86     [](sdbusplus::message::message &m) {
87         sensorTree.clear();
88         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
89                          std::chrono::system_clock::now().time_since_epoch())
90                          .count();
91     });
92 
93 static sdbusplus::bus::match::match sensorRemoved(
94     *getSdBus(),
95     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
96     "sensors/'",
97     [](sdbusplus::message::message &m) {
98         sensorTree.clear();
99         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
100                             std::chrono::system_clock::now().time_since_epoch())
101                             .count();
102     });
103 
104 // this keeps track of deassertions for sensor event status command. A
105 // deasertion can only happen if an assertion was seen first.
106 static boost::container::flat_map<
107     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
108     thresholdDeassertMap;
109 
110 static sdbusplus::bus::match::match thresholdChanged(
111     *getSdBus(),
112     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
113     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
114     [](sdbusplus::message::message &m) {
115         boost::container::flat_map<std::string, std::variant<bool, double>>
116             values;
117         m.read(std::string(), values);
118 
119         auto findAssert =
120             std::find_if(values.begin(), values.end(), [](const auto &pair) {
121                 return pair.first.find("Alarm") != std::string::npos;
122             });
123         if (findAssert != values.end())
124         {
125             auto ptr = std::get_if<bool>(&(findAssert->second));
126             if (ptr == nullptr)
127             {
128                 phosphor::logging::log<phosphor::logging::level::ERR>(
129                     "thresholdChanged: Assert non bool");
130                 return;
131             }
132             if (*ptr)
133             {
134                 phosphor::logging::log<phosphor::logging::level::INFO>(
135                     "thresholdChanged: Assert",
136                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
137                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
138             }
139             else
140             {
141                 auto &value =
142                     thresholdDeassertMap[m.get_path()][findAssert->first];
143                 if (value)
144                 {
145                     phosphor::logging::log<phosphor::logging::level::INFO>(
146                         "thresholdChanged: deassert",
147                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
148                     value = *ptr;
149                 }
150             }
151         }
152     });
153 
154 static void getSensorMaxMin(const SensorMap &sensorMap, double &max,
155                             double &min)
156 {
157     max = 127;
158     min = -128;
159 
160     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
161     auto critical =
162         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
163     auto warning =
164         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
165 
166     if (sensorObject != sensorMap.end())
167     {
168         auto maxMap = sensorObject->second.find("MaxValue");
169         auto minMap = sensorObject->second.find("MinValue");
170 
171         if (maxMap != sensorObject->second.end())
172         {
173             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
174         }
175         if (minMap != sensorObject->second.end())
176         {
177             min = std::visit(VariantToDoubleVisitor(), minMap->second);
178         }
179     }
180     if (critical != sensorMap.end())
181     {
182         auto lower = critical->second.find("CriticalLow");
183         auto upper = critical->second.find("CriticalHigh");
184         if (lower != critical->second.end())
185         {
186             double value = std::visit(VariantToDoubleVisitor(), lower->second);
187             min = std::min(value, min);
188         }
189         if (upper != critical->second.end())
190         {
191             double value = std::visit(VariantToDoubleVisitor(), upper->second);
192             max = std::max(value, max);
193         }
194     }
195     if (warning != sensorMap.end())
196     {
197 
198         auto lower = warning->second.find("WarningLow");
199         auto upper = warning->second.find("WarningHigh");
200         if (lower != warning->second.end())
201         {
202             double value = std::visit(VariantToDoubleVisitor(), lower->second);
203             min = std::min(value, min);
204         }
205         if (upper != warning->second.end())
206         {
207             double value = std::visit(VariantToDoubleVisitor(), upper->second);
208             max = std::max(value, max);
209         }
210     }
211 }
212 
213 static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
214                          SensorMap &sensorMap)
215 {
216     static boost::container::flat_map<
217         std::string, std::chrono::time_point<std::chrono::steady_clock>>
218         updateTimeMap;
219 
220     auto updateFind = updateTimeMap.find(sensorConnection);
221     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
222     if (updateFind != updateTimeMap.end())
223     {
224         lastUpdate = updateFind->second;
225     }
226 
227     auto now = std::chrono::steady_clock::now();
228 
229     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
230             .count() > sensorMapUpdatePeriod)
231     {
232         updateTimeMap[sensorConnection] = now;
233 
234         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
235         auto managedObj = dbus->new_method_call(
236             sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
237             "GetManagedObjects");
238 
239         ManagedObjectType managedObjects;
240         try
241         {
242             auto reply = dbus->call(managedObj);
243             reply.read(managedObjects);
244         }
245         catch (sdbusplus::exception_t &)
246         {
247             phosphor::logging::log<phosphor::logging::level::ERR>(
248                 "Error getting managed objects from connection",
249                 phosphor::logging::entry("CONNECTION=%s",
250                                          sensorConnection.c_str()));
251             return false;
252         }
253 
254         SensorCache[sensorConnection] = managedObjects;
255     }
256     auto connection = SensorCache.find(sensorConnection);
257     if (connection == SensorCache.end())
258     {
259         return false;
260     }
261     auto path = connection->second.find(sensorPath);
262     if (path == connection->second.end())
263     {
264         return false;
265     }
266     sensorMap = path->second;
267 
268     return true;
269 }
270 
271 /* sensor commands */
272 ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
273                                      ipmi_request_t request,
274                                      ipmi_response_t response,
275                                      ipmi_data_len_t dataLen,
276                                      ipmi_context_t context)
277 {
278     *dataLen = 0;
279     printCommand(+netfn, +cmd);
280     return IPMI_CC_INVALID;
281 }
282 
283 namespace meHealth
284 {
285 constexpr const char *busname = "xyz.openbmc_project.NodeManagerProxy";
286 constexpr const char *path = "/xyz/openbmc_project/status/me";
287 constexpr const char *interface = "xyz.openbmc_project.SetHealth";
288 constexpr const char *method = "SetHealth";
289 constexpr const char *critical = "critical";
290 constexpr const char *warning = "warning";
291 constexpr const char *ok = "ok";
292 } // namespace meHealth
293 
294 static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
295 {
296     constexpr const std::array<uint8_t, 10> critical = {
297         0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
298     constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
299                                                       0x1A};
300 
301     std::string state;
302     if (std::find(critical.begin(), critical.end(), eventData2) !=
303         critical.end())
304     {
305         state = meHealth::critical;
306     }
307     // special case 0x3 as we only care about a few states
308     else if (eventData2 == 0x3)
309     {
310         if (eventData3 <= 0x2)
311         {
312             state = meHealth::warning;
313         }
314         else
315         {
316             return;
317         }
318     }
319     else if (std::find(warning.begin(), warning.end(), eventData2) !=
320              warning.end())
321     {
322         state = meHealth::warning;
323     }
324     else
325     {
326         return;
327     }
328     if (disable)
329     {
330         state = meHealth::ok;
331     }
332 
333     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
334     auto setHealth =
335         dbus->new_method_call(meHealth::busname, meHealth::path,
336                               meHealth::interface, meHealth::method);
337     setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
338     try
339     {
340         dbus->call(setHealth);
341     }
342     catch (sdbusplus::exception_t &)
343     {
344         phosphor::logging::log<phosphor::logging::level::ERR>(
345             "Failed to set ME Health");
346     }
347 }
348 
349 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p)
350 {
351     constexpr const uint8_t meId = 0x2C;
352     constexpr const uint8_t meSensorNum = 0x17;
353     constexpr const uint8_t disabled = 0x80;
354 
355     uint8_t generatorID = 0;
356     uint8_t evmRev = 0;
357     uint8_t sensorType = 0;
358     uint8_t sensorNum = 0;
359     uint8_t eventType = 0;
360     uint8_t eventData1 = 0;
361     std::optional<uint8_t> eventData2 = 0;
362     std::optional<uint8_t> eventData3 = 0;
363 
364     // todo: This check is supposed to be based on the incoming channel.
365     //      e.g. system channel will provide upto 8 bytes including generator
366     //      ID, but ipmb channel will provide only up to 7 bytes without the
367     //      generator ID.
368     // Support for this check is coming in future patches, so for now just base
369     // it on if the first byte is the EvMRev (0x04).
370     if (p.size() && p.data()[0] == 0x04)
371     {
372         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
373                  eventData2, eventData3);
374         // todo: the generator ID for this channel is supposed to come from the
375         // IPMB requesters slave address. Support for this is coming in future
376         // patches, so for now just assume it is coming from the ME (0x2C).
377         generatorID = 0x2C;
378     }
379     else
380     {
381         p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
382                  eventData1, eventData2, eventData3);
383     }
384     if (!p.fullyUnpacked())
385     {
386         return ipmi::responseReqDataLenInvalid();
387     }
388 
389     // Send this request to the Redfish hooks to log it as a Redfish message
390     // instead.  There is no need to add it to the SEL, so just return success.
391     intel_oem::ipmi::sel::checkRedfishHooks(
392         generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
393         eventData2.value_or(0xFF), eventData3.value_or(0xFF));
394 
395     if (generatorID == meId && sensorNum == meSensorNum && eventData2 &&
396         eventData3)
397     {
398         setMeStatus(*eventData2, *eventData3, (eventType & disabled));
399     }
400 
401     return ipmi::responseSuccess();
402 }
403 
404 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
405     ipmiSenGetSensorReading(uint8_t sensnum)
406 {
407     std::string connection;
408     std::string path;
409 
410     auto status = getSensorConnection(sensnum, connection, path);
411     if (status)
412     {
413         return ipmi::response(status);
414     }
415 
416     SensorMap sensorMap;
417     if (!getSensorMap(connection, path, sensorMap))
418     {
419         return ipmi::responseResponseError();
420     }
421     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
422 
423     if (sensorObject == sensorMap.end() ||
424         sensorObject->second.find("Value") == sensorObject->second.end())
425     {
426         return ipmi::responseResponseError();
427     }
428     auto &valueVariant = sensorObject->second["Value"];
429     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
430 
431     double max = 0;
432     double min = 0;
433     getSensorMaxMin(sensorMap, max, min);
434 
435     int16_t mValue = 0;
436     int16_t bValue = 0;
437     int8_t rExp = 0;
438     int8_t bExp = 0;
439     bool bSigned = false;
440 
441     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
442     {
443         return ipmi::responseResponseError();
444     }
445 
446     uint8_t value =
447         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
448     uint8_t operation =
449         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
450     operation |=
451         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
452 
453     uint8_t thresholds = 0;
454 
455     auto warningObject =
456         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
457     if (warningObject != sensorMap.end())
458     {
459         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
460         auto alarmLow = warningObject->second.find("WarningAlarmLow");
461         if (alarmHigh != warningObject->second.end())
462         {
463             if (std::get<bool>(alarmHigh->second))
464             {
465                 thresholds |= static_cast<uint8_t>(
466                     IPMISensorReadingByte3::upperNonCritical);
467             }
468         }
469         if (alarmLow != warningObject->second.end())
470         {
471             if (std::get<bool>(alarmLow->second))
472             {
473                 thresholds |= static_cast<uint8_t>(
474                     IPMISensorReadingByte3::lowerNonCritical);
475             }
476         }
477     }
478 
479     auto criticalObject =
480         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
481     if (criticalObject != sensorMap.end())
482     {
483         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
484         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
485         if (alarmHigh != criticalObject->second.end())
486         {
487             if (std::get<bool>(alarmHigh->second))
488             {
489                 thresholds |=
490                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
491             }
492         }
493         if (alarmLow != criticalObject->second.end())
494         {
495             if (std::get<bool>(alarmLow->second))
496             {
497                 thresholds |=
498                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
499             }
500         }
501     }
502 
503     // no discrete as of today so optional byte is never returned
504     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
505 }
506 
507 /** @brief implements the Set Sensor threshold command
508  *  @param sensorNumber        - sensor number
509  *  @param lowerNonCriticalThreshMask
510  *  @param lowerCriticalThreshMask
511  *  @param lowerNonRecovThreshMask
512  *  @param upperNonCriticalThreshMask
513  *  @param upperCriticalThreshMask
514  *  @param upperNonRecovThreshMask
515  *  @param reserved
516  *  @param lowerNonCritical    - lower non-critical threshold
517  *  @param lowerCritical       - Lower critical threshold
518  *  @param lowerNonRecoverable - Lower non recovarable threshold
519  *  @param upperNonCritical    - Upper non-critical threshold
520  *  @param upperCritical       - Upper critical
521  *  @param upperNonRecoverable - Upper Non-recoverable
522  *
523  *  @returns IPMI completion code
524  */
525 ipmi::RspType<> ipmiSenSetSensorThresholds(
526     uint8_t sensorNum, bool lowerNonCriticalThreshMask,
527     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
528     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
529     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
530     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
531     uint8_t upperNonCritical, uint8_t upperCritical,
532     uint8_t upperNonRecoverable)
533 {
534     constexpr uint8_t thresholdMask = 0xFF;
535 
536     if (reserved)
537     {
538         return ipmi::responseInvalidFieldRequest();
539     }
540 
541     // lower nc and upper nc not suppported on any sensor
542     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
543     {
544         return ipmi::responseInvalidFieldRequest();
545     }
546 
547     // if none of the threshold mask are set, nothing to do
548     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
549           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
550           upperCriticalThreshMask | upperNonRecovThreshMask))
551     {
552         return ipmi::responseSuccess();
553     }
554 
555     std::string connection;
556     std::string path;
557 
558     ipmi::Cc status = getSensorConnection(sensorNum, connection, path);
559     if (status)
560     {
561         return ipmi::response(status);
562     }
563     SensorMap sensorMap;
564     if (!getSensorMap(connection, path, sensorMap))
565     {
566         return ipmi::responseResponseError();
567     }
568 
569     double max = 0;
570     double min = 0;
571     getSensorMaxMin(sensorMap, max, min);
572 
573     int16_t mValue = 0;
574     int16_t bValue = 0;
575     int8_t rExp = 0;
576     int8_t bExp = 0;
577     bool bSigned = false;
578 
579     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
580     {
581         return ipmi::responseResponseError();
582     }
583 
584     // store a vector of property name, value to set, and interface
585     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
586 
587     // define the indexes of the tuple
588     constexpr uint8_t propertyName = 0;
589     constexpr uint8_t thresholdValue = 1;
590     constexpr uint8_t interface = 2;
591     // verifiy all needed fields are present
592     if (lowerCriticalThreshMask || upperCriticalThreshMask)
593     {
594         auto findThreshold =
595             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
596         if (findThreshold == sensorMap.end())
597         {
598             return ipmi::responseInvalidFieldRequest();
599         }
600         if (lowerCriticalThreshMask)
601         {
602             auto findLower = findThreshold->second.find("CriticalLow");
603             if (findLower == findThreshold->second.end())
604             {
605                 return ipmi::responseInvalidFieldRequest();
606             }
607             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
608                                          findThreshold->first);
609         }
610         if (upperCriticalThreshMask)
611         {
612             auto findUpper = findThreshold->second.find("CriticalHigh");
613             if (findUpper == findThreshold->second.end())
614             {
615                 return ipmi::responseInvalidFieldRequest();
616             }
617             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
618                                          findThreshold->first);
619         }
620     }
621     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
622     {
623         auto findThreshold =
624             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
625         if (findThreshold == sensorMap.end())
626         {
627             return ipmi::responseInvalidFieldRequest();
628         }
629         if (lowerNonCriticalThreshMask)
630         {
631             auto findLower = findThreshold->second.find("WarningLow");
632             if (findLower == findThreshold->second.end())
633             {
634                 return ipmi::responseInvalidFieldRequest();
635             }
636             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
637                                          findThreshold->first);
638         }
639         if (upperNonCriticalThreshMask)
640         {
641             auto findUpper = findThreshold->second.find("WarningHigh");
642             if (findUpper == findThreshold->second.end())
643             {
644                 return ipmi::responseInvalidFieldRequest();
645             }
646             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
647                                          findThreshold->first);
648         }
649     }
650     for (const auto &property : thresholdsToSet)
651     {
652         // from section 36.3 in the IPMI Spec, assume all linear
653         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
654                              (bValue * std::pow(10, bExp))) *
655                             std::pow(10, rExp);
656         setDbusProperty(
657             *getSdBus(), connection, path, std::get<interface>(property),
658             std::get<propertyName>(property), ipmi::Value(valueToSet));
659     }
660     return ipmi::responseSuccess();
661 }
662 
663 IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap)
664 {
665     IPMIThresholds resp;
666     auto warningInterface =
667         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
668     auto criticalInterface =
669         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
670 
671     if ((warningInterface != sensorMap.end()) ||
672         (criticalInterface != sensorMap.end()))
673     {
674         auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
675 
676         if (sensorPair == sensorMap.end())
677         {
678             // should not have been able to find a sensor not implementing
679             // the sensor object
680             throw std::runtime_error("Invalid sensor map");
681         }
682 
683         double max = 0;
684         double min = 0;
685         getSensorMaxMin(sensorMap, max, min);
686 
687         int16_t mValue = 0;
688         int16_t bValue = 0;
689         int8_t rExp = 0;
690         int8_t bExp = 0;
691         bool bSigned = false;
692 
693         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
694         {
695             throw std::runtime_error("Invalid sensor atrributes");
696         }
697         if (warningInterface != sensorMap.end())
698         {
699             auto &warningMap = warningInterface->second;
700 
701             auto warningHigh = warningMap.find("WarningHigh");
702             auto warningLow = warningMap.find("WarningLow");
703 
704             if (warningHigh != warningMap.end())
705             {
706 
707                 double value =
708                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
709                 resp.warningHigh = scaleIPMIValueFromDouble(
710                     value, mValue, rExp, bValue, bExp, bSigned);
711             }
712             if (warningLow != warningMap.end())
713             {
714                 double value =
715                     std::visit(VariantToDoubleVisitor(), warningLow->second);
716                 resp.warningLow = scaleIPMIValueFromDouble(
717                     value, mValue, rExp, bValue, bExp, bSigned);
718             }
719         }
720         if (criticalInterface != sensorMap.end())
721         {
722             auto &criticalMap = criticalInterface->second;
723 
724             auto criticalHigh = criticalMap.find("CriticalHigh");
725             auto criticalLow = criticalMap.find("CriticalLow");
726 
727             if (criticalHigh != criticalMap.end())
728             {
729                 double value =
730                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
731                 resp.criticalHigh = scaleIPMIValueFromDouble(
732                     value, mValue, rExp, bValue, bExp, bSigned);
733             }
734             if (criticalLow != criticalMap.end())
735             {
736                 double value =
737                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
738                 resp.criticalLow = scaleIPMIValueFromDouble(
739                     value, mValue, rExp, bValue, bExp, bSigned);
740             }
741         }
742     }
743     return resp;
744 }
745 
746 ipmi::RspType<uint8_t, // readable
747               uint8_t, // lowerNCrit
748               uint8_t, // lowerCrit
749               uint8_t, // lowerNrecoverable
750               uint8_t, // upperNC
751               uint8_t, // upperCrit
752               uint8_t> // upperNRecoverable
753     ipmiSenGetSensorThresholds(uint8_t sensorNumber)
754 {
755     std::string connection;
756     std::string path;
757 
758     auto status = getSensorConnection(sensorNumber, connection, path);
759     if (status)
760     {
761         return ipmi::response(status);
762     }
763 
764     SensorMap sensorMap;
765     if (!getSensorMap(connection, path, sensorMap))
766     {
767         return ipmi::responseResponseError();
768     }
769 
770     IPMIThresholds thresholdData;
771     try
772     {
773         thresholdData = getIPMIThresholds(sensorMap);
774     }
775     catch (std::exception &)
776     {
777         return ipmi::responseResponseError();
778     }
779 
780     uint8_t readable = 0;
781     uint8_t lowerNC = 0;
782     uint8_t lowerCritical = 0;
783     uint8_t lowerNonRecoverable = 0;
784     uint8_t upperNC = 0;
785     uint8_t upperCritical = 0;
786     uint8_t upperNonRecoverable = 0;
787 
788     if (thresholdData.warningHigh)
789     {
790         readable |=
791             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
792         upperNC = *thresholdData.warningHigh;
793     }
794     if (thresholdData.warningLow)
795     {
796         readable |=
797             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
798         lowerNC = *thresholdData.warningLow;
799     }
800 
801     if (thresholdData.criticalHigh)
802     {
803         readable |=
804             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
805         upperCritical = *thresholdData.criticalHigh;
806     }
807     if (thresholdData.criticalLow)
808     {
809         readable |=
810             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
811         lowerCritical = *thresholdData.criticalLow;
812     }
813 
814     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
815                                  lowerNonRecoverable, upperNC, upperCritical,
816                                  upperNonRecoverable);
817 }
818 
819 /** @brief implements the get Sensor event enable command
820  *  @param sensorNumber - sensor number
821  *
822  *  @returns IPMI completion code plus response data
823  *   - enabled               - Sensor Event messages
824  *   - assertionEnabledLsb   - Assertion event messages
825  *   - assertionEnabledMsb   - Assertion event messages
826  *   - deassertionEnabledLsb - Deassertion event messages
827  *   - deassertionEnabledMsb - Deassertion event messages
828  */
829 
830 ipmi::RspType<uint8_t, // enabled
831               uint8_t, // assertionEnabledLsb
832               uint8_t, // assertionEnabledMsb
833               uint8_t, // deassertionEnabledLsb
834               uint8_t> // deassertionEnabledMsb
835     ipmiSenGetSensorEventEnable(uint8_t sensorNum)
836 {
837     std::string connection;
838     std::string path;
839 
840     uint8_t enabled = 0;
841     uint8_t assertionEnabledLsb = 0;
842     uint8_t assertionEnabledMsb = 0;
843     uint8_t deassertionEnabledLsb = 0;
844     uint8_t deassertionEnabledMsb = 0;
845 
846     auto status = getSensorConnection(sensorNum, connection, path);
847     if (status)
848     {
849         return ipmi::response(status);
850     }
851 
852     SensorMap sensorMap;
853     if (!getSensorMap(connection, path, sensorMap))
854     {
855         return ipmi::responseResponseError();
856     }
857 
858     auto warningInterface =
859         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
860     auto criticalInterface =
861         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
862     if ((warningInterface != sensorMap.end()) ||
863         (criticalInterface != sensorMap.end()))
864     {
865         enabled = static_cast<uint8_t>(
866             IPMISensorEventEnableByte2::sensorScanningEnable);
867         if (warningInterface != sensorMap.end())
868         {
869             auto &warningMap = warningInterface->second;
870 
871             auto warningHigh = warningMap.find("WarningHigh");
872             auto warningLow = warningMap.find("WarningLow");
873             if (warningHigh != warningMap.end())
874             {
875                 assertionEnabledLsb |= static_cast<uint8_t>(
876                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
877                 deassertionEnabledLsb |= static_cast<uint8_t>(
878                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
879             }
880             if (warningLow != warningMap.end())
881             {
882                 assertionEnabledLsb |= static_cast<uint8_t>(
883                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
884                 deassertionEnabledLsb |= static_cast<uint8_t>(
885                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
886             }
887         }
888         if (criticalInterface != sensorMap.end())
889         {
890             auto &criticalMap = criticalInterface->second;
891 
892             auto criticalHigh = criticalMap.find("CriticalHigh");
893             auto criticalLow = criticalMap.find("CriticalLow");
894 
895             if (criticalHigh != criticalMap.end())
896             {
897                 assertionEnabledMsb |= static_cast<uint8_t>(
898                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
899                 deassertionEnabledMsb |= static_cast<uint8_t>(
900                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
901             }
902             if (criticalLow != criticalMap.end())
903             {
904                 assertionEnabledLsb |= static_cast<uint8_t>(
905                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
906                 deassertionEnabledLsb |= static_cast<uint8_t>(
907                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
908             }
909         }
910     }
911 
912     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
913                                  assertionEnabledMsb, deassertionEnabledLsb,
914                                  deassertionEnabledMsb);
915 }
916 
917 ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
918                                        ipmi_request_t request,
919                                        ipmi_response_t response,
920                                        ipmi_data_len_t dataLen,
921                                        ipmi_context_t context)
922 {
923     if (*dataLen != 1)
924     {
925         *dataLen = 0;
926         return IPMI_CC_REQ_DATA_LEN_INVALID;
927     }
928     *dataLen = 0; // default to 0 in case of an error
929 
930     uint8_t sensnum = *(static_cast<uint8_t *>(request));
931 
932     std::string connection;
933     std::string path;
934 
935     auto status = getSensorConnection(sensnum, connection, path);
936     if (status)
937     {
938         return status;
939     }
940 
941     SensorMap sensorMap;
942     if (!getSensorMap(connection, path, sensorMap))
943     {
944         return IPMI_CC_RESPONSE_ERROR;
945     }
946 
947     auto warningInterface =
948         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
949     auto criticalInterface =
950         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
951 
952     // zero out response buff
953     auto responseClear = static_cast<uint8_t *>(response);
954     std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
955     auto resp = static_cast<SensorEventStatusResp *>(response);
956     resp->enabled =
957         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
958 
959     std::optional<bool> criticalDeassertHigh =
960         thresholdDeassertMap[path]["CriticalAlarmHigh"];
961     std::optional<bool> criticalDeassertLow =
962         thresholdDeassertMap[path]["CriticalAlarmLow"];
963     std::optional<bool> warningDeassertHigh =
964         thresholdDeassertMap[path]["WarningAlarmHigh"];
965     std::optional<bool> warningDeassertLow =
966         thresholdDeassertMap[path]["WarningAlarmLow"];
967 
968     if (criticalDeassertHigh && !*criticalDeassertHigh)
969     {
970         resp->deassertionsMSB |= static_cast<uint8_t>(
971             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
972     }
973     if (criticalDeassertLow && !*criticalDeassertLow)
974     {
975         resp->deassertionsMSB |= static_cast<uint8_t>(
976             IPMISensorEventEnableThresholds::upperCriticalGoingLow);
977     }
978     if (warningDeassertHigh && !*warningDeassertHigh)
979     {
980         resp->deassertionsLSB |= static_cast<uint8_t>(
981             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
982     }
983     if (warningDeassertLow && !*warningDeassertLow)
984     {
985         resp->deassertionsLSB |= static_cast<uint8_t>(
986             IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
987     }
988 
989     if ((warningInterface != sensorMap.end()) ||
990         (criticalInterface != sensorMap.end()))
991     {
992         resp->enabled = static_cast<uint8_t>(
993             IPMISensorEventEnableByte2::eventMessagesEnable);
994         if (warningInterface != sensorMap.end())
995         {
996             auto &warningMap = warningInterface->second;
997 
998             auto warningHigh = warningMap.find("WarningAlarmHigh");
999             auto warningLow = warningMap.find("WarningAlarmLow");
1000             auto warningHighAlarm = false;
1001             auto warningLowAlarm = false;
1002 
1003             if (warningHigh != warningMap.end())
1004             {
1005                 warningHighAlarm = std::get<bool>(warningHigh->second);
1006             }
1007             if (warningLow != warningMap.end())
1008             {
1009                 warningLowAlarm = std::get<bool>(warningLow->second);
1010             }
1011             if (warningHighAlarm)
1012             {
1013                 resp->assertionsLSB |= static_cast<uint8_t>(
1014                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1015             }
1016             if (warningLowAlarm)
1017             {
1018                 resp->assertionsLSB |= 1; // lower nc going low
1019             }
1020         }
1021         if (criticalInterface != sensorMap.end())
1022         {
1023             auto &criticalMap = criticalInterface->second;
1024 
1025             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1026             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1027             auto criticalHighAlarm = false;
1028             auto criticalLowAlarm = false;
1029 
1030             if (criticalHigh != criticalMap.end())
1031             {
1032                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1033             }
1034             if (criticalLow != criticalMap.end())
1035             {
1036                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1037             }
1038             if (criticalHighAlarm)
1039             {
1040                 resp->assertionsMSB |= static_cast<uint8_t>(
1041                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1042             }
1043             if (criticalLowAlarm)
1044             {
1045                 resp->assertionsLSB |= static_cast<uint8_t>(
1046                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1047             }
1048         }
1049         *dataLen = sizeof(SensorEventStatusResp);
1050     }
1051 
1052     // no thresholds enabled, don't need assertionMSB
1053     else
1054     {
1055         *dataLen = sizeof(SensorEventStatusResp) - 1;
1056     }
1057 
1058     return IPMI_CC_OK;
1059 }
1060 
1061 /* end sensor commands */
1062 
1063 /* storage commands */
1064 
1065 ipmi::RspType<uint8_t,  // sdr version
1066               uint16_t, // record count
1067               uint16_t, // free space
1068               uint32_t, // most recent addition
1069               uint32_t, // most recent erase
1070               uint8_t   // operationSupport
1071               >
1072     ipmiStorageGetSDRRepositoryInfo(void)
1073 {
1074     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
1075     if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1076     {
1077         return ipmi::responseResponseError();
1078     }
1079 
1080     size_t fruCount = 0;
1081     ipmi::Cc ret = ipmi::storage::getFruSdrCount(fruCount);
1082     if (ret != ipmi::ccSuccess)
1083     {
1084         return ipmi::response(ret);
1085     }
1086 
1087     uint16_t recordCount =
1088         sensorTree.size() + fruCount + ipmi::storage::type12Count;
1089 
1090     uint8_t operationSupport = static_cast<uint8_t>(
1091         SdrRepositoryInfoOps::overflow); // write not supported
1092 
1093     operationSupport |=
1094         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1095     operationSupport |= static_cast<uint8_t>(
1096         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1097     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1098                                  unspecifiedFreeSpace, sdrLastAdd,
1099                                  sdrLastRemove, operationSupport);
1100 }
1101 
1102 /** @brief implements the get SDR allocation info command
1103  *
1104  *  @returns IPMI completion code plus response data
1105  *   - allocUnits    - Number of possible allocation units
1106  *   - allocUnitSize - Allocation unit size in bytes.
1107  *   - allocUnitFree - Number of free allocation units
1108  *   - allocUnitLargestFree - Largest free block in allocation units
1109  *   - maxRecordSize    - Maximum record size in allocation units.
1110  */
1111 ipmi::RspType<uint16_t, // allocUnits
1112               uint16_t, // allocUnitSize
1113               uint16_t, // allocUnitFree
1114               uint16_t, // allocUnitLargestFree
1115               uint8_t   // maxRecordSize
1116               >
1117     ipmiStorageGetSDRAllocationInfo()
1118 {
1119     // 0000h unspecified number of alloc units
1120     constexpr uint16_t allocUnits = 0;
1121 
1122     constexpr uint16_t allocUnitFree = 0;
1123     constexpr uint16_t allocUnitLargestFree = 0;
1124     // only allow one block at a time
1125     constexpr uint8_t maxRecordSize = 1;
1126 
1127     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1128                                  allocUnitLargestFree, maxRecordSize);
1129 }
1130 
1131 /** @brief implements the reserve SDR command
1132  *  @returns IPMI completion code plus response data
1133  *   - sdrReservationID
1134  */
1135 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1136 {
1137     sdrReservationID++;
1138     if (sdrReservationID == 0)
1139     {
1140         sdrReservationID++;
1141     }
1142 
1143     return ipmi::responseSuccess(sdrReservationID);
1144 }
1145 
1146 ipmi::RspType<uint16_t,            // next record ID
1147               std::vector<uint8_t> // payload
1148               >
1149     ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset,
1150                       uint8_t bytesToRead)
1151 {
1152     constexpr uint16_t lastRecordIndex = 0xFFFF;
1153 
1154     // reservation required for partial reads with non zero offset into
1155     // record
1156     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1157     {
1158         return ipmi::responseInvalidReservationId();
1159     }
1160 
1161     if (sensorTree.empty() && !getSensorSubtree(sensorTree))
1162     {
1163         return ipmi::responseResponseError();
1164     }
1165 
1166     size_t fruCount = 0;
1167     ipmi::Cc ret = ipmi::storage::getFruSdrCount(fruCount);
1168     if (ret != ipmi::ccSuccess)
1169     {
1170         return ipmi::response(ret);
1171     }
1172 
1173     size_t lastRecord =
1174         sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
1175     if (recordID == lastRecordIndex)
1176     {
1177         recordID = lastRecord;
1178     }
1179     if (recordID > lastRecord)
1180     {
1181         return ipmi::responseInvalidFieldRequest();
1182     }
1183 
1184     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1185 
1186     if (recordID >= sensorTree.size())
1187     {
1188         std::vector<uint8_t> recordData;
1189         size_t fruIndex = recordID - sensorTree.size();
1190         if (fruIndex >= fruCount)
1191         {
1192             // handle type 12 hardcoded records
1193             size_t type12Index = fruIndex - fruCount;
1194             if (type12Index >= ipmi::storage::type12Count ||
1195                 offset > sizeof(Type12Record))
1196             {
1197                 return ipmi::responseInvalidFieldRequest();
1198             }
1199             std::vector<uint8_t> record =
1200                 ipmi::storage::getType12SDRs(type12Index, recordID);
1201             if (record.size() < (offset + bytesToRead))
1202             {
1203                 bytesToRead = record.size() - offset;
1204             }
1205 
1206             recordData.insert(recordData.end(), record.begin() + offset,
1207                               record.begin() + offset + bytesToRead);
1208         }
1209         else
1210         {
1211             // handle fru records
1212             get_sdr::SensorDataFruRecord data;
1213             if (offset > sizeof(data))
1214             {
1215                 return ipmi::responseInvalidFieldRequest();
1216             }
1217             ret = ipmi::storage::getFruSdrs(fruIndex, data);
1218             if (ret != IPMI_CC_OK)
1219             {
1220                 return ipmi::response(ret);
1221             }
1222             data.header.record_id_msb = recordID << 8;
1223             data.header.record_id_lsb = recordID & 0xFF;
1224             if (sizeof(data) < (offset + bytesToRead))
1225             {
1226                 bytesToRead = sizeof(data) - offset;
1227             }
1228 
1229             uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset;
1230             recordData.insert(recordData.end(), respStart,
1231                               respStart + bytesToRead);
1232         }
1233 
1234         return ipmi::responseSuccess(nextRecordId, recordData);
1235     }
1236 
1237     std::string connection;
1238     std::string path;
1239     uint16_t sensorIndex = recordID;
1240     for (const auto &sensor : sensorTree)
1241     {
1242         if (sensorIndex-- == 0)
1243         {
1244             if (!sensor.second.size())
1245             {
1246                 return ipmi::responseResponseError();
1247             }
1248             connection = sensor.second.begin()->first;
1249             path = sensor.first;
1250             break;
1251         }
1252     }
1253 
1254     SensorMap sensorMap;
1255     if (!getSensorMap(connection, path, sensorMap))
1256     {
1257         return ipmi::responseResponseError();
1258     }
1259     uint8_t sensornumber = (recordID & 0xFF);
1260     get_sdr::SensorDataFullRecord record = {0};
1261 
1262     record.header.record_id_msb = recordID << 8;
1263     record.header.record_id_lsb = recordID & 0xFF;
1264     record.header.sdr_version = ipmiSdrVersion;
1265     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1266     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1267                                   sizeof(get_sdr::SensorDataRecordHeader);
1268     record.key.owner_id = 0x20;
1269     record.key.owner_lun = 0x0;
1270     record.key.sensor_number = sensornumber;
1271 
1272     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1273     record.body.sensor_type = getSensorTypeFromPath(path);
1274     std::string type = getSensorTypeStringFromPath(path);
1275     auto typeCstr = type.c_str();
1276     auto findUnits = sensorUnits.find(typeCstr);
1277     if (findUnits != sensorUnits.end())
1278     {
1279         record.body.sensor_units_2_base =
1280             static_cast<uint8_t>(findUnits->second);
1281     } // else default 0x0 unspecified
1282 
1283     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1284 
1285     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1286     if (sensorObject == sensorMap.end())
1287     {
1288         return ipmi::responseResponseError();
1289     }
1290 
1291     uint8_t entityId = 0;
1292     uint8_t entityInstance = 0x01;
1293 
1294     // follow the association chain to get the parent board's entityid and
1295     // entityInstance
1296     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1297 
1298     record.body.entity_id = entityId;
1299     record.body.entity_instance = entityInstance;
1300 
1301     auto maxObject = sensorObject->second.find("MaxValue");
1302     auto minObject = sensorObject->second.find("MinValue");
1303     double max = 128;
1304     double min = -127;
1305     if (maxObject != sensorObject->second.end())
1306     {
1307         max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1308     }
1309 
1310     if (minObject != sensorObject->second.end())
1311     {
1312         min = std::visit(VariantToDoubleVisitor(), minObject->second);
1313     }
1314 
1315     int16_t mValue = 0;
1316     int8_t rExp = 0;
1317     int16_t bValue = 0;
1318     int8_t bExp = 0;
1319     bool bSigned = false;
1320 
1321     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1322     {
1323         return ipmi::responseResponseError();
1324     }
1325 
1326     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1327     record.body.m_lsb = mValue & 0xFF;
1328 
1329     // move the smallest bit of the MSB into place (bit 9)
1330     // the MSbs are bits 7:8 in m_msb_and_tolerance
1331     uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1332 
1333     // assign the negative
1334     if (mValue < 0)
1335     {
1336         mMsb |= (1 << 7);
1337     }
1338     record.body.m_msb_and_tolerance = mMsb;
1339 
1340     record.body.b_lsb = bValue & 0xFF;
1341 
1342     // move the smallest bit of the MSB into place
1343     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1344     uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
1345 
1346     // assign the negative
1347     if (bValue < 0)
1348     {
1349         bMsb |= (1 << 7);
1350     }
1351     record.body.b_msb_and_accuracy_lsb = bMsb;
1352 
1353     record.body.r_b_exponents = bExp & 0x7;
1354     if (bExp < 0)
1355     {
1356         record.body.r_b_exponents |= 1 << 3;
1357     }
1358     record.body.r_b_exponents = (rExp & 0x7) << 4;
1359     if (rExp < 0)
1360     {
1361         record.body.r_b_exponents |= 1 << 7;
1362     }
1363 
1364     // todo fill out rest of units
1365     if (bSigned)
1366     {
1367         record.body.sensor_units_1 = 1 << 7;
1368     }
1369 
1370     // populate sensor name from path
1371     std::string name;
1372     size_t nameStart = path.rfind("/");
1373     if (nameStart != std::string::npos)
1374     {
1375         name = path.substr(nameStart + 1, std::string::npos - nameStart);
1376     }
1377 
1378     std::replace(name.begin(), name.end(), '_', ' ');
1379     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1380     {
1381         // try to not truncate by replacing common words
1382         constexpr std::array<std::pair<const char *, const char *>, 2>
1383             replaceWords = {std::make_pair("Output", "Out"),
1384                             std::make_pair("Input", "In")};
1385         for (const auto &[find, replace] : replaceWords)
1386         {
1387             boost::replace_all(name, find, replace);
1388         }
1389 
1390         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1391     }
1392     record.body.id_string_info = name.size();
1393     std::strncpy(record.body.id_string, name.c_str(),
1394                  sizeof(record.body.id_string));
1395 
1396     IPMIThresholds thresholdData;
1397     try
1398     {
1399         thresholdData = getIPMIThresholds(sensorMap);
1400     }
1401     catch (std::exception &)
1402     {
1403         return ipmi::responseResponseError();
1404     }
1405 
1406     if (thresholdData.criticalHigh)
1407     {
1408         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1409         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1410             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1411         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1412             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1413         record.body.discrete_reading_setting_mask[0] |=
1414             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1415     }
1416     if (thresholdData.warningHigh)
1417     {
1418         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1419         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1420             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1421         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1422             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1423         record.body.discrete_reading_setting_mask[0] |=
1424             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1425     }
1426     if (thresholdData.criticalLow)
1427     {
1428         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1429         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1430             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1431         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1432             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1433         record.body.discrete_reading_setting_mask[0] |=
1434             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1435     }
1436     if (thresholdData.warningLow)
1437     {
1438         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1439         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1440             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1441         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1442             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1443         record.body.discrete_reading_setting_mask[0] |=
1444             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1445     }
1446 
1447     // everything that is readable is setable
1448     record.body.discrete_reading_setting_mask[1] =
1449         record.body.discrete_reading_setting_mask[0];
1450 
1451     if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead))
1452     {
1453         bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset;
1454     }
1455 
1456     uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset;
1457     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1458 
1459     return ipmi::responseSuccess(nextRecordId, recordData);
1460 }
1461 /* end storage commands */
1462 
1463 void registerSensorFunctions()
1464 {
1465     // get firmware version information
1466     ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1467                          ipmiSensorWildcardHandler, PRIVILEGE_USER);
1468 
1469     // <Get Sensor Type>
1470     ipmiPrintAndRegister(NETFUN_SENSOR, ipmi::sensor_event::cmdGetSensorType,
1471                          nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
1472 
1473     // <Set Sensor Reading and Event Status>
1474     ipmiPrintAndRegister(
1475         NETFUN_SENSOR, ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1476         nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
1477 
1478     // <Platform Event>
1479     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1480                           ipmi::sensor_event::cmdPlatformEvent,
1481                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1482 
1483     // <Get Sensor Reading>
1484     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1485                           ipmi::sensor_event::cmdGetSensorReading,
1486                           ipmi::Privilege::User, ipmiSenGetSensorReading);
1487 
1488     // <Get Sensor Threshold>
1489     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1490                           ipmi::sensor_event::cmdGetSensorThreshold,
1491                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1492 
1493     // <Set Sensor Threshold>
1494     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1495                           ipmi::sensor_event::cmdSetSensorThreshold,
1496                           ipmi::Privilege::Operator,
1497                           ipmiSenSetSensorThresholds);
1498 
1499     // <Get Sensor Event Enable>
1500     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1501                           ipmi::sensor_event::cmdGetSensorEventEnable,
1502                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1503 
1504     // <Get Sensor Event Status>
1505     ipmiPrintAndRegister(NETFUN_SENSOR,
1506                          ipmi::sensor_event::cmdGetSensorEventStatus, nullptr,
1507                          ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
1508 
1509     // register all storage commands for both Sensor and Storage command
1510     // versions
1511 
1512     // <Get SDR Repository Info>
1513     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1514                           ipmi::storage::cmdGetSdrRepositoryInfo,
1515                           ipmi::Privilege::User,
1516                           ipmiStorageGetSDRRepositoryInfo);
1517 
1518     // <Get SDR Allocation Info>
1519     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1520                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1521                           ipmi::Privilege::User,
1522                           ipmiStorageGetSDRAllocationInfo);
1523 
1524     // <Reserve SDR Repo>
1525     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1526                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1527                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1528 
1529     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1530                           ipmi::storage::cmdReserveSdrRepository,
1531                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1532 
1533     // <Get Sdr>
1534     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1535                           ipmi::sensor_event::cmdGetDeviceSdr,
1536                           ipmi::Privilege::User, ipmiStorageGetSDR);
1537 
1538     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1539                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1540                           ipmiStorageGetSDR);
1541 }
1542 } // namespace ipmi
1543