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