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