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