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