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