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