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