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