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 <user_channel/channel_layer.hpp>
42 #include <utility>
43 #include <variant>
44 
45 #ifdef FEATURE_HYBRID_SENSORS
46 
47 #include "sensordatahandler.hpp"
48 namespace ipmi
49 {
50 namespace sensor
51 {
52 extern const IdInfoMap sensors;
53 } // namespace sensor
54 } // namespace ipmi
55 #endif
56 
57 constexpr std::array<const char*, 7> suffixes = {
58     "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
59     "_Output_Power",   "_Input_Power",   "_Temperature"};
60 namespace ipmi
61 {
62 
63 using phosphor::logging::entry;
64 using phosphor::logging::level;
65 using phosphor::logging::log;
66 
67 static constexpr int sensorMapUpdatePeriod = 10;
68 static constexpr int sensorMapSdrUpdatePeriod = 60;
69 
70 // BMC I2C address is generally at 0x20
71 static constexpr uint8_t bmcI2CAddr = 0x20;
72 
73 constexpr size_t maxSDRTotalSize =
74     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
75 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
76 
77 static uint16_t sdrReservationID;
78 static uint32_t sdrLastAdd = noTimestamp;
79 static uint32_t sdrLastRemove = noTimestamp;
80 static constexpr size_t lastRecordIndex = 0xFFFF;
81 
82 // The IPMI spec defines four Logical Units (LUN), each capable of supporting
83 // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
84 // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
85 // number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
86 // location.
87 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
88 
89 static constexpr size_t lun0MaxSensorNum = 0xfe;
90 static constexpr size_t lun1MaxSensorNum = 0x1fe;
91 static constexpr size_t lun3MaxSensorNum = 0x3fe;
92 static constexpr int GENERAL_ERROR = -1;
93 
94 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
95 
96 // Specify the comparison required to sort and find char* map objects
97 struct CmpStr
98 {
99     bool operator()(const char* a, const char* b) const
100     {
101         return std::strcmp(a, b) < 0;
102     }
103 };
104 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
105     sensorUnits{{{"temperature", SensorUnits::degreesC},
106                  {"voltage", SensorUnits::volts},
107                  {"current", SensorUnits::amps},
108                  {"fan_tach", SensorUnits::rpm},
109                  {"power", SensorUnits::watts}}};
110 
111 void registerSensorFunctions() __attribute__((constructor));
112 
113 static sdbusplus::bus::match::match sensorAdded(
114     *getSdBus(),
115     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
116     "sensors/'",
117     [](sdbusplus::message::message& m) {
118         getSensorTree().clear();
119         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
120                          std::chrono::system_clock::now().time_since_epoch())
121                          .count();
122     });
123 
124 static sdbusplus::bus::match::match sensorRemoved(
125     *getSdBus(),
126     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
127     "sensors/'",
128     [](sdbusplus::message::message& m) {
129         getSensorTree().clear();
130         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
131                             std::chrono::system_clock::now().time_since_epoch())
132                             .count();
133     });
134 
135 // this keeps track of deassertions for sensor event status command. A
136 // deasertion can only happen if an assertion was seen first.
137 static boost::container::flat_map<
138     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
139     thresholdDeassertMap;
140 
141 static sdbusplus::bus::match::match thresholdChanged(
142     *getSdBus(),
143     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
144     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
145     [](sdbusplus::message::message& m) {
146         boost::container::flat_map<std::string, std::variant<bool, double>>
147             values;
148         m.read(std::string(), values);
149 
150         auto findAssert =
151             std::find_if(values.begin(), values.end(), [](const auto& pair) {
152                 return pair.first.find("Alarm") != std::string::npos;
153             });
154         if (findAssert != values.end())
155         {
156             auto ptr = std::get_if<bool>(&(findAssert->second));
157             if (ptr == nullptr)
158             {
159                 phosphor::logging::log<phosphor::logging::level::ERR>(
160                     "thresholdChanged: Assert non bool");
161                 return;
162             }
163             if (*ptr)
164             {
165                 phosphor::logging::log<phosphor::logging::level::INFO>(
166                     "thresholdChanged: Assert",
167                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
168                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
169             }
170             else
171             {
172                 auto& value =
173                     thresholdDeassertMap[m.get_path()][findAssert->first];
174                 if (value)
175                 {
176                     phosphor::logging::log<phosphor::logging::level::INFO>(
177                         "thresholdChanged: deassert",
178                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
179                     value = *ptr;
180                 }
181             }
182         }
183     });
184 
185 namespace sensor
186 {
187 static constexpr const char* vrInterface =
188     "xyz.openbmc_project.Control.VoltageRegulatorMode";
189 static constexpr const char* sensorInterface =
190     "xyz.openbmc_project.Sensor.Value";
191 } // namespace sensor
192 
193 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
194                             double& min)
195 {
196     max = 127;
197     min = -128;
198 
199     auto sensorObject = sensorMap.find(sensor::sensorInterface);
200     auto critical =
201         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
202     auto warning =
203         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
204 
205     if (sensorObject != sensorMap.end())
206     {
207         auto maxMap = sensorObject->second.find("MaxValue");
208         auto minMap = sensorObject->second.find("MinValue");
209 
210         if (maxMap != sensorObject->second.end())
211         {
212             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
213         }
214         if (minMap != sensorObject->second.end())
215         {
216             min = std::visit(VariantToDoubleVisitor(), minMap->second);
217         }
218     }
219     if (critical != sensorMap.end())
220     {
221         auto lower = critical->second.find("CriticalLow");
222         auto upper = critical->second.find("CriticalHigh");
223         if (lower != critical->second.end())
224         {
225             double value = std::visit(VariantToDoubleVisitor(), lower->second);
226             if (std::isfinite(value))
227             {
228                 min = std::min(value, min);
229             }
230         }
231         if (upper != critical->second.end())
232         {
233             double value = std::visit(VariantToDoubleVisitor(), upper->second);
234             if (std::isfinite(value))
235             {
236                 max = std::max(value, max);
237             }
238         }
239     }
240     if (warning != sensorMap.end())
241     {
242 
243         auto lower = warning->second.find("WarningLow");
244         auto upper = warning->second.find("WarningHigh");
245         if (lower != warning->second.end())
246         {
247             double value = std::visit(VariantToDoubleVisitor(), lower->second);
248             if (std::isfinite(value))
249             {
250                 min = std::min(value, min);
251             }
252         }
253         if (upper != warning->second.end())
254         {
255             double value = std::visit(VariantToDoubleVisitor(), upper->second);
256             if (std::isfinite(value))
257             {
258                 max = std::max(value, max);
259             }
260         }
261     }
262 }
263 
264 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
265                          std::string sensorPath, DbusInterfaceMap& sensorMap,
266                          int updatePeriod = sensorMapUpdatePeriod)
267 {
268 #ifdef FEATURE_HYBRID_SENSORS
269     if (auto sensor = findStaticSensor(sensorPath);
270         sensor != ipmi::sensor::sensors.end() &&
271         getSensorEventTypeFromPath(sensorPath) !=
272             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
273     {
274         // If the incoming sensor is a discrete sensor, it might fail in
275         // getManagedObjects(), return true, and use its own getFunc to get
276         // value.
277         return true;
278     }
279 #endif
280 
281     static boost::container::flat_map<
282         std::string, std::chrono::time_point<std::chrono::steady_clock>>
283         updateTimeMap;
284 
285     auto updateFind = updateTimeMap.find(sensorConnection);
286     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
287     if (updateFind != updateTimeMap.end())
288     {
289         lastUpdate = updateFind->second;
290     }
291 
292     auto now = std::chrono::steady_clock::now();
293 
294     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
295             .count() > updatePeriod)
296     {
297         ObjectValueTree managedObjects;
298         boost::system::error_code ec = getManagedObjects(
299             ctx, sensorConnection.c_str(), "/", managedObjects);
300         if (ec)
301         {
302             phosphor::logging::log<phosphor::logging::level::ERR>(
303                 "GetMangagedObjects for getSensorMap failed",
304                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
305 
306             return false;
307         }
308 
309         SensorCache[sensorConnection] = managedObjects;
310         // Update time after finish building the map which allow the
311         // data to be cached for updatePeriod plus the build time.
312         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
313     }
314     auto connection = SensorCache.find(sensorConnection);
315     if (connection == SensorCache.end())
316     {
317         return false;
318     }
319     auto path = connection->second.find(sensorPath);
320     if (path == connection->second.end())
321     {
322         return false;
323     }
324     sensorMap = path->second;
325 
326     return true;
327 }
328 
329 namespace sensor
330 {
331 // Read VR profiles from sensor(daemon) interface
332 static std::optional<std::vector<std::string>>
333     getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
334 {
335     // get VR mode profiles from Supported Interface
336     auto supportedProperty = object.find("Supported");
337     if (supportedProperty == object.end() ||
338         object.find("Selected") == object.end())
339     {
340         phosphor::logging::log<phosphor::logging::level::ERR>(
341             "Missing the required Supported and Selected properties");
342         return std::nullopt;
343     }
344 
345     const auto profilesPtr =
346         std::get_if<std::vector<std::string>>(&supportedProperty->second);
347 
348     if (profilesPtr == nullptr)
349     {
350         phosphor::logging::log<phosphor::logging::level::ERR>(
351             "property is not array of string");
352         return std::nullopt;
353     }
354     return *profilesPtr;
355 }
356 
357 // Calculate VR Mode from input IPMI discrete event bytes
358 static std::optional<std::string>
359     calculateVRMode(uint15_t assertOffset,
360                     const ipmi::DbusInterfaceMap::mapped_type& VRObject)
361 {
362     // get VR mode profiles from Supported Interface
363     auto profiles = getSupportedVrProfiles(VRObject);
364     if (!profiles)
365     {
366         return std::nullopt;
367     }
368 
369     // interpret IPMI cmd bits into profiles' index
370     long unsigned int index = 0;
371     // only one bit should be set and the highest bit should not be used.
372     if (assertOffset == 0 || assertOffset == (1u << 15) ||
373         (assertOffset & (assertOffset - 1)))
374     {
375         phosphor::logging::log<phosphor::logging::level::ERR>(
376             "IPMI cmd format incorrect",
377 
378             phosphor::logging::entry("BYTES=%#02x",
379                                      static_cast<uint16_t>(assertOffset)));
380         return std::nullopt;
381     }
382 
383     while (assertOffset != 1)
384     {
385         assertOffset >>= 1;
386         index++;
387     }
388 
389     if (index >= profiles->size())
390     {
391         phosphor::logging::log<phosphor::logging::level::ERR>(
392             "profile index out of boundary");
393         return std::nullopt;
394     }
395 
396     return profiles->at(index);
397 }
398 
399 // Calculate sensor value from IPMI reading byte
400 static std::optional<double>
401     calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
402                    const ipmi::DbusInterfaceMap::mapped_type& valueObject)
403 {
404     if (valueObject.find("Value") == valueObject.end())
405     {
406         phosphor::logging::log<phosphor::logging::level::ERR>(
407             "Missing the required Value property");
408         return std::nullopt;
409     }
410 
411     double max = 0;
412     double min = 0;
413     getSensorMaxMin(sensorMap, max, min);
414 
415     int16_t mValue = 0;
416     int16_t bValue = 0;
417     int8_t rExp = 0;
418     int8_t bExp = 0;
419     bool bSigned = false;
420 
421     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
422     {
423         return std::nullopt;
424     }
425 
426     double value = bSigned ? ((int8_t)reading) : reading;
427 
428     value *= ((double)mValue);
429     value += ((double)bValue) * std::pow(10.0, bExp);
430     value *= std::pow(10.0, rExp);
431 
432     return value;
433 }
434 
435 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
436 // if it is too long.
437 std::string parseSdrIdFromPath(const std::string& path)
438 {
439     std::string name;
440     size_t nameStart = path.rfind("/");
441     if (nameStart != std::string::npos)
442     {
443         name = path.substr(nameStart + 1, std::string::npos - nameStart);
444     }
445 
446     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
447     {
448         // try to not truncate by replacing common words
449         for (const auto& suffix : suffixes)
450         {
451             if (boost::ends_with(name, suffix))
452             {
453                 boost::replace_all(name, suffix, "");
454                 break;
455             }
456         }
457         if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
458         {
459             name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
460         }
461     }
462     std::replace(name.begin(), name.end(), '_', ' ');
463     return name;
464 }
465 
466 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
467                       const std::string& path,
468                       const ipmi::DbusInterfaceMap::mapped_type& object,
469                       std::bitset<16>& assertions)
470 {
471     auto profiles = sensor::getSupportedVrProfiles(object);
472     if (!profiles)
473     {
474         return false;
475     }
476     ipmi::Value modeVariant;
477 
478     auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
479                               "Selected", modeVariant);
480     if (ec)
481     {
482         log<level::ERR>("Failed to get property",
483                         entry("PROPERTY=%s", "Selected"),
484                         entry("PATH=%s", path.c_str()),
485                         entry("INTERFACE=%s", sensor::sensorInterface),
486                         entry("WHAT=%s", ec.message().c_str()));
487         return false;
488     }
489 
490     auto mode = std::get_if<std::string>(&modeVariant);
491     if (mode == nullptr)
492     {
493         log<level::ERR>("property is not a string",
494                         entry("PROPERTY=%s", "Selected"),
495                         entry("PATH=%s", path.c_str()),
496                         entry("INTERFACE=%s", sensor::sensorInterface));
497         return false;
498     }
499 
500     auto itr = std::find(profiles->begin(), profiles->end(), *mode);
501     if (itr == profiles->end())
502     {
503         using namespace phosphor::logging;
504         log<level::ERR>("VR mode doesn't match any of its profiles",
505                         entry("PATH=%s", path.c_str()));
506         return false;
507     }
508     std::size_t index =
509         static_cast<std::size_t>(std::distance(profiles->begin(), itr));
510 
511     // map index to reponse event assertion bit.
512     if (index < 8)
513     {
514         assertions.set(1u << index);
515     }
516     else if (index < 15)
517     {
518         assertions.set(1u << (index - 8));
519     }
520     else
521     {
522         log<level::ERR>("VR profile index reaches max assertion bit",
523                         entry("PATH=%s", path.c_str()),
524                         entry("INDEX=%uz", index));
525         return false;
526     }
527     if constexpr (debug)
528     {
529         std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
530                   << " mode is: [" << index << "] " << *mode << std::endl;
531     }
532     return true;
533 }
534 } // namespace sensor
535 
536 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
537                                      ipmi::message::Payload& p)
538 {
539     constexpr const uint8_t validEnvmRev = 0x04;
540     constexpr const uint8_t lastSensorType = 0x2C;
541     constexpr const uint8_t oemReserved = 0xC0;
542 
543     uint8_t generatorID = 0;
544     uint8_t evmRev = 0;
545     uint8_t sensorType = 0;
546     uint8_t sensorNum = 0;
547     uint8_t eventType = 0;
548     uint8_t eventData1 = 0;
549     std::optional<uint8_t> eventData2 = 0;
550     std::optional<uint8_t> eventData3 = 0;
551     ipmi::ChannelInfo chInfo;
552 
553     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
554     {
555         phosphor::logging::log<phosphor::logging::level::ERR>(
556             "Failed to get Channel Info",
557             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
558         return ipmi::responseUnspecifiedError();
559     }
560 
561     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
562         ipmi::EChannelMediumType::systemInterface)
563     {
564 
565         p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
566                  eventData1, eventData2, eventData3);
567     }
568     else
569     {
570 
571         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
572                  eventData2, eventData3);
573         generatorID = ctx->rqSA;
574     }
575 
576     if (!p.fullyUnpacked())
577     {
578         return ipmi::responseReqDataLenInvalid();
579     }
580 
581     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
582     if (evmRev != validEnvmRev)
583     {
584         return ipmi::responseInvalidFieldRequest();
585     }
586     if ((sensorType > lastSensorType) && (sensorType < oemReserved))
587     {
588         return ipmi::responseInvalidFieldRequest();
589     }
590 
591     return ipmi::responseSuccess();
592 }
593 
594 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
595                                      uint8_t sensorNumber, uint8_t operation,
596                                      uint8_t reading, uint15_t assertOffset,
597                                      bool resvd1, uint15_t deassertOffset,
598                                      bool resvd2, uint8_t eventData1,
599                                      uint8_t eventData2, uint8_t eventData3)
600 {
601     std::string connection;
602     std::string path;
603     std::vector<std::string> interfaces;
604 
605     ipmi::Cc status =
606         getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
607     if (status)
608     {
609         return ipmi::response(status);
610     }
611 
612     // we can tell the sensor type by its interface type
613     if (std::find(interfaces.begin(), interfaces.end(),
614                   sensor::sensorInterface) != interfaces.end())
615     {
616         DbusInterfaceMap sensorMap;
617         if (!getSensorMap(ctx, connection, path, sensorMap))
618         {
619             return ipmi::responseResponseError();
620         }
621         auto sensorObject = sensorMap.find(sensor::sensorInterface);
622         if (sensorObject == sensorMap.end())
623         {
624             return ipmi::responseResponseError();
625         }
626 
627         // Only allow external SetSensor if write permission granted
628         if (!details::sdrWriteTable.getWritePermission(sensorNumber))
629         {
630             return ipmi::responseResponseError();
631         }
632 
633         auto value =
634             sensor::calculateValue(reading, sensorMap, sensorObject->second);
635         if (!value)
636         {
637             return ipmi::responseResponseError();
638         }
639 
640         if constexpr (debug)
641         {
642             phosphor::logging::log<phosphor::logging::level::INFO>(
643                 "IPMI SET_SENSOR",
644                 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
645                 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
646                 phosphor::logging::entry("VALUE=%f", *value));
647         }
648 
649         boost::system::error_code ec =
650             setDbusProperty(ctx, connection, path, sensor::sensorInterface,
651                             "Value", ipmi::Value(*value));
652 
653         // setDbusProperty intended to resolve dbus exception/rc within the
654         // function but failed to achieve that. Catch exception in the ipmi
655         // callback functions for now (e.g. ipmiSetSensorReading).
656         if (ec)
657         {
658             using namespace phosphor::logging;
659             log<level::ERR>("Failed to set property",
660                             entry("PROPERTY=%s", "Value"),
661                             entry("PATH=%s", path.c_str()),
662                             entry("INTERFACE=%s", sensor::sensorInterface),
663                             entry("WHAT=%s", ec.message().c_str()));
664             return ipmi::responseResponseError();
665         }
666         return ipmi::responseSuccess();
667     }
668 
669     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
670         interfaces.end())
671     {
672         DbusInterfaceMap sensorMap;
673         if (!getSensorMap(ctx, connection, path, sensorMap))
674         {
675             return ipmi::responseResponseError();
676         }
677         auto sensorObject = sensorMap.find(sensor::vrInterface);
678         if (sensorObject == sensorMap.end())
679         {
680             return ipmi::responseResponseError();
681         }
682 
683         // VR sensors are treated as a special case and we will not check the
684         // write permission for VR sensors, since they always deemed writable
685         // and permission table are not applied to VR sensors.
686         auto vrMode =
687             sensor::calculateVRMode(assertOffset, sensorObject->second);
688         if (!vrMode)
689         {
690             return ipmi::responseResponseError();
691         }
692         boost::system::error_code ec = setDbusProperty(
693             ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
694         // setDbusProperty intended to resolve dbus exception/rc within the
695         // function but failed to achieve that. Catch exception in the ipmi
696         // callback functions for now (e.g. ipmiSetSensorReading).
697         if (ec)
698         {
699             using namespace phosphor::logging;
700             log<level::ERR>("Failed to set property",
701                             entry("PROPERTY=%s", "Selected"),
702                             entry("PATH=%s", path.c_str()),
703                             entry("INTERFACE=%s", sensor::sensorInterface),
704                             entry("WHAT=%s", ec.message().c_str()));
705             return ipmi::responseResponseError();
706         }
707         return ipmi::responseSuccess();
708     }
709 
710     phosphor::logging::log<phosphor::logging::level::ERR>(
711         "unknown sensor type",
712         phosphor::logging::entry("PATH=%s", path.c_str()));
713     return ipmi::responseResponseError();
714 }
715 
716 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
717     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
718 {
719     std::string connection;
720     std::string path;
721 
722     if (sensnum == reservedSensorNumber)
723     {
724         return ipmi::responseInvalidFieldRequest();
725     }
726 
727     auto status = getSensorConnection(ctx, sensnum, connection, path);
728     if (status)
729     {
730         return ipmi::response(status);
731     }
732 
733 #ifdef FEATURE_HYBRID_SENSORS
734     if (auto sensor = findStaticSensor(path);
735         sensor != ipmi::sensor::sensors.end() &&
736         getSensorEventTypeFromPath(path) !=
737             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
738     {
739         if (ipmi::sensor::Mutability::Read !=
740             (sensor->second.mutability & ipmi::sensor::Mutability::Read))
741         {
742             return ipmi::responseIllegalCommand();
743         }
744 
745         uint8_t operation;
746         try
747         {
748             ipmi::sensor::GetSensorResponse getResponse =
749                 sensor->second.getFunc(sensor->second);
750 
751             if (getResponse.readingOrStateUnavailable)
752             {
753                 operation |= static_cast<uint8_t>(
754                     IPMISensorReadingByte2::readingStateUnavailable);
755             }
756             if (getResponse.scanningEnabled)
757             {
758                 operation |= static_cast<uint8_t>(
759                     IPMISensorReadingByte2::sensorScanningEnable);
760             }
761             if (getResponse.allEventMessagesEnabled)
762             {
763                 operation |= static_cast<uint8_t>(
764                     IPMISensorReadingByte2::eventMessagesEnable);
765             }
766             return ipmi::responseSuccess(
767                 getResponse.reading, operation,
768                 getResponse.thresholdLevelsStates,
769                 getResponse.discreteReadingSensorStates);
770         }
771         catch (const std::exception& e)
772         {
773             operation |= static_cast<uint8_t>(
774                 IPMISensorReadingByte2::readingStateUnavailable);
775             return ipmi::responseSuccess(0, operation, 0, std::nullopt);
776         }
777     }
778 #endif
779 
780     DbusInterfaceMap sensorMap;
781     if (!getSensorMap(ctx, connection, path, sensorMap))
782     {
783         return ipmi::responseResponseError();
784     }
785     auto sensorObject = sensorMap.find(sensor::sensorInterface);
786 
787     if (sensorObject == sensorMap.end() ||
788         sensorObject->second.find("Value") == sensorObject->second.end())
789     {
790         return ipmi::responseResponseError();
791     }
792     auto& valueVariant = sensorObject->second["Value"];
793     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
794 
795     double max = 0;
796     double min = 0;
797     getSensorMaxMin(sensorMap, max, min);
798 
799     int16_t mValue = 0;
800     int16_t bValue = 0;
801     int8_t rExp = 0;
802     int8_t bExp = 0;
803     bool bSigned = false;
804 
805     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
806     {
807         return ipmi::responseResponseError();
808     }
809 
810     uint8_t value =
811         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
812     uint8_t operation =
813         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
814     operation |=
815         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
816     bool notReading = std::isnan(reading);
817 
818     if (!notReading)
819     {
820         auto availableObject =
821             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
822         if (availableObject != sensorMap.end())
823         {
824             auto findAvailable = availableObject->second.find("Available");
825             if (findAvailable != availableObject->second.end())
826             {
827                 bool* available = std::get_if<bool>(&(findAvailable->second));
828                 if (available && !(*available))
829                 {
830                     notReading = true;
831                 }
832             }
833         }
834     }
835 
836     if (notReading)
837     {
838         operation |= static_cast<uint8_t>(
839             IPMISensorReadingByte2::readingStateUnavailable);
840     }
841 
842     if constexpr (details::enableInstrumentation)
843     {
844         int byteValue;
845         if (bSigned)
846         {
847             byteValue = static_cast<int>(static_cast<int8_t>(value));
848         }
849         else
850         {
851             byteValue = static_cast<int>(static_cast<uint8_t>(value));
852         }
853 
854         // Keep stats on the reading just obtained, even if it is "NaN"
855         if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
856         {
857             // This is the first reading, show the coefficients
858             double step = (max - min) / 255.0;
859             std::cerr << "IPMI sensor "
860                       << details::sdrStatsTable.getName(sensnum)
861                       << ": Range min=" << min << " max=" << max
862                       << ", step=" << step
863                       << ", Coefficients mValue=" << static_cast<int>(mValue)
864                       << " rExp=" << static_cast<int>(rExp)
865                       << " bValue=" << static_cast<int>(bValue)
866                       << " bExp=" << static_cast<int>(bExp)
867                       << " bSigned=" << static_cast<int>(bSigned) << "\n";
868         }
869     }
870 
871     uint8_t thresholds = 0;
872 
873     auto warningObject =
874         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
875     if (warningObject != sensorMap.end())
876     {
877         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
878         auto alarmLow = warningObject->second.find("WarningAlarmLow");
879         if (alarmHigh != warningObject->second.end())
880         {
881             if (std::get<bool>(alarmHigh->second))
882             {
883                 thresholds |= static_cast<uint8_t>(
884                     IPMISensorReadingByte3::upperNonCritical);
885             }
886         }
887         if (alarmLow != warningObject->second.end())
888         {
889             if (std::get<bool>(alarmLow->second))
890             {
891                 thresholds |= static_cast<uint8_t>(
892                     IPMISensorReadingByte3::lowerNonCritical);
893             }
894         }
895     }
896 
897     auto criticalObject =
898         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
899     if (criticalObject != sensorMap.end())
900     {
901         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
902         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
903         if (alarmHigh != criticalObject->second.end())
904         {
905             if (std::get<bool>(alarmHigh->second))
906             {
907                 thresholds |=
908                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
909             }
910         }
911         if (alarmLow != criticalObject->second.end())
912         {
913             if (std::get<bool>(alarmLow->second))
914             {
915                 thresholds |=
916                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
917             }
918         }
919     }
920 
921     // no discrete as of today so optional byte is never returned
922     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
923 }
924 
925 /** @brief implements the Set Sensor threshold command
926  *  @param sensorNumber        - sensor number
927  *  @param lowerNonCriticalThreshMask
928  *  @param lowerCriticalThreshMask
929  *  @param lowerNonRecovThreshMask
930  *  @param upperNonCriticalThreshMask
931  *  @param upperCriticalThreshMask
932  *  @param upperNonRecovThreshMask
933  *  @param reserved
934  *  @param lowerNonCritical    - lower non-critical threshold
935  *  @param lowerCritical       - Lower critical threshold
936  *  @param lowerNonRecoverable - Lower non recovarable threshold
937  *  @param upperNonCritical    - Upper non-critical threshold
938  *  @param upperCritical       - Upper critical
939  *  @param upperNonRecoverable - Upper Non-recoverable
940  *
941  *  @returns IPMI completion code
942  */
943 ipmi::RspType<> ipmiSenSetSensorThresholds(
944     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
945     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
946     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
947     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
948     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
949     uint8_t upperNonCritical, uint8_t upperCritical,
950     uint8_t upperNonRecoverable)
951 {
952     if (sensorNum == reservedSensorNumber || reserved)
953     {
954         return ipmi::responseInvalidFieldRequest();
955     }
956 
957     // lower nc and upper nc not suppported on any sensor
958     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
959     {
960         return ipmi::responseInvalidFieldRequest();
961     }
962 
963     // if none of the threshold mask are set, nothing to do
964     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
965           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
966           upperCriticalThreshMask | upperNonRecovThreshMask))
967     {
968         return ipmi::responseSuccess();
969     }
970 
971     std::string connection;
972     std::string path;
973 
974     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
975     if (status)
976     {
977         return ipmi::response(status);
978     }
979     DbusInterfaceMap sensorMap;
980     if (!getSensorMap(ctx, connection, path, sensorMap))
981     {
982         return ipmi::responseResponseError();
983     }
984 
985     double max = 0;
986     double min = 0;
987     getSensorMaxMin(sensorMap, max, min);
988 
989     int16_t mValue = 0;
990     int16_t bValue = 0;
991     int8_t rExp = 0;
992     int8_t bExp = 0;
993     bool bSigned = false;
994 
995     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
996     {
997         return ipmi::responseResponseError();
998     }
999 
1000     // store a vector of property name, value to set, and interface
1001     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1002 
1003     // define the indexes of the tuple
1004     constexpr uint8_t propertyName = 0;
1005     constexpr uint8_t thresholdValue = 1;
1006     constexpr uint8_t interface = 2;
1007     // verifiy all needed fields are present
1008     if (lowerCriticalThreshMask || upperCriticalThreshMask)
1009     {
1010         auto findThreshold =
1011             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1012         if (findThreshold == sensorMap.end())
1013         {
1014             return ipmi::responseInvalidFieldRequest();
1015         }
1016         if (lowerCriticalThreshMask)
1017         {
1018             auto findLower = findThreshold->second.find("CriticalLow");
1019             if (findLower == findThreshold->second.end())
1020             {
1021                 return ipmi::responseInvalidFieldRequest();
1022             }
1023             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1024                                          findThreshold->first);
1025         }
1026         if (upperCriticalThreshMask)
1027         {
1028             auto findUpper = findThreshold->second.find("CriticalHigh");
1029             if (findUpper == findThreshold->second.end())
1030             {
1031                 return ipmi::responseInvalidFieldRequest();
1032             }
1033             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1034                                          findThreshold->first);
1035         }
1036     }
1037     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1038     {
1039         auto findThreshold =
1040             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1041         if (findThreshold == sensorMap.end())
1042         {
1043             return ipmi::responseInvalidFieldRequest();
1044         }
1045         if (lowerNonCriticalThreshMask)
1046         {
1047             auto findLower = findThreshold->second.find("WarningLow");
1048             if (findLower == findThreshold->second.end())
1049             {
1050                 return ipmi::responseInvalidFieldRequest();
1051             }
1052             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1053                                          findThreshold->first);
1054         }
1055         if (upperNonCriticalThreshMask)
1056         {
1057             auto findUpper = findThreshold->second.find("WarningHigh");
1058             if (findUpper == findThreshold->second.end())
1059             {
1060                 return ipmi::responseInvalidFieldRequest();
1061             }
1062             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1063                                          findThreshold->first);
1064         }
1065     }
1066     for (const auto& property : thresholdsToSet)
1067     {
1068         // from section 36.3 in the IPMI Spec, assume all linear
1069         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1070                              (bValue * std::pow(10.0, bExp))) *
1071                             std::pow(10.0, rExp);
1072         setDbusProperty(
1073             *getSdBus(), connection, path, std::get<interface>(property),
1074             std::get<propertyName>(property), ipmi::Value(valueToSet));
1075     }
1076     return ipmi::responseSuccess();
1077 }
1078 
1079 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1080 {
1081     IPMIThresholds resp;
1082     auto warningInterface =
1083         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1084     auto criticalInterface =
1085         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1086 
1087     if ((warningInterface != sensorMap.end()) ||
1088         (criticalInterface != sensorMap.end()))
1089     {
1090         auto sensorPair = sensorMap.find(sensor::sensorInterface);
1091 
1092         if (sensorPair == sensorMap.end())
1093         {
1094             // should not have been able to find a sensor not implementing
1095             // the sensor object
1096             throw std::runtime_error("Invalid sensor map");
1097         }
1098 
1099         double max = 0;
1100         double min = 0;
1101         getSensorMaxMin(sensorMap, max, min);
1102 
1103         int16_t mValue = 0;
1104         int16_t bValue = 0;
1105         int8_t rExp = 0;
1106         int8_t bExp = 0;
1107         bool bSigned = false;
1108 
1109         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1110         {
1111             throw std::runtime_error("Invalid sensor atrributes");
1112         }
1113         if (warningInterface != sensorMap.end())
1114         {
1115             auto& warningMap = warningInterface->second;
1116 
1117             auto warningHigh = warningMap.find("WarningHigh");
1118             auto warningLow = warningMap.find("WarningLow");
1119 
1120             if (warningHigh != warningMap.end())
1121             {
1122 
1123                 double value =
1124                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
1125                 if (std::isfinite(value))
1126                 {
1127                     resp.warningHigh = scaleIPMIValueFromDouble(
1128                         value, mValue, rExp, bValue, bExp, bSigned);
1129                 }
1130             }
1131             if (warningLow != warningMap.end())
1132             {
1133                 double value =
1134                     std::visit(VariantToDoubleVisitor(), warningLow->second);
1135                 if (std::isfinite(value))
1136                 {
1137                     resp.warningLow = scaleIPMIValueFromDouble(
1138                         value, mValue, rExp, bValue, bExp, bSigned);
1139                 }
1140             }
1141         }
1142         if (criticalInterface != sensorMap.end())
1143         {
1144             auto& criticalMap = criticalInterface->second;
1145 
1146             auto criticalHigh = criticalMap.find("CriticalHigh");
1147             auto criticalLow = criticalMap.find("CriticalLow");
1148 
1149             if (criticalHigh != criticalMap.end())
1150             {
1151                 double value =
1152                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1153                 if (std::isfinite(value))
1154                 {
1155                     resp.criticalHigh = scaleIPMIValueFromDouble(
1156                         value, mValue, rExp, bValue, bExp, bSigned);
1157                 }
1158             }
1159             if (criticalLow != criticalMap.end())
1160             {
1161                 double value =
1162                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
1163                 if (std::isfinite(value))
1164                 {
1165                     resp.criticalLow = scaleIPMIValueFromDouble(
1166                         value, mValue, rExp, bValue, bExp, bSigned);
1167                 }
1168             }
1169         }
1170     }
1171     return resp;
1172 }
1173 
1174 ipmi::RspType<uint8_t, // readable
1175               uint8_t, // lowerNCrit
1176               uint8_t, // lowerCrit
1177               uint8_t, // lowerNrecoverable
1178               uint8_t, // upperNC
1179               uint8_t, // upperCrit
1180               uint8_t> // upperNRecoverable
1181     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1182 {
1183     std::string connection;
1184     std::string path;
1185 
1186     if (sensorNumber == reservedSensorNumber)
1187     {
1188         return ipmi::responseInvalidFieldRequest();
1189     }
1190 
1191     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1192     if (status)
1193     {
1194         return ipmi::response(status);
1195     }
1196 
1197     DbusInterfaceMap sensorMap;
1198     if (!getSensorMap(ctx, connection, path, sensorMap))
1199     {
1200         return ipmi::responseResponseError();
1201     }
1202 
1203     IPMIThresholds thresholdData;
1204     try
1205     {
1206         thresholdData = getIPMIThresholds(sensorMap);
1207     }
1208     catch (const std::exception&)
1209     {
1210         return ipmi::responseResponseError();
1211     }
1212 
1213     uint8_t readable = 0;
1214     uint8_t lowerNC = 0;
1215     uint8_t lowerCritical = 0;
1216     uint8_t lowerNonRecoverable = 0;
1217     uint8_t upperNC = 0;
1218     uint8_t upperCritical = 0;
1219     uint8_t upperNonRecoverable = 0;
1220 
1221     if (thresholdData.warningHigh)
1222     {
1223         readable |=
1224             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1225         upperNC = *thresholdData.warningHigh;
1226     }
1227     if (thresholdData.warningLow)
1228     {
1229         readable |=
1230             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1231         lowerNC = *thresholdData.warningLow;
1232     }
1233 
1234     if (thresholdData.criticalHigh)
1235     {
1236         readable |=
1237             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1238         upperCritical = *thresholdData.criticalHigh;
1239     }
1240     if (thresholdData.criticalLow)
1241     {
1242         readable |=
1243             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1244         lowerCritical = *thresholdData.criticalLow;
1245     }
1246 
1247     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1248                                  lowerNonRecoverable, upperNC, upperCritical,
1249                                  upperNonRecoverable);
1250 }
1251 
1252 /** @brief implements the get Sensor event enable command
1253  *  @param sensorNumber - sensor number
1254  *
1255  *  @returns IPMI completion code plus response data
1256  *   - enabled               - Sensor Event messages
1257  *   - assertionEnabledLsb   - Assertion event messages
1258  *   - assertionEnabledMsb   - Assertion event messages
1259  *   - deassertionEnabledLsb - Deassertion event messages
1260  *   - deassertionEnabledMsb - Deassertion event messages
1261  */
1262 
1263 ipmi::RspType<uint8_t, // enabled
1264               uint8_t, // assertionEnabledLsb
1265               uint8_t, // assertionEnabledMsb
1266               uint8_t, // deassertionEnabledLsb
1267               uint8_t> // deassertionEnabledMsb
1268     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1269 {
1270     std::string connection;
1271     std::string path;
1272 
1273     uint8_t enabled = 0;
1274     uint8_t assertionEnabledLsb = 0;
1275     uint8_t assertionEnabledMsb = 0;
1276     uint8_t deassertionEnabledLsb = 0;
1277     uint8_t deassertionEnabledMsb = 0;
1278 
1279     if (sensorNum == reservedSensorNumber)
1280     {
1281         return ipmi::responseInvalidFieldRequest();
1282     }
1283 
1284     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1285     if (status)
1286     {
1287         return ipmi::response(status);
1288     }
1289 
1290 #ifdef FEATURE_HYBRID_SENSORS
1291     if (auto sensor = findStaticSensor(path);
1292         sensor != ipmi::sensor::sensors.end() &&
1293         getSensorEventTypeFromPath(path) !=
1294             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1295     {
1296         enabled = static_cast<uint8_t>(
1297             IPMISensorEventEnableByte2::sensorScanningEnable);
1298         uint16_t assertionEnabled = 0;
1299         for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1300                                       ->second.begin()
1301                                       ->second.second)
1302         {
1303             assertionEnabled |= (1 << offsetValMap.first);
1304         }
1305         assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1306         assertionEnabledMsb =
1307             static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1308 
1309         return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1310                                      assertionEnabledMsb, deassertionEnabledLsb,
1311                                      deassertionEnabledMsb);
1312     }
1313 #endif
1314 
1315     DbusInterfaceMap sensorMap;
1316     if (!getSensorMap(ctx, connection, path, sensorMap))
1317     {
1318         return ipmi::responseResponseError();
1319     }
1320 
1321     auto warningInterface =
1322         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1323     auto criticalInterface =
1324         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1325     if ((warningInterface != sensorMap.end()) ||
1326         (criticalInterface != sensorMap.end()))
1327     {
1328         enabled = static_cast<uint8_t>(
1329             IPMISensorEventEnableByte2::sensorScanningEnable);
1330         if (warningInterface != sensorMap.end())
1331         {
1332             auto& warningMap = warningInterface->second;
1333 
1334             auto warningHigh = warningMap.find("WarningHigh");
1335             auto warningLow = warningMap.find("WarningLow");
1336             if (warningHigh != warningMap.end())
1337             {
1338                 assertionEnabledLsb |= static_cast<uint8_t>(
1339                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1340                 deassertionEnabledLsb |= static_cast<uint8_t>(
1341                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1342             }
1343             if (warningLow != warningMap.end())
1344             {
1345                 assertionEnabledLsb |= static_cast<uint8_t>(
1346                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1347                 deassertionEnabledLsb |= static_cast<uint8_t>(
1348                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1349             }
1350         }
1351         if (criticalInterface != sensorMap.end())
1352         {
1353             auto& criticalMap = criticalInterface->second;
1354 
1355             auto criticalHigh = criticalMap.find("CriticalHigh");
1356             auto criticalLow = criticalMap.find("CriticalLow");
1357 
1358             if (criticalHigh != criticalMap.end())
1359             {
1360                 assertionEnabledMsb |= static_cast<uint8_t>(
1361                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1362                 deassertionEnabledMsb |= static_cast<uint8_t>(
1363                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1364             }
1365             if (criticalLow != criticalMap.end())
1366             {
1367                 assertionEnabledLsb |= static_cast<uint8_t>(
1368                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1369                 deassertionEnabledLsb |= static_cast<uint8_t>(
1370                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1371             }
1372         }
1373     }
1374 
1375     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1376                                  assertionEnabledMsb, deassertionEnabledLsb,
1377                                  deassertionEnabledMsb);
1378 }
1379 
1380 /** @brief implements the get Sensor event status command
1381  *  @param sensorNumber - sensor number, FFh = reserved
1382  *
1383  *  @returns IPMI completion code plus response data
1384  *   - sensorEventStatus - Sensor Event messages state
1385  *   - assertions        - Assertion event messages
1386  *   - deassertions      - Deassertion event messages
1387  */
1388 ipmi::RspType<uint8_t,         // sensorEventStatus
1389               std::bitset<16>, // assertions
1390               std::bitset<16>  // deassertion
1391               >
1392     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1393 {
1394     if (sensorNum == reservedSensorNumber)
1395     {
1396         return ipmi::responseInvalidFieldRequest();
1397     }
1398 
1399     std::string connection;
1400     std::string path;
1401     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1402     if (status)
1403     {
1404         phosphor::logging::log<phosphor::logging::level::ERR>(
1405             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1406             phosphor::logging::entry("SENSOR=%d", sensorNum));
1407         return ipmi::response(status);
1408     }
1409 
1410 #ifdef FEATURE_HYBRID_SENSORS
1411     if (auto sensor = findStaticSensor(path);
1412         sensor != ipmi::sensor::sensors.end() &&
1413         getSensorEventTypeFromPath(path) !=
1414             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1415     {
1416         auto response = ipmi::sensor::get::mapDbusToAssertion(
1417             sensor->second, path, sensor->second.sensorInterface);
1418         std::bitset<16> assertions;
1419         // deassertions are not used.
1420         std::bitset<16> deassertions = 0;
1421         uint8_t sensorEventStatus;
1422         if (response.readingOrStateUnavailable)
1423         {
1424             sensorEventStatus |= static_cast<uint8_t>(
1425                 IPMISensorReadingByte2::readingStateUnavailable);
1426         }
1427         if (response.scanningEnabled)
1428         {
1429             sensorEventStatus |= static_cast<uint8_t>(
1430                 IPMISensorReadingByte2::sensorScanningEnable);
1431         }
1432         if (response.allEventMessagesEnabled)
1433         {
1434             sensorEventStatus |= static_cast<uint8_t>(
1435                 IPMISensorReadingByte2::eventMessagesEnable);
1436         }
1437         assertions |= response.discreteReadingSensorStates << 8;
1438         assertions |= response.thresholdLevelsStates;
1439         return ipmi::responseSuccess(sensorEventStatus, assertions,
1440                                      deassertions);
1441     }
1442 #endif
1443 
1444     DbusInterfaceMap sensorMap;
1445     if (!getSensorMap(ctx, connection, path, sensorMap))
1446     {
1447         phosphor::logging::log<phosphor::logging::level::ERR>(
1448             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1449             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1450         return ipmi::responseResponseError();
1451     }
1452 
1453     uint8_t sensorEventStatus =
1454         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1455     std::bitset<16> assertions = 0;
1456     std::bitset<16> deassertions = 0;
1457 
1458     // handle VR typed sensor
1459     auto vrInterface = sensorMap.find(sensor::vrInterface);
1460     if (vrInterface != sensorMap.end())
1461     {
1462         if (!sensor::getVrEventStatus(ctx, connection, path,
1463                                       vrInterface->second, assertions))
1464         {
1465             return ipmi::responseResponseError();
1466         }
1467 
1468         // both Event Message and Sensor Scanning are disable for VR.
1469         sensorEventStatus = 0;
1470         return ipmi::responseSuccess(sensorEventStatus, assertions,
1471                                      deassertions);
1472     }
1473 
1474     auto warningInterface =
1475         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1476     auto criticalInterface =
1477         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1478 
1479     std::optional<bool> criticalDeassertHigh =
1480         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1481     std::optional<bool> criticalDeassertLow =
1482         thresholdDeassertMap[path]["CriticalAlarmLow"];
1483     std::optional<bool> warningDeassertHigh =
1484         thresholdDeassertMap[path]["WarningAlarmHigh"];
1485     std::optional<bool> warningDeassertLow =
1486         thresholdDeassertMap[path]["WarningAlarmLow"];
1487 
1488     if (criticalDeassertHigh && !*criticalDeassertHigh)
1489     {
1490         deassertions.set(static_cast<size_t>(
1491             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1492     }
1493     if (criticalDeassertLow && !*criticalDeassertLow)
1494     {
1495         deassertions.set(static_cast<size_t>(
1496             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1497     }
1498     if (warningDeassertHigh && !*warningDeassertHigh)
1499     {
1500         deassertions.set(static_cast<size_t>(
1501             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1502     }
1503     if (warningDeassertLow && !*warningDeassertLow)
1504     {
1505         deassertions.set(static_cast<size_t>(
1506             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1507     }
1508     if ((warningInterface != sensorMap.end()) ||
1509         (criticalInterface != sensorMap.end()))
1510     {
1511         sensorEventStatus = static_cast<size_t>(
1512             IPMISensorEventEnableByte2::eventMessagesEnable);
1513         if (warningInterface != sensorMap.end())
1514         {
1515             auto& warningMap = warningInterface->second;
1516 
1517             auto warningHigh = warningMap.find("WarningAlarmHigh");
1518             auto warningLow = warningMap.find("WarningAlarmLow");
1519             auto warningHighAlarm = false;
1520             auto warningLowAlarm = false;
1521 
1522             if (warningHigh != warningMap.end())
1523             {
1524                 warningHighAlarm = std::get<bool>(warningHigh->second);
1525             }
1526             if (warningLow != warningMap.end())
1527             {
1528                 warningLowAlarm = std::get<bool>(warningLow->second);
1529             }
1530             if (warningHighAlarm)
1531             {
1532                 assertions.set(
1533                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1534                                             upperNonCriticalGoingHigh));
1535             }
1536             if (warningLowAlarm)
1537             {
1538                 assertions.set(
1539                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1540                                             lowerNonCriticalGoingLow));
1541             }
1542         }
1543         if (criticalInterface != sensorMap.end())
1544         {
1545             auto& criticalMap = criticalInterface->second;
1546 
1547             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1548             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1549             auto criticalHighAlarm = false;
1550             auto criticalLowAlarm = false;
1551 
1552             if (criticalHigh != criticalMap.end())
1553             {
1554                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1555             }
1556             if (criticalLow != criticalMap.end())
1557             {
1558                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1559             }
1560             if (criticalHighAlarm)
1561             {
1562                 assertions.set(
1563                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1564                                             upperCriticalGoingHigh));
1565             }
1566             if (criticalLowAlarm)
1567             {
1568                 assertions.set(static_cast<size_t>(
1569                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1570             }
1571         }
1572     }
1573 
1574     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1575 }
1576 
1577 // Construct a type 1 SDR for threshold sensor.
1578 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1579                                  get_sdr::SensorDataFullRecord& record)
1580 {
1581     get_sdr::header::set_record_id(
1582         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1583 
1584     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1585     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1586 
1587     record.header.sdr_version = ipmiSdrVersion;
1588     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1589     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1590                                   sizeof(get_sdr::SensorDataRecordHeader);
1591     record.key.owner_id = bmcI2CAddr;
1592     record.key.owner_lun = lun;
1593     record.key.sensor_number = sensornumber;
1594 }
1595 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1596                         uint16_t recordID, const std::string& service,
1597                         const std::string& path,
1598                         get_sdr::SensorDataFullRecord& record)
1599 {
1600     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1601     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1602 
1603     DbusInterfaceMap sensorMap;
1604     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1605     {
1606         phosphor::logging::log<phosphor::logging::level::ERR>(
1607             "Failed to update sensor map for threshold sensor",
1608             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1609             phosphor::logging::entry("PATH=%s", path.c_str()));
1610         return false;
1611     }
1612 
1613     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1614     record.body.sensor_type = getSensorTypeFromPath(path);
1615     std::string type = getSensorTypeStringFromPath(path);
1616     auto typeCstr = type.c_str();
1617     auto findUnits = sensorUnits.find(typeCstr);
1618     if (findUnits != sensorUnits.end())
1619     {
1620         record.body.sensor_units_2_base =
1621             static_cast<uint8_t>(findUnits->second);
1622     } // else default 0x0 unspecified
1623 
1624     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1625 
1626     auto sensorObject = sensorMap.find(sensor::sensorInterface);
1627     if (sensorObject == sensorMap.end())
1628     {
1629         phosphor::logging::log<phosphor::logging::level::ERR>(
1630             "getSensorDataRecord: sensorObject error");
1631         return false;
1632     }
1633 
1634     uint8_t entityId = 0;
1635     uint8_t entityInstance = 0x01;
1636 
1637     // follow the association chain to get the parent board's entityid and
1638     // entityInstance
1639     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1640 
1641     record.body.entity_id = entityId;
1642     record.body.entity_instance = entityInstance;
1643 
1644     double max = 0;
1645     double min = 0;
1646     getSensorMaxMin(sensorMap, max, min);
1647 
1648     int16_t mValue = 0;
1649     int8_t rExp = 0;
1650     int16_t bValue = 0;
1651     int8_t bExp = 0;
1652     bool bSigned = false;
1653 
1654     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1655     {
1656         phosphor::logging::log<phosphor::logging::level::ERR>(
1657             "getSensorDataRecord: getSensorAttributes error");
1658         return false;
1659     }
1660 
1661     // The record.body is a struct SensorDataFullRecordBody
1662     // from sensorhandler.hpp in phosphor-ipmi-host.
1663     // The meaning of these bits appears to come from
1664     // table 43.1 of the IPMI spec.
1665     // The above 5 sensor attributes are stuffed in as follows:
1666     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1667     // Byte 22-24 are for other purposes
1668     // Byte 25 = MMMMMMMM = LSB of M
1669     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1670     // Byte 27 = BBBBBBBB = LSB of B
1671     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1672     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1673     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1674 
1675     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1676     record.body.m_lsb = mValue & 0xFF;
1677 
1678     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1679     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1680 
1681     // move the smallest bit of the MSB into place (bit 9)
1682     // the MSbs are bits 7:8 in m_msb_and_tolerance
1683     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1684 
1685     record.body.b_lsb = bValue & 0xFF;
1686 
1687     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1688     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1689 
1690     // move the smallest bit of the MSB into place (bit 9)
1691     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1692     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1693 
1694     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1695     uint8_t rExpBits = rExp & 0x07;
1696 
1697     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1698     uint8_t bExpBits = bExp & 0x07;
1699 
1700     // move rExp and bExp into place
1701     record.body.r_b_exponents =
1702         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1703 
1704     // Set the analog reading byte interpretation accordingly
1705     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1706 
1707     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1708     // These seem redundant, but derivable from the above 5 attributes
1709     // Original comment said "todo fill out rest of units"
1710 
1711     // populate sensor name from path
1712     auto name = sensor::parseSdrIdFromPath(path);
1713     record.body.id_string_info = name.size();
1714     std::strncpy(record.body.id_string, name.c_str(),
1715                  sizeof(record.body.id_string));
1716 
1717     // Remember the sensor name, as determined for this sensor number
1718     details::sdrStatsTable.updateName(sensornumber, name);
1719 
1720     bool sensorSettable = false;
1721     auto mutability =
1722         sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1723     if (mutability != sensorMap.end())
1724     {
1725         sensorSettable =
1726             mappedVariant<bool>(mutability->second, "Mutable", false);
1727     }
1728     get_sdr::body::init_settable_state(sensorSettable, &record.body);
1729 
1730     // Grant write permission to sensors deemed externally settable
1731     details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
1732 
1733     IPMIThresholds thresholdData;
1734     try
1735     {
1736         thresholdData = getIPMIThresholds(sensorMap);
1737     }
1738     catch (const std::exception&)
1739     {
1740         phosphor::logging::log<phosphor::logging::level::ERR>(
1741             "getSensorDataRecord: getIPMIThresholds error");
1742         return false;
1743     }
1744 
1745     if (thresholdData.criticalHigh)
1746     {
1747         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1748         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1749             IPMISensorEventEnableThresholds::criticalThreshold);
1750         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1751             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1752         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1753             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1754         record.body.discrete_reading_setting_mask[0] |=
1755             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1756     }
1757     if (thresholdData.warningHigh)
1758     {
1759         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1760         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1761             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1762         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1763             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1764         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1765             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1766         record.body.discrete_reading_setting_mask[0] |=
1767             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1768     }
1769     if (thresholdData.criticalLow)
1770     {
1771         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1772         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1773             IPMISensorEventEnableThresholds::criticalThreshold);
1774         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1775             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1776         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1777             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1778         record.body.discrete_reading_setting_mask[0] |=
1779             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1780     }
1781     if (thresholdData.warningLow)
1782     {
1783         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1784         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1785             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1786         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1787             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1788         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1789             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1790         record.body.discrete_reading_setting_mask[0] |=
1791             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1792     }
1793 
1794     // everything that is readable is setable
1795     record.body.discrete_reading_setting_mask[1] =
1796         record.body.discrete_reading_setting_mask[0];
1797     return true;
1798 }
1799 
1800 #ifdef FEATURE_HYBRID_SENSORS
1801 // Construct a type 1 SDR for discrete Sensor typed sensor.
1802 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1803                               uint16_t recordID,
1804                               ipmi::sensor::IdInfoMap::const_iterator sensor,
1805                               get_sdr::SensorDataFullRecord& record)
1806 {
1807     constructSensorSdrHeaderKey(sensorNum, recordID, record);
1808 
1809     record.body.entity_id = sensor->second.entityType;
1810     record.body.sensor_type = sensor->second.sensorType;
1811     record.body.event_reading_type = sensor->second.sensorReadingType;
1812     record.body.entity_instance = sensor->second.instance;
1813     if (ipmi::sensor::Mutability::Write ==
1814         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1815     {
1816         get_sdr::body::init_settable_state(true, &(record.body));
1817     }
1818 
1819     auto id_string = sensor->second.sensorName;
1820 
1821     if (id_string.empty())
1822     {
1823         id_string = sensor->second.sensorNameFunc(sensor->second);
1824     }
1825 
1826     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1827     {
1828         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1829                                      &(record.body));
1830     }
1831     else
1832     {
1833         get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1834     }
1835     std::strncpy(record.body.id_string, id_string.c_str(),
1836                  get_sdr::body::get_id_strlen(&(record.body)));
1837 }
1838 #endif
1839 
1840 // Construct type 3 SDR header and key (for VR and other discrete sensors)
1841 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1842                                 get_sdr::SensorDataEventRecord& record)
1843 {
1844     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1845     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1846 
1847     get_sdr::header::set_record_id(
1848         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1849 
1850     record.header.sdr_version = ipmiSdrVersion;
1851     record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1852     record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1853                                   sizeof(get_sdr::SensorDataRecordHeader);
1854     record.key.owner_id = bmcI2CAddr;
1855     record.key.owner_lun = lun;
1856     record.key.sensor_number = sensornumber;
1857 
1858     record.body.entity_id = 0x00;
1859     record.body.entity_instance = 0x01;
1860 }
1861 
1862 // Construct a type 3 SDR for VR typed sensor(daemon).
1863 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1864                     uint16_t recordID, const std::string& service,
1865                     const std::string& path,
1866                     get_sdr::SensorDataEventRecord& record)
1867 {
1868     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1869     constructEventSdrHeaderKey(sensorNum, recordID, record);
1870 
1871     DbusInterfaceMap sensorMap;
1872     if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1873     {
1874         phosphor::logging::log<phosphor::logging::level::ERR>(
1875             "Failed to update sensor map for VR sensor",
1876             phosphor::logging::entry("SERVICE=%s", service.c_str()),
1877             phosphor::logging::entry("PATH=%s", path.c_str()));
1878         return false;
1879     }
1880     // follow the association chain to get the parent board's entityid and
1881     // entityInstance
1882     updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1883                               record.body.entity_instance);
1884 
1885     // Sensor type is hardcoded as a module/board type instead of parsing from
1886     // sensor path. This is because VR control is allocated in an independent
1887     // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1888     // types.
1889     static constexpr const uint8_t module_board_type = 0x15;
1890     record.body.sensor_type = module_board_type;
1891     record.body.event_reading_type = 0x00;
1892 
1893     record.body.sensor_record_sharing_1 = 0x00;
1894     record.body.sensor_record_sharing_2 = 0x00;
1895 
1896     // populate sensor name from path
1897     auto name = sensor::parseSdrIdFromPath(path);
1898     int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1899     record.body.id_string_info = nameSize;
1900     std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1901     std::memcpy(record.body.id_string, name.c_str(), nameSize);
1902 
1903     // Remember the sensor name, as determined for this sensor number
1904     details::sdrStatsTable.updateName(sensornumber, name);
1905 
1906     return true;
1907 }
1908 
1909 static inline uint16_t getNumberOfSensors()
1910 {
1911     return std::min(getSensorTree().size(), maxIPMISensors);
1912 }
1913 
1914 static int
1915     getSensorDataRecord(ipmi::Context::ptr ctx,
1916                         std::vector<uint8_t>& recordData, uint16_t recordID,
1917                         uint8_t readBytes = std::numeric_limits<uint8_t>::max())
1918 {
1919     size_t fruCount = 0;
1920     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1921     if (ret != ipmi::ccSuccess)
1922     {
1923         phosphor::logging::log<phosphor::logging::level::ERR>(
1924             "getSensorDataRecord: getFruSdrCount error");
1925         return GENERAL_ERROR;
1926     }
1927 
1928     size_t lastRecord =
1929         getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
1930     if (recordID == lastRecordIndex)
1931     {
1932         recordID = lastRecord;
1933     }
1934     if (recordID > lastRecord)
1935     {
1936         phosphor::logging::log<phosphor::logging::level::ERR>(
1937             "getSensorDataRecord: recordID > lastRecord error");
1938         return GENERAL_ERROR;
1939     }
1940 
1941     if (recordID >= getNumberOfSensors())
1942     {
1943         size_t fruIndex = recordID - getNumberOfSensors();
1944 
1945         if (fruIndex >= fruCount)
1946         {
1947             // handle type 12 hardcoded records
1948             size_t type12Index = fruIndex - fruCount;
1949             if (type12Index >= ipmi::storage::type12Count)
1950             {
1951                 phosphor::logging::log<phosphor::logging::level::ERR>(
1952                     "getSensorDataRecord: type12Index error");
1953                 return GENERAL_ERROR;
1954             }
1955             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1956         }
1957         else
1958         {
1959             // handle fru records
1960             get_sdr::SensorDataFruRecord data;
1961             ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1962             if (ret != IPMI_CC_OK)
1963             {
1964                 return GENERAL_ERROR;
1965             }
1966             data.header.record_id_msb = recordID >> 8;
1967             data.header.record_id_lsb = recordID & 0xFF;
1968             recordData.insert(recordData.end(), (uint8_t*)&data,
1969                               ((uint8_t*)&data) + sizeof(data));
1970         }
1971 
1972         return 0;
1973     }
1974 
1975     // Perform a incremental scan of the SDR Record ID's and translate the
1976     // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1977     // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1978     // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1979     // which has special meaning.
1980     std::string connection;
1981     std::string path;
1982     std::vector<std::string> interfaces;
1983     uint16_t sensNumFromRecID{recordID};
1984     if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
1985     {
1986         // LUN 0 has one reserved sensor number. Compensate here by adding one
1987         // to the record ID
1988         sensNumFromRecID = recordID + 1;
1989         ctx->lun = 1;
1990     }
1991     else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
1992     {
1993         // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
1994         // to the record ID. Skip all 256 sensors in LUN 2, as it has special
1995         // rules governing its use.
1996         sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
1997         ctx->lun = 3;
1998     }
1999 
2000     auto status =
2001         getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2002                             connection, path, &interfaces);
2003     if (status)
2004     {
2005         phosphor::logging::log<phosphor::logging::level::ERR>(
2006             "getSensorDataRecord: getSensorConnection error");
2007         return GENERAL_ERROR;
2008     }
2009     uint16_t sensorNum = getSensorNumberFromPath(path);
2010     // Return an error on LUN 2 assingments, and any sensor number beyond the
2011     // range of LUN 3
2012     if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2013         (sensorNum > lun3MaxSensorNum))
2014     {
2015         phosphor::logging::log<phosphor::logging::level::ERR>(
2016             "getSensorDataRecord: invalidSensorNumber");
2017         return GENERAL_ERROR;
2018     }
2019     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2020     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2021 
2022     if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2023         (lun != ctx->lun))
2024     {
2025         phosphor::logging::log<phosphor::logging::level::ERR>(
2026             "getSensorDataRecord: sensor record mismatch");
2027         return GENERAL_ERROR;
2028     }
2029 
2030     // Construct full record (SDR type 1) for the threshold sensors
2031     if (std::find(interfaces.begin(), interfaces.end(),
2032                   sensor::sensorInterface) != interfaces.end())
2033     {
2034         get_sdr::SensorDataFullRecord record = {0};
2035 
2036         // If the request doesn't read SDR body, construct only header and key
2037         // part to avoid additional DBus transaction.
2038         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2039         {
2040             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2041         }
2042         else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2043                                      record))
2044         {
2045             return GENERAL_ERROR;
2046         }
2047 
2048         recordData.insert(recordData.end(), (uint8_t*)&record,
2049                           ((uint8_t*)&record) + sizeof(record));
2050 
2051         return 0;
2052     }
2053 
2054 #ifdef FEATURE_HYBRID_SENSORS
2055     if (auto sensor = findStaticSensor(path);
2056         sensor != ipmi::sensor::sensors.end() &&
2057         getSensorEventTypeFromPath(path) !=
2058             static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2059     {
2060         get_sdr::SensorDataFullRecord record = {0};
2061 
2062         // If the request doesn't read SDR body, construct only header and key
2063         // part to avoid additional DBus transaction.
2064         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2065         {
2066             constructSensorSdrHeaderKey(sensorNum, recordID, record);
2067         }
2068         else
2069         {
2070             constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2071         }
2072 
2073         recordData.insert(recordData.end(), (uint8_t*)&record,
2074                           ((uint8_t*)&record) + sizeof(record));
2075 
2076         return 0;
2077     }
2078 #endif
2079 
2080     // Contruct SDR type 3 record for VR sensor (daemon)
2081     if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2082         interfaces.end())
2083     {
2084         get_sdr::SensorDataEventRecord record = {0};
2085 
2086         // If the request doesn't read SDR body, construct only header and key
2087         // part to avoid additional DBus transaction.
2088         if (readBytes <= sizeof(record.header) + sizeof(record.key))
2089         {
2090             constructEventSdrHeaderKey(sensorNum, recordID, record);
2091         }
2092         else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2093                                  record))
2094         {
2095             return GENERAL_ERROR;
2096         }
2097         recordData.insert(recordData.end(), (uint8_t*)&record,
2098                           ((uint8_t*)&record) + sizeof(record));
2099     }
2100 
2101     return 0;
2102 }
2103 
2104 /** @brief implements the get SDR Info command
2105  *  @param count - Operation
2106  *
2107  *  @returns IPMI completion code plus response data
2108  *   - sdrCount - sensor/SDR count
2109  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2110  */
2111 static ipmi::RspType<uint8_t, // respcount
2112                      uint8_t, // dynamic population flags
2113                      uint32_t // last time a sensor was added
2114                      >
2115     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2116                                std::optional<uint8_t> count)
2117 {
2118     auto& sensorTree = getSensorTree();
2119     uint8_t sdrCount = 0;
2120     uint16_t recordID = 0;
2121     std::vector<uint8_t> record;
2122     // Sensors are dynamically allocated, and there is at least one LUN
2123     uint8_t lunsAndDynamicPopulation = 0x80;
2124     constexpr uint8_t getSdrCount = 0x01;
2125     constexpr uint8_t getSensorCount = 0x00;
2126 
2127     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2128     {
2129         return ipmi::responseResponseError();
2130     }
2131     uint16_t numSensors = getNumberOfSensors();
2132     if (count.value_or(0) == getSdrCount)
2133     {
2134         // Count the number of Type 1 SDR entries assigned to the LUN
2135         while (!getSensorDataRecord(ctx, record, recordID++))
2136         {
2137             get_sdr::SensorDataRecordHeader* hdr =
2138                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
2139                     record.data());
2140             if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2141             {
2142                 get_sdr::SensorDataFullRecord* recordData =
2143                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
2144                         record.data());
2145                 if (ctx->lun == recordData->key.owner_lun)
2146                 {
2147                     sdrCount++;
2148                 }
2149             }
2150         }
2151     }
2152     else if (count.value_or(0) == getSensorCount)
2153     {
2154         // Return the number of sensors attached to the LUN
2155         if ((ctx->lun == 0) && (numSensors > 0))
2156         {
2157             sdrCount =
2158                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2159         }
2160         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2161         {
2162             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2163                            ? maxSensorsPerLUN
2164                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2165         }
2166         else if (ctx->lun == 3)
2167         {
2168             if (numSensors <= maxIPMISensors)
2169             {
2170                 sdrCount =
2171                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2172             }
2173             else
2174             {
2175                 // error
2176                 throw std::out_of_range(
2177                     "Maximum number of IPMI sensors exceeded.");
2178             }
2179         }
2180     }
2181     else
2182     {
2183         return ipmi::responseInvalidFieldRequest();
2184     }
2185 
2186     // Get Sensor count. This returns the number of sensors
2187     if (numSensors > 0)
2188     {
2189         lunsAndDynamicPopulation |= 1;
2190     }
2191     if (numSensors > maxSensorsPerLUN)
2192     {
2193         lunsAndDynamicPopulation |= 2;
2194     }
2195     if (numSensors >= (maxSensorsPerLUN * 2))
2196     {
2197         lunsAndDynamicPopulation |= 8;
2198     }
2199     if (numSensors > maxIPMISensors)
2200     {
2201         // error
2202         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2203     }
2204 
2205     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2206                                  sdrLastAdd);
2207 }
2208 
2209 /* end sensor commands */
2210 
2211 /* storage commands */
2212 
2213 ipmi::RspType<uint8_t,  // sdr version
2214               uint16_t, // record count
2215               uint16_t, // free space
2216               uint32_t, // most recent addition
2217               uint32_t, // most recent erase
2218               uint8_t   // operationSupport
2219               >
2220     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2221 {
2222     auto& sensorTree = getSensorTree();
2223     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2224     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2225     {
2226         return ipmi::responseResponseError();
2227     }
2228 
2229     size_t fruCount = 0;
2230     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2231     if (ret != ipmi::ccSuccess)
2232     {
2233         return ipmi::response(ret);
2234     }
2235 
2236     uint16_t recordCount =
2237         getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
2238 
2239     uint8_t operationSupport = static_cast<uint8_t>(
2240         SdrRepositoryInfoOps::overflow); // write not supported
2241 
2242     operationSupport |=
2243         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2244     operationSupport |= static_cast<uint8_t>(
2245         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2246     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2247                                  unspecifiedFreeSpace, sdrLastAdd,
2248                                  sdrLastRemove, operationSupport);
2249 }
2250 
2251 /** @brief implements the get SDR allocation info command
2252  *
2253  *  @returns IPMI completion code plus response data
2254  *   - allocUnits    - Number of possible allocation units
2255  *   - allocUnitSize - Allocation unit size in bytes.
2256  *   - allocUnitFree - Number of free allocation units
2257  *   - allocUnitLargestFree - Largest free block in allocation units
2258  *   - maxRecordSize    - Maximum record size in allocation units.
2259  */
2260 ipmi::RspType<uint16_t, // allocUnits
2261               uint16_t, // allocUnitSize
2262               uint16_t, // allocUnitFree
2263               uint16_t, // allocUnitLargestFree
2264               uint8_t   // maxRecordSize
2265               >
2266     ipmiStorageGetSDRAllocationInfo()
2267 {
2268     // 0000h unspecified number of alloc units
2269     constexpr uint16_t allocUnits = 0;
2270 
2271     constexpr uint16_t allocUnitFree = 0;
2272     constexpr uint16_t allocUnitLargestFree = 0;
2273     // only allow one block at a time
2274     constexpr uint8_t maxRecordSize = 1;
2275 
2276     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2277                                  allocUnitLargestFree, maxRecordSize);
2278 }
2279 
2280 /** @brief implements the reserve SDR command
2281  *  @returns IPMI completion code plus response data
2282  *   - sdrReservationID
2283  */
2284 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2285 {
2286     sdrReservationID++;
2287     if (sdrReservationID == 0)
2288     {
2289         sdrReservationID++;
2290     }
2291 
2292     return ipmi::responseSuccess(sdrReservationID);
2293 }
2294 
2295 ipmi::RspType<uint16_t,            // next record ID
2296               std::vector<uint8_t> // payload
2297               >
2298     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2299                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2300 {
2301     size_t fruCount = 0;
2302     // reservation required for partial reads with non zero offset into
2303     // record
2304     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2305     {
2306         phosphor::logging::log<phosphor::logging::level::ERR>(
2307             "ipmiStorageGetSDR: responseInvalidReservationId");
2308         return ipmi::responseInvalidReservationId();
2309     }
2310     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2311     if (ret != ipmi::ccSuccess)
2312     {
2313         phosphor::logging::log<phosphor::logging::level::ERR>(
2314             "ipmiStorageGetSDR: getFruSdrCount error");
2315         return ipmi::response(ret);
2316     }
2317 
2318     auto& sensorTree = getSensorTree();
2319     size_t lastRecord =
2320         getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
2321     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2322 
2323     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2324     {
2325         phosphor::logging::log<phosphor::logging::level::ERR>(
2326             "ipmiStorageGetSDR: getSensorSubtree error");
2327         return ipmi::responseResponseError();
2328     }
2329 
2330     std::vector<uint8_t> record;
2331     if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
2332     {
2333         phosphor::logging::log<phosphor::logging::level::ERR>(
2334             "ipmiStorageGetSDR: fail to get SDR");
2335         return ipmi::responseInvalidFieldRequest();
2336     }
2337     get_sdr::SensorDataRecordHeader* hdr =
2338         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2339     if (!hdr)
2340     {
2341         phosphor::logging::log<phosphor::logging::level::ERR>(
2342             "ipmiStorageGetSDR: record header is null");
2343         return ipmi::responseSuccess(nextRecordId, record);
2344     }
2345 
2346     size_t sdrLength =
2347         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2348     if (sdrLength < (offset + bytesToRead))
2349     {
2350         bytesToRead = sdrLength - offset;
2351     }
2352 
2353     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2354     if (!respStart)
2355     {
2356         phosphor::logging::log<phosphor::logging::level::ERR>(
2357             "ipmiStorageGetSDR: record is null");
2358         return ipmi::responseSuccess(nextRecordId, record);
2359     }
2360 
2361     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2362 
2363     return ipmi::responseSuccess(nextRecordId, recordData);
2364 }
2365 /* end storage commands */
2366 
2367 void registerSensorFunctions()
2368 {
2369     // <Platform Event>
2370     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2371                           ipmi::sensor_event::cmdPlatformEvent,
2372                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2373 
2374     // <Set Sensor Reading and Event Status>
2375     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2376                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2377                           ipmi::Privilege::Operator, ipmiSetSensorReading);
2378 
2379     // <Get Sensor Reading>
2380     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2381                           ipmi::sensor_event::cmdGetSensorReading,
2382                           ipmi::Privilege::User, ipmiSenGetSensorReading);
2383 
2384     // <Get Sensor Threshold>
2385     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2386                           ipmi::sensor_event::cmdGetSensorThreshold,
2387                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2388 
2389     // <Set Sensor Threshold>
2390     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2391                           ipmi::sensor_event::cmdSetSensorThreshold,
2392                           ipmi::Privilege::Operator,
2393                           ipmiSenSetSensorThresholds);
2394 
2395     // <Get Sensor Event Enable>
2396     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2397                           ipmi::sensor_event::cmdGetSensorEventEnable,
2398                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2399 
2400     // <Get Sensor Event Status>
2401     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2402                           ipmi::sensor_event::cmdGetSensorEventStatus,
2403                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2404 
2405     // register all storage commands for both Sensor and Storage command
2406     // versions
2407 
2408     // <Get SDR Repository Info>
2409     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2410                           ipmi::storage::cmdGetSdrRepositoryInfo,
2411                           ipmi::Privilege::User,
2412                           ipmiStorageGetSDRRepositoryInfo);
2413 
2414     // <Get Device SDR Info>
2415     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2416                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
2417                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2418 
2419     // <Get SDR Allocation Info>
2420     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2421                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2422                           ipmi::Privilege::User,
2423                           ipmiStorageGetSDRAllocationInfo);
2424 
2425     // <Reserve SDR Repo>
2426     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2427                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2428                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2429 
2430     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2431                           ipmi::storage::cmdReserveSdrRepository,
2432                           ipmi::Privilege::User, ipmiStorageReserveSDR);
2433 
2434     // <Get Sdr>
2435     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2436                           ipmi::sensor_event::cmdGetDeviceSdr,
2437                           ipmi::Privilege::User, ipmiStorageGetSDR);
2438 
2439     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2440                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2441                           ipmiStorageGetSDR);
2442 }
2443 } // namespace ipmi
2444