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