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