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