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