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