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