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