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