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 "sensorcommands.hpp"
18 
19 #include "commandutils.hpp"
20 #include "ipmi_to_redfish_hooks.hpp"
21 #include "sdrutils.hpp"
22 #include "sensorutils.hpp"
23 #include "storagecommands.hpp"
24 #include "types.hpp"
25 
26 #include <boost/algorithm/string.hpp>
27 #include <boost/container/flat_map.hpp>
28 #include <ipmid/api.hpp>
29 #include <ipmid/utils.hpp>
30 #include <phosphor-logging/log.hpp>
31 #include <sdbusplus/bus.hpp>
32 
33 #include <algorithm>
34 #include <array>
35 #include <chrono>
36 #include <cmath>
37 #include <cstring>
38 #include <iostream>
39 #include <map>
40 #include <memory>
41 #include <optional>
42 #include <stdexcept>
43 #include <string>
44 #include <utility>
45 #include <variant>
46 
47 namespace ipmi
48 {
49 using ManagedObjectType =
50     std::map<sdbusplus::message::object_path,
51              std::map<std::string, std::map<std::string, DbusVariant>>>;
52 
53 static constexpr int sensorMapUpdatePeriod = 10;
54 static constexpr int sensorMapSdrUpdatePeriod = 60;
55 
56 constexpr size_t maxSDRTotalSize =
57     76; // Largest SDR Record Size (type 01) + SDR Overheader Size
58 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
59 
60 static uint16_t sdrReservationID;
61 static uint32_t sdrLastAdd = noTimestamp;
62 static uint32_t sdrLastRemove = noTimestamp;
63 static constexpr size_t lastRecordIndex = 0xFFFF;
64 
65 // The IPMI spec defines four Logical Units (LUN), each capable of supporting
66 // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
67 // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
68 // number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
69 // location.
70 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
71 
72 static constexpr size_t lun0MaxSensorNum = 0xfe;
73 static constexpr size_t lun1MaxSensorNum = 0x1fe;
74 static constexpr size_t lun3MaxSensorNum = 0x3fe;
75 static constexpr int GENERAL_ERROR = -1;
76 
77 SensorSubTree sensorTree;
78 
79 static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
80 
81 // Specify the comparison required to sort and find char* map objects
82 struct CmpStr
83 {
84     bool operator()(const char* a, const char* b) const
85     {
86         return std::strcmp(a, b) < 0;
87     }
88 };
89 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
90     sensorUnits{{{"temperature", SensorUnits::degreesC},
91                  {"voltage", SensorUnits::volts},
92                  {"current", SensorUnits::amps},
93                  {"fan_tach", SensorUnits::rpm},
94                  {"power", SensorUnits::watts}}};
95 
96 void registerSensorFunctions() __attribute__((constructor));
97 
98 static sdbusplus::bus::match::match sensorAdded(
99     *getSdBus(),
100     "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
101     "sensors/'",
102     [](sdbusplus::message::message& m) {
103         sensorTree.clear();
104         sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
105                          std::chrono::system_clock::now().time_since_epoch())
106                          .count();
107     });
108 
109 static sdbusplus::bus::match::match sensorRemoved(
110     *getSdBus(),
111     "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
112     "sensors/'",
113     [](sdbusplus::message::message& m) {
114         sensorTree.clear();
115         sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
116                             std::chrono::system_clock::now().time_since_epoch())
117                             .count();
118     });
119 
120 // this keeps track of deassertions for sensor event status command. A
121 // deasertion can only happen if an assertion was seen first.
122 static boost::container::flat_map<
123     std::string, boost::container::flat_map<std::string, std::optional<bool>>>
124     thresholdDeassertMap;
125 
126 static sdbusplus::bus::match::match thresholdChanged(
127     *getSdBus(),
128     "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
129     "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
130     [](sdbusplus::message::message& m) {
131         boost::container::flat_map<std::string, std::variant<bool, double>>
132             values;
133         m.read(std::string(), values);
134 
135         auto findAssert =
136             std::find_if(values.begin(), values.end(), [](const auto& pair) {
137                 return pair.first.find("Alarm") != std::string::npos;
138             });
139         if (findAssert != values.end())
140         {
141             auto ptr = std::get_if<bool>(&(findAssert->second));
142             if (ptr == nullptr)
143             {
144                 phosphor::logging::log<phosphor::logging::level::ERR>(
145                     "thresholdChanged: Assert non bool");
146                 return;
147             }
148             if (*ptr)
149             {
150                 phosphor::logging::log<phosphor::logging::level::INFO>(
151                     "thresholdChanged: Assert",
152                     phosphor::logging::entry("SENSOR=%s", m.get_path()));
153                 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
154             }
155             else
156             {
157                 auto& value =
158                     thresholdDeassertMap[m.get_path()][findAssert->first];
159                 if (value)
160                 {
161                     phosphor::logging::log<phosphor::logging::level::INFO>(
162                         "thresholdChanged: deassert",
163                         phosphor::logging::entry("SENSOR=%s", m.get_path()));
164                     value = *ptr;
165                 }
166             }
167         }
168     });
169 
170 static void getSensorMaxMin(const SensorMap& sensorMap, double& max,
171                             double& min)
172 {
173     max = 127;
174     min = -128;
175 
176     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
177     auto critical =
178         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
179     auto warning =
180         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
181 
182     if (sensorObject != sensorMap.end())
183     {
184         auto maxMap = sensorObject->second.find("MaxValue");
185         auto minMap = sensorObject->second.find("MinValue");
186 
187         if (maxMap != sensorObject->second.end())
188         {
189             max = std::visit(VariantToDoubleVisitor(), maxMap->second);
190         }
191         if (minMap != sensorObject->second.end())
192         {
193             min = std::visit(VariantToDoubleVisitor(), minMap->second);
194         }
195     }
196     if (critical != sensorMap.end())
197     {
198         auto lower = critical->second.find("CriticalLow");
199         auto upper = critical->second.find("CriticalHigh");
200         if (lower != critical->second.end())
201         {
202             double value = std::visit(VariantToDoubleVisitor(), lower->second);
203             min = std::min(value, min);
204         }
205         if (upper != critical->second.end())
206         {
207             double value = std::visit(VariantToDoubleVisitor(), upper->second);
208             max = std::max(value, max);
209         }
210     }
211     if (warning != sensorMap.end())
212     {
213 
214         auto lower = warning->second.find("WarningLow");
215         auto upper = warning->second.find("WarningHigh");
216         if (lower != warning->second.end())
217         {
218             double value = std::visit(VariantToDoubleVisitor(), lower->second);
219             min = std::min(value, min);
220         }
221         if (upper != warning->second.end())
222         {
223             double value = std::visit(VariantToDoubleVisitor(), upper->second);
224             max = std::max(value, max);
225         }
226     }
227 }
228 
229 static bool getSensorMap(boost::asio::yield_context yield,
230                          std::string sensorConnection, std::string sensorPath,
231                          SensorMap& sensorMap,
232                          int updatePeriod = sensorMapUpdatePeriod)
233 {
234     static boost::container::flat_map<
235         std::string, std::chrono::time_point<std::chrono::steady_clock>>
236         updateTimeMap;
237 
238     auto updateFind = updateTimeMap.find(sensorConnection);
239     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
240     if (updateFind != updateTimeMap.end())
241     {
242         lastUpdate = updateFind->second;
243     }
244 
245     auto now = std::chrono::steady_clock::now();
246 
247     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
248             .count() > updatePeriod)
249     {
250         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
251         boost::system::error_code ec;
252         auto managedObjects = dbus->yield_method_call<ManagedObjectType>(
253             yield, ec, sensorConnection.c_str(), "/",
254             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
255         if (ec)
256         {
257             phosphor::logging::log<phosphor::logging::level::ERR>(
258                 "GetMangagedObjects for getSensorMap failed",
259                 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
260 
261             return false;
262         }
263 
264         SensorCache[sensorConnection] = managedObjects;
265         // Update time after finish building the map which allow the
266         // data to be cached for updatePeriod plus the build time.
267         updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
268     }
269     auto connection = SensorCache.find(sensorConnection);
270     if (connection == SensorCache.end())
271     {
272         return false;
273     }
274     auto path = connection->second.find(sensorPath);
275     if (path == connection->second.end())
276     {
277         return false;
278     }
279     sensorMap = path->second;
280 
281     return true;
282 }
283 
284 /* sensor commands */
285 namespace meHealth
286 {
287 constexpr const char* busname = "xyz.openbmc_project.NodeManagerProxy";
288 constexpr const char* path = "/xyz/openbmc_project/status/me";
289 constexpr const char* interface = "xyz.openbmc_project.SetHealth";
290 constexpr const char* method = "SetHealth";
291 constexpr const char* critical = "critical";
292 constexpr const char* warning = "warning";
293 constexpr const char* ok = "ok";
294 } // namespace meHealth
295 
296 static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable)
297 {
298     constexpr const std::array<uint8_t, 10> critical = {
299         0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE};
300     constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19,
301                                                       0x1A};
302 
303     std::string state;
304     if (std::find(critical.begin(), critical.end(), eventData2) !=
305         critical.end())
306     {
307         state = meHealth::critical;
308     }
309     // special case 0x3 as we only care about a few states
310     else if (eventData2 == 0x3)
311     {
312         if (eventData3 <= 0x2)
313         {
314             state = meHealth::warning;
315         }
316         else
317         {
318             return;
319         }
320     }
321     else if (std::find(warning.begin(), warning.end(), eventData2) !=
322              warning.end())
323     {
324         state = meHealth::warning;
325     }
326     else
327     {
328         return;
329     }
330     if (disable)
331     {
332         state = meHealth::ok;
333     }
334 
335     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
336     auto setHealth =
337         dbus->new_method_call(meHealth::busname, meHealth::path,
338                               meHealth::interface, meHealth::method);
339     setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state);
340     try
341     {
342         dbus->call(setHealth);
343     }
344     catch (sdbusplus::exception_t&)
345     {
346         phosphor::logging::log<phosphor::logging::level::ERR>(
347             "Failed to set ME Health");
348     }
349 }
350 
351 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
352                                      ipmi::message::Payload& p)
353 {
354     constexpr const uint8_t meId = 0x2C;
355     constexpr const uint8_t meSensorNum = 0x17;
356     constexpr const uint8_t disabled = 0x80;
357 
358     uint8_t generatorID = 0;
359     uint8_t evmRev = 0;
360     uint8_t sensorType = 0;
361     uint8_t sensorNum = 0;
362     uint8_t eventType = 0;
363     uint8_t eventData1 = 0;
364     std::optional<uint8_t> eventData2 = 0;
365     std::optional<uint8_t> eventData3 = 0;
366     ipmi::ChannelInfo chInfo;
367 
368     if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
369     {
370         phosphor::logging::log<phosphor::logging::level::ERR>(
371             "Failed to get Channel Info",
372             phosphor::logging::entry("CHANNEL=%d", ctx->channel));
373         return ipmi::responseUnspecifiedError();
374     }
375 
376     if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
377         ipmi::EChannelMediumType::systemInterface)
378     {
379 
380         p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
381                  eventData1, eventData2, eventData3);
382     }
383     else
384     {
385 
386         p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
387                  eventData2, eventData3);
388         generatorID = ctx->rqSA << 1;
389     }
390 
391     if (!p.fullyUnpacked())
392     {
393         return ipmi::responseReqDataLenInvalid();
394     }
395 
396     // Check for valid evmRev and Sensor Type(per Table 42 of spec)
397     if (evmRev != 0x04)
398     {
399         return ipmi::responseInvalidFieldRequest();
400     }
401     if ((sensorType > 0x2C) && (sensorType < 0xC0))
402     {
403         return ipmi::responseInvalidFieldRequest();
404     }
405 
406     // Send this request to the Redfish hooks to log it as a Redfish message
407     // instead.  There is no need to add it to the SEL, so just return success.
408     intel_oem::ipmi::sel::checkRedfishHooks(
409         generatorID, evmRev, sensorType, sensorNum, eventType, eventData1,
410         eventData2.value_or(0xFF), eventData3.value_or(0xFF));
411 
412     if (generatorID == meId && sensorNum == meSensorNum && eventData2 &&
413         eventData3)
414     {
415         setMeStatus(*eventData2, *eventData3, (eventType & disabled));
416     }
417 
418     return ipmi::responseSuccess();
419 }
420 
421 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
422     ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
423 {
424     std::string connection;
425     std::string path;
426 
427     if (sensnum == reservedSensorNumber)
428     {
429         return ipmi::responseInvalidFieldRequest();
430     }
431 
432     auto status = getSensorConnection(ctx, sensnum, connection, path);
433     if (status)
434     {
435         return ipmi::response(status);
436     }
437 
438     SensorMap sensorMap;
439     if (!getSensorMap(ctx->yield, connection, path, sensorMap))
440     {
441         return ipmi::responseResponseError();
442     }
443     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
444 
445     if (sensorObject == sensorMap.end() ||
446         sensorObject->second.find("Value") == sensorObject->second.end())
447     {
448         return ipmi::responseResponseError();
449     }
450     auto& valueVariant = sensorObject->second["Value"];
451     double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
452 
453     double max = 0;
454     double min = 0;
455     getSensorMaxMin(sensorMap, max, min);
456 
457     int16_t mValue = 0;
458     int16_t bValue = 0;
459     int8_t rExp = 0;
460     int8_t bExp = 0;
461     bool bSigned = false;
462 
463     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
464     {
465         return ipmi::responseResponseError();
466     }
467 
468     uint8_t value =
469         scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
470     uint8_t operation =
471         static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
472     operation |=
473         static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
474     bool notReading = std::isnan(reading);
475 
476     if (!notReading)
477     {
478         auto availableObject =
479             sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
480         if (availableObject != sensorMap.end())
481         {
482             auto findAvailable = availableObject->second.find("Available");
483             if (findAvailable != availableObject->second.end())
484             {
485                 bool* available = std::get_if<bool>(&(findAvailable->second));
486                 if (available && !(*available))
487                 {
488                     notReading = true;
489                 }
490             }
491         }
492     }
493 
494     if (notReading)
495     {
496         operation |= static_cast<uint8_t>(
497             IPMISensorReadingByte2::readingStateUnavailable);
498     }
499 
500     int byteValue;
501     if (bSigned)
502     {
503         byteValue = static_cast<int>(static_cast<int8_t>(value));
504     }
505     else
506     {
507         byteValue = static_cast<int>(static_cast<uint8_t>(value));
508     }
509 
510     // Keep stats on the reading just obtained, even if it is "NaN"
511     if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
512     {
513         // This is the first reading, show the coefficients
514         double step = (max - min) / 255.0;
515         std::cerr << "IPMI sensor " << details::sdrStatsTable.getName(sensnum)
516                   << ": Range min=" << min << " max=" << max
517                   << ", step=" << step
518                   << ", Coefficients mValue=" << static_cast<int>(mValue)
519                   << " rExp=" << static_cast<int>(rExp)
520                   << " bValue=" << static_cast<int>(bValue)
521                   << " bExp=" << static_cast<int>(bExp)
522                   << " bSigned=" << static_cast<int>(bSigned) << "\n";
523     };
524 
525     uint8_t thresholds = 0;
526 
527     auto warningObject =
528         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
529     if (warningObject != sensorMap.end())
530     {
531         auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
532         auto alarmLow = warningObject->second.find("WarningAlarmLow");
533         if (alarmHigh != warningObject->second.end())
534         {
535             if (std::get<bool>(alarmHigh->second))
536             {
537                 thresholds |= static_cast<uint8_t>(
538                     IPMISensorReadingByte3::upperNonCritical);
539             }
540         }
541         if (alarmLow != warningObject->second.end())
542         {
543             if (std::get<bool>(alarmLow->second))
544             {
545                 thresholds |= static_cast<uint8_t>(
546                     IPMISensorReadingByte3::lowerNonCritical);
547             }
548         }
549     }
550 
551     auto criticalObject =
552         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
553     if (criticalObject != sensorMap.end())
554     {
555         auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
556         auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
557         if (alarmHigh != criticalObject->second.end())
558         {
559             if (std::get<bool>(alarmHigh->second))
560             {
561                 thresholds |=
562                     static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
563             }
564         }
565         if (alarmLow != criticalObject->second.end())
566         {
567             if (std::get<bool>(alarmLow->second))
568             {
569                 thresholds |=
570                     static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
571             }
572         }
573     }
574 
575     // no discrete as of today so optional byte is never returned
576     return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
577 }
578 
579 /** @brief implements the Set Sensor threshold command
580  *  @param sensorNumber        - sensor number
581  *  @param lowerNonCriticalThreshMask
582  *  @param lowerCriticalThreshMask
583  *  @param lowerNonRecovThreshMask
584  *  @param upperNonCriticalThreshMask
585  *  @param upperCriticalThreshMask
586  *  @param upperNonRecovThreshMask
587  *  @param reserved
588  *  @param lowerNonCritical    - lower non-critical threshold
589  *  @param lowerCritical       - Lower critical threshold
590  *  @param lowerNonRecoverable - Lower non recovarable threshold
591  *  @param upperNonCritical    - Upper non-critical threshold
592  *  @param upperCritical       - Upper critical
593  *  @param upperNonRecoverable - Upper Non-recoverable
594  *
595  *  @returns IPMI completion code
596  */
597 ipmi::RspType<> ipmiSenSetSensorThresholds(
598     ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
599     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
600     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
601     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
602     uint8_t lowerCritical, uint8_t lowerNonRecoverable,
603     uint8_t upperNonCritical, uint8_t upperCritical,
604     uint8_t upperNonRecoverable)
605 {
606     constexpr uint8_t thresholdMask = 0xFF;
607 
608     if (sensorNum == reservedSensorNumber)
609     {
610         return ipmi::responseInvalidFieldRequest();
611     }
612 
613     if (reserved)
614     {
615         return ipmi::responseInvalidFieldRequest();
616     }
617 
618     // lower nc and upper nc not suppported on any sensor
619     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
620     {
621         return ipmi::responseInvalidFieldRequest();
622     }
623 
624     // if none of the threshold mask are set, nothing to do
625     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
626           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
627           upperCriticalThreshMask | upperNonRecovThreshMask))
628     {
629         return ipmi::responseSuccess();
630     }
631 
632     std::string connection;
633     std::string path;
634 
635     ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
636     if (status)
637     {
638         return ipmi::response(status);
639     }
640     SensorMap sensorMap;
641     if (!getSensorMap(ctx->yield, connection, path, sensorMap))
642     {
643         return ipmi::responseResponseError();
644     }
645 
646     double max = 0;
647     double min = 0;
648     getSensorMaxMin(sensorMap, max, min);
649 
650     int16_t mValue = 0;
651     int16_t bValue = 0;
652     int8_t rExp = 0;
653     int8_t bExp = 0;
654     bool bSigned = false;
655 
656     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
657     {
658         return ipmi::responseResponseError();
659     }
660 
661     // store a vector of property name, value to set, and interface
662     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
663 
664     // define the indexes of the tuple
665     constexpr uint8_t propertyName = 0;
666     constexpr uint8_t thresholdValue = 1;
667     constexpr uint8_t interface = 2;
668     // verifiy all needed fields are present
669     if (lowerCriticalThreshMask || upperCriticalThreshMask)
670     {
671         auto findThreshold =
672             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
673         if (findThreshold == sensorMap.end())
674         {
675             return ipmi::responseInvalidFieldRequest();
676         }
677         if (lowerCriticalThreshMask)
678         {
679             auto findLower = findThreshold->second.find("CriticalLow");
680             if (findLower == findThreshold->second.end())
681             {
682                 return ipmi::responseInvalidFieldRequest();
683             }
684             thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
685                                          findThreshold->first);
686         }
687         if (upperCriticalThreshMask)
688         {
689             auto findUpper = findThreshold->second.find("CriticalHigh");
690             if (findUpper == findThreshold->second.end())
691             {
692                 return ipmi::responseInvalidFieldRequest();
693             }
694             thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
695                                          findThreshold->first);
696         }
697     }
698     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
699     {
700         auto findThreshold =
701             sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
702         if (findThreshold == sensorMap.end())
703         {
704             return ipmi::responseInvalidFieldRequest();
705         }
706         if (lowerNonCriticalThreshMask)
707         {
708             auto findLower = findThreshold->second.find("WarningLow");
709             if (findLower == findThreshold->second.end())
710             {
711                 return ipmi::responseInvalidFieldRequest();
712             }
713             thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
714                                          findThreshold->first);
715         }
716         if (upperNonCriticalThreshMask)
717         {
718             auto findUpper = findThreshold->second.find("WarningHigh");
719             if (findUpper == findThreshold->second.end())
720             {
721                 return ipmi::responseInvalidFieldRequest();
722             }
723             thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
724                                          findThreshold->first);
725         }
726     }
727     for (const auto& property : thresholdsToSet)
728     {
729         // from section 36.3 in the IPMI Spec, assume all linear
730         double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
731                              (bValue * std::pow(10.0, bExp))) *
732                             std::pow(10.0, rExp);
733         setDbusProperty(
734             *getSdBus(), connection, path, std::get<interface>(property),
735             std::get<propertyName>(property), ipmi::Value(valueToSet));
736     }
737     return ipmi::responseSuccess();
738 }
739 
740 IPMIThresholds getIPMIThresholds(const SensorMap& sensorMap)
741 {
742     IPMIThresholds resp;
743     auto warningInterface =
744         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
745     auto criticalInterface =
746         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
747 
748     if ((warningInterface != sensorMap.end()) ||
749         (criticalInterface != sensorMap.end()))
750     {
751         auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
752 
753         if (sensorPair == sensorMap.end())
754         {
755             // should not have been able to find a sensor not implementing
756             // the sensor object
757             throw std::runtime_error("Invalid sensor map");
758         }
759 
760         double max = 0;
761         double min = 0;
762         getSensorMaxMin(sensorMap, max, min);
763 
764         int16_t mValue = 0;
765         int16_t bValue = 0;
766         int8_t rExp = 0;
767         int8_t bExp = 0;
768         bool bSigned = false;
769 
770         if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
771         {
772             throw std::runtime_error("Invalid sensor atrributes");
773         }
774         if (warningInterface != sensorMap.end())
775         {
776             auto& warningMap = warningInterface->second;
777 
778             auto warningHigh = warningMap.find("WarningHigh");
779             auto warningLow = warningMap.find("WarningLow");
780 
781             if (warningHigh != warningMap.end())
782             {
783 
784                 double value =
785                     std::visit(VariantToDoubleVisitor(), warningHigh->second);
786                 resp.warningHigh = scaleIPMIValueFromDouble(
787                     value, mValue, rExp, bValue, bExp, bSigned);
788             }
789             if (warningLow != warningMap.end())
790             {
791                 double value =
792                     std::visit(VariantToDoubleVisitor(), warningLow->second);
793                 resp.warningLow = scaleIPMIValueFromDouble(
794                     value, mValue, rExp, bValue, bExp, bSigned);
795             }
796         }
797         if (criticalInterface != sensorMap.end())
798         {
799             auto& criticalMap = criticalInterface->second;
800 
801             auto criticalHigh = criticalMap.find("CriticalHigh");
802             auto criticalLow = criticalMap.find("CriticalLow");
803 
804             if (criticalHigh != criticalMap.end())
805             {
806                 double value =
807                     std::visit(VariantToDoubleVisitor(), criticalHigh->second);
808                 resp.criticalHigh = scaleIPMIValueFromDouble(
809                     value, mValue, rExp, bValue, bExp, bSigned);
810             }
811             if (criticalLow != criticalMap.end())
812             {
813                 double value =
814                     std::visit(VariantToDoubleVisitor(), criticalLow->second);
815                 resp.criticalLow = scaleIPMIValueFromDouble(
816                     value, mValue, rExp, bValue, bExp, bSigned);
817             }
818         }
819     }
820     return resp;
821 }
822 
823 ipmi::RspType<uint8_t, // readable
824               uint8_t, // lowerNCrit
825               uint8_t, // lowerCrit
826               uint8_t, // lowerNrecoverable
827               uint8_t, // upperNC
828               uint8_t, // upperCrit
829               uint8_t> // upperNRecoverable
830     ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
831 {
832     std::string connection;
833     std::string path;
834 
835     if (sensorNumber == reservedSensorNumber)
836     {
837         return ipmi::responseInvalidFieldRequest();
838     }
839 
840     auto status = getSensorConnection(ctx, sensorNumber, connection, path);
841     if (status)
842     {
843         return ipmi::response(status);
844     }
845 
846     SensorMap sensorMap;
847     if (!getSensorMap(ctx->yield, connection, path, sensorMap))
848     {
849         return ipmi::responseResponseError();
850     }
851 
852     IPMIThresholds thresholdData;
853     try
854     {
855         thresholdData = getIPMIThresholds(sensorMap);
856     }
857     catch (std::exception&)
858     {
859         return ipmi::responseResponseError();
860     }
861 
862     uint8_t readable = 0;
863     uint8_t lowerNC = 0;
864     uint8_t lowerCritical = 0;
865     uint8_t lowerNonRecoverable = 0;
866     uint8_t upperNC = 0;
867     uint8_t upperCritical = 0;
868     uint8_t upperNonRecoverable = 0;
869 
870     if (thresholdData.warningHigh)
871     {
872         readable |=
873             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
874         upperNC = *thresholdData.warningHigh;
875     }
876     if (thresholdData.warningLow)
877     {
878         readable |=
879             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
880         lowerNC = *thresholdData.warningLow;
881     }
882 
883     if (thresholdData.criticalHigh)
884     {
885         readable |=
886             1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
887         upperCritical = *thresholdData.criticalHigh;
888     }
889     if (thresholdData.criticalLow)
890     {
891         readable |=
892             1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
893         lowerCritical = *thresholdData.criticalLow;
894     }
895 
896     return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
897                                  lowerNonRecoverable, upperNC, upperCritical,
898                                  upperNonRecoverable);
899 }
900 
901 /** @brief implements the get Sensor event enable command
902  *  @param sensorNumber - sensor number
903  *
904  *  @returns IPMI completion code plus response data
905  *   - enabled               - Sensor Event messages
906  *   - assertionEnabledLsb   - Assertion event messages
907  *   - assertionEnabledMsb   - Assertion event messages
908  *   - deassertionEnabledLsb - Deassertion event messages
909  *   - deassertionEnabledMsb - Deassertion event messages
910  */
911 
912 ipmi::RspType<uint8_t, // enabled
913               uint8_t, // assertionEnabledLsb
914               uint8_t, // assertionEnabledMsb
915               uint8_t, // deassertionEnabledLsb
916               uint8_t> // deassertionEnabledMsb
917     ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
918 {
919     std::string connection;
920     std::string path;
921 
922     uint8_t enabled = 0;
923     uint8_t assertionEnabledLsb = 0;
924     uint8_t assertionEnabledMsb = 0;
925     uint8_t deassertionEnabledLsb = 0;
926     uint8_t deassertionEnabledMsb = 0;
927 
928     if (sensorNum == reservedSensorNumber)
929     {
930         return ipmi::responseInvalidFieldRequest();
931     }
932 
933     auto status = getSensorConnection(ctx, sensorNum, connection, path);
934     if (status)
935     {
936         return ipmi::response(status);
937     }
938 
939     SensorMap sensorMap;
940     if (!getSensorMap(ctx->yield, connection, path, sensorMap))
941     {
942         return ipmi::responseResponseError();
943     }
944 
945     auto warningInterface =
946         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
947     auto criticalInterface =
948         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
949     if ((warningInterface != sensorMap.end()) ||
950         (criticalInterface != sensorMap.end()))
951     {
952         enabled = static_cast<uint8_t>(
953             IPMISensorEventEnableByte2::sensorScanningEnable);
954         if (warningInterface != sensorMap.end())
955         {
956             auto& warningMap = warningInterface->second;
957 
958             auto warningHigh = warningMap.find("WarningHigh");
959             auto warningLow = warningMap.find("WarningLow");
960             if (warningHigh != warningMap.end())
961             {
962                 assertionEnabledLsb |= static_cast<uint8_t>(
963                     IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
964                 deassertionEnabledLsb |= static_cast<uint8_t>(
965                     IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
966             }
967             if (warningLow != warningMap.end())
968             {
969                 assertionEnabledLsb |= static_cast<uint8_t>(
970                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
971                 deassertionEnabledLsb |= static_cast<uint8_t>(
972                     IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
973             }
974         }
975         if (criticalInterface != sensorMap.end())
976         {
977             auto& criticalMap = criticalInterface->second;
978 
979             auto criticalHigh = criticalMap.find("CriticalHigh");
980             auto criticalLow = criticalMap.find("CriticalLow");
981 
982             if (criticalHigh != criticalMap.end())
983             {
984                 assertionEnabledMsb |= static_cast<uint8_t>(
985                     IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
986                 deassertionEnabledMsb |= static_cast<uint8_t>(
987                     IPMISensorEventEnableThresholds::upperCriticalGoingLow);
988             }
989             if (criticalLow != criticalMap.end())
990             {
991                 assertionEnabledLsb |= static_cast<uint8_t>(
992                     IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
993                 deassertionEnabledLsb |= static_cast<uint8_t>(
994                     IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
995             }
996         }
997     }
998 
999     return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1000                                  assertionEnabledMsb, deassertionEnabledLsb,
1001                                  deassertionEnabledMsb);
1002 }
1003 
1004 /** @brief implements the get Sensor event status command
1005  *  @param sensorNumber - sensor number, FFh = reserved
1006  *
1007  *  @returns IPMI completion code plus response data
1008  *   - sensorEventStatus - Sensor Event messages state
1009  *   - assertions        - Assertion event messages
1010  *   - deassertions      - Deassertion event messages
1011  */
1012 ipmi::RspType<uint8_t,         // sensorEventStatus
1013               std::bitset<16>, // assertions
1014               std::bitset<16>  // deassertion
1015               >
1016     ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1017 {
1018     if (sensorNum == reservedSensorNumber)
1019     {
1020         return ipmi::responseInvalidFieldRequest();
1021     }
1022 
1023     std::string connection;
1024     std::string path;
1025     auto status = getSensorConnection(ctx, sensorNum, connection, path);
1026     if (status)
1027     {
1028         phosphor::logging::log<phosphor::logging::level::ERR>(
1029             "ipmiSenGetSensorEventStatus: Sensor connection Error",
1030             phosphor::logging::entry("SENSOR=%d", sensorNum));
1031         return ipmi::response(status);
1032     }
1033 
1034     SensorMap sensorMap;
1035     if (!getSensorMap(ctx->yield, connection, path, sensorMap))
1036     {
1037         phosphor::logging::log<phosphor::logging::level::ERR>(
1038             "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1039             phosphor::logging::entry("SENSOR=%s", path.c_str()));
1040         return ipmi::responseResponseError();
1041     }
1042     auto warningInterface =
1043         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1044     auto criticalInterface =
1045         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1046 
1047     uint8_t sensorEventStatus =
1048         static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1049 
1050     std::optional<bool> criticalDeassertHigh =
1051         thresholdDeassertMap[path]["CriticalAlarmHigh"];
1052     std::optional<bool> criticalDeassertLow =
1053         thresholdDeassertMap[path]["CriticalAlarmLow"];
1054     std::optional<bool> warningDeassertHigh =
1055         thresholdDeassertMap[path]["WarningAlarmHigh"];
1056     std::optional<bool> warningDeassertLow =
1057         thresholdDeassertMap[path]["WarningAlarmLow"];
1058 
1059     std::bitset<16> assertions = 0;
1060     std::bitset<16> deassertions = 0;
1061 
1062     if (criticalDeassertHigh && !*criticalDeassertHigh)
1063     {
1064         deassertions.set(static_cast<size_t>(
1065             IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1066     }
1067     if (criticalDeassertLow && !*criticalDeassertLow)
1068     {
1069         deassertions.set(static_cast<size_t>(
1070             IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1071     }
1072     if (warningDeassertHigh && !*warningDeassertHigh)
1073     {
1074         deassertions.set(static_cast<size_t>(
1075             IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1076     }
1077     if (warningDeassertLow && !*warningDeassertLow)
1078     {
1079         deassertions.set(static_cast<size_t>(
1080             IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1081     }
1082     if ((warningInterface != sensorMap.end()) ||
1083         (criticalInterface != sensorMap.end()))
1084     {
1085         sensorEventStatus = static_cast<size_t>(
1086             IPMISensorEventEnableByte2::eventMessagesEnable);
1087         if (warningInterface != sensorMap.end())
1088         {
1089             auto& warningMap = warningInterface->second;
1090 
1091             auto warningHigh = warningMap.find("WarningAlarmHigh");
1092             auto warningLow = warningMap.find("WarningAlarmLow");
1093             auto warningHighAlarm = false;
1094             auto warningLowAlarm = false;
1095 
1096             if (warningHigh != warningMap.end())
1097             {
1098                 warningHighAlarm = std::get<bool>(warningHigh->second);
1099             }
1100             if (warningLow != warningMap.end())
1101             {
1102                 warningLowAlarm = std::get<bool>(warningLow->second);
1103             }
1104             if (warningHighAlarm)
1105             {
1106                 assertions.set(
1107                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1108                                             upperNonCriticalGoingHigh));
1109             }
1110             if (warningLowAlarm)
1111             {
1112                 assertions.set(
1113                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1114                                             lowerNonCriticalGoingLow));
1115             }
1116         }
1117         if (criticalInterface != sensorMap.end())
1118         {
1119             auto& criticalMap = criticalInterface->second;
1120 
1121             auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1122             auto criticalLow = criticalMap.find("CriticalAlarmLow");
1123             auto criticalHighAlarm = false;
1124             auto criticalLowAlarm = false;
1125 
1126             if (criticalHigh != criticalMap.end())
1127             {
1128                 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1129             }
1130             if (criticalLow != criticalMap.end())
1131             {
1132                 criticalLowAlarm = std::get<bool>(criticalLow->second);
1133             }
1134             if (criticalHighAlarm)
1135             {
1136                 assertions.set(
1137                     static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1138                                             upperCriticalGoingHigh));
1139             }
1140             if (criticalLowAlarm)
1141             {
1142                 assertions.set(static_cast<size_t>(
1143                     IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1144             }
1145         }
1146     }
1147 
1148     return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1149 }
1150 
1151 static inline uint16_t getNumberOfSensors(void)
1152 {
1153     return sensorTree.size() > maxIPMISensors ? maxIPMISensors
1154                                               : sensorTree.size();
1155 }
1156 
1157 static int getSensorDataRecord(ipmi::Context::ptr ctx,
1158                                std::vector<uint8_t>& recordData,
1159                                uint16_t recordID)
1160 {
1161     size_t fruCount = 0;
1162     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1163     if (ret != ipmi::ccSuccess)
1164     {
1165         phosphor::logging::log<phosphor::logging::level::ERR>(
1166             "getSensorDataRecord: getFruSdrCount error");
1167         return GENERAL_ERROR;
1168     }
1169 
1170     size_t lastRecord = getNumberOfSensors() + fruCount +
1171                         ipmi::storage::type12Count +
1172                         ipmi::storage::nmDiscoverySDRCount - 1;
1173     if (recordID == lastRecordIndex)
1174     {
1175         recordID = lastRecord;
1176     }
1177     if (recordID > lastRecord)
1178     {
1179         phosphor::logging::log<phosphor::logging::level::ERR>(
1180             "getSensorDataRecord: recordID > lastRecord error");
1181         return GENERAL_ERROR;
1182     }
1183 
1184     if (recordID >= getNumberOfSensors())
1185     {
1186         size_t fruIndex = recordID - getNumberOfSensors();
1187         size_t type12End = fruCount + ipmi::storage::type12Count;
1188 
1189         if (fruIndex >= type12End)
1190         {
1191             // NM discovery SDR
1192             size_t nmDiscoveryIndex = fruIndex - type12End;
1193             if (nmDiscoveryIndex >= ipmi::storage::nmDiscoverySDRCount)
1194             {
1195                 phosphor::logging::log<phosphor::logging::level::ERR>(
1196                     "getSensorDataRecord: NM DiscoveryIndex error");
1197                 return GENERAL_ERROR;
1198             }
1199             recordData =
1200                 ipmi::storage::getNMDiscoverySDR(nmDiscoveryIndex, recordID);
1201         }
1202         else if (fruIndex >= fruCount)
1203         {
1204             // handle type 12 hardcoded records
1205             size_t type12Index = fruIndex - fruCount;
1206             if (type12Index >= ipmi::storage::type12Count)
1207             {
1208                 phosphor::logging::log<phosphor::logging::level::ERR>(
1209                     "getSensorDataRecord: type12Index error");
1210                 return GENERAL_ERROR;
1211             }
1212             recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1213         }
1214         else
1215         {
1216             // handle fru records
1217             get_sdr::SensorDataFruRecord data;
1218             ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1219             if (ret != IPMI_CC_OK)
1220             {
1221                 return GENERAL_ERROR;
1222             }
1223             data.header.record_id_msb = recordID >> 8;
1224             data.header.record_id_lsb = recordID & 0xFF;
1225             recordData.insert(recordData.end(), (uint8_t*)&data,
1226                               ((uint8_t*)&data) + sizeof(data));
1227         }
1228 
1229         return 0;
1230     }
1231 
1232     // Perform a incremental scan of the SDR Record ID's and translate the
1233     // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1234     // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1235     // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1236     // which has special meaning.
1237     std::string connection;
1238     std::string path;
1239     uint16_t sensNumFromRecID{recordID};
1240     if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
1241     {
1242         // LUN 0 has one reserved sensor number. Compensate here by adding one
1243         // to the record ID
1244         sensNumFromRecID = recordID + 1;
1245         ctx->lun = 1;
1246     }
1247     else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
1248     {
1249         // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
1250         // to the record ID. Skip all 256 sensors in LUN 2, as it has special
1251         // rules governing its use.
1252         sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
1253         ctx->lun = 3;
1254     }
1255 
1256     auto status = getSensorConnection(
1257         ctx, static_cast<uint8_t>(sensNumFromRecID), connection, path);
1258     if (status)
1259     {
1260         phosphor::logging::log<phosphor::logging::level::ERR>(
1261             "getSensorDataRecord: getSensorConnection error");
1262         return GENERAL_ERROR;
1263     }
1264     SensorMap sensorMap;
1265     if (!getSensorMap(ctx->yield, connection, path, sensorMap,
1266                       sensorMapUpdatePeriod))
1267     {
1268         phosphor::logging::log<phosphor::logging::level::ERR>(
1269             "getSensorDataRecord: getSensorMap error");
1270         return GENERAL_ERROR;
1271     }
1272     uint16_t sensorNum = getSensorNumberFromPath(path);
1273     // Return an error on LUN 2 assingments, and any sensor number beyond the
1274     // range of LUN 3
1275     if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
1276         (sensorNum > lun3MaxSensorNum))
1277     {
1278         phosphor::logging::log<phosphor::logging::level::ERR>(
1279             "getSensorDataRecord: invalidSensorNumber");
1280         return GENERAL_ERROR;
1281     }
1282     uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1283     uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1284 
1285     if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
1286         (lun != ctx->lun))
1287     {
1288         phosphor::logging::log<phosphor::logging::level::ERR>(
1289             "getSensorDataRecord: sensor record mismatch");
1290         return GENERAL_ERROR;
1291     }
1292     get_sdr::SensorDataFullRecord record = {0};
1293 
1294     get_sdr::header::set_record_id(
1295         recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1296 
1297     record.header.sdr_version = ipmiSdrVersion;
1298     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1299     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1300                                   sizeof(get_sdr::SensorDataRecordHeader);
1301     record.key.owner_id = 0x20;
1302     record.key.owner_lun = lun;
1303     record.key.sensor_number = sensornumber;
1304 
1305     record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1306     record.body.sensor_type = getSensorTypeFromPath(path);
1307     std::string type = getSensorTypeStringFromPath(path);
1308     auto typeCstr = type.c_str();
1309     auto findUnits = sensorUnits.find(typeCstr);
1310     if (findUnits != sensorUnits.end())
1311     {
1312         record.body.sensor_units_2_base =
1313             static_cast<uint8_t>(findUnits->second);
1314     } // else default 0x0 unspecified
1315 
1316     record.body.event_reading_type = getSensorEventTypeFromPath(path);
1317 
1318     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
1319     if (sensorObject == sensorMap.end())
1320     {
1321         phosphor::logging::log<phosphor::logging::level::ERR>(
1322             "getSensorDataRecord: sensorObject error");
1323         return GENERAL_ERROR;
1324     }
1325 
1326     uint8_t entityId = 0;
1327     uint8_t entityInstance = 0x01;
1328 
1329     // follow the association chain to get the parent board's entityid and
1330     // entityInstance
1331     updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1332 
1333     record.body.entity_id = entityId;
1334     record.body.entity_instance = entityInstance;
1335 
1336     auto maxObject = sensorObject->second.find("MaxValue");
1337     auto minObject = sensorObject->second.find("MinValue");
1338 
1339     // If min and/or max are left unpopulated,
1340     // then default to what a signed byte would be, namely (-128,127) range.
1341     auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1342     auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1343     if (maxObject != sensorObject->second.end())
1344     {
1345         max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1346     }
1347 
1348     if (minObject != sensorObject->second.end())
1349     {
1350         min = std::visit(VariantToDoubleVisitor(), minObject->second);
1351     }
1352 
1353     int16_t mValue = 0;
1354     int8_t rExp = 0;
1355     int16_t bValue = 0;
1356     int8_t bExp = 0;
1357     bool bSigned = false;
1358 
1359     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1360     {
1361         phosphor::logging::log<phosphor::logging::level::ERR>(
1362             "getSensorDataRecord: getSensorAttributes error");
1363         return GENERAL_ERROR;
1364     }
1365 
1366     // The record.body is a struct SensorDataFullRecordBody
1367     // from sensorhandler.hpp in phosphor-ipmi-host.
1368     // The meaning of these bits appears to come from
1369     // table 43.1 of the IPMI spec.
1370     // The above 5 sensor attributes are stuffed in as follows:
1371     // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1372     // Byte 22-24 are for other purposes
1373     // Byte 25 = MMMMMMMM = LSB of M
1374     // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1375     // Byte 27 = BBBBBBBB = LSB of B
1376     // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1377     // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1378     // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1379 
1380     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1381     record.body.m_lsb = mValue & 0xFF;
1382 
1383     uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1384     uint8_t mBitNine = (mValue & 0x0100) >> 8;
1385 
1386     // move the smallest bit of the MSB into place (bit 9)
1387     // the MSbs are bits 7:8 in m_msb_and_tolerance
1388     record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1389 
1390     record.body.b_lsb = bValue & 0xFF;
1391 
1392     uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1393     uint8_t bBitNine = (bValue & 0x0100) >> 8;
1394 
1395     // move the smallest bit of the MSB into place (bit 9)
1396     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1397     record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1398 
1399     uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1400     uint8_t rExpBits = rExp & 0x07;
1401 
1402     uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1403     uint8_t bExpBits = bExp & 0x07;
1404 
1405     // move rExp and bExp into place
1406     record.body.r_b_exponents =
1407         (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1408 
1409     // Set the analog reading byte interpretation accordingly
1410     record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1411 
1412     // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1413     // These seem redundant, but derivable from the above 5 attributes
1414     // Original comment said "todo fill out rest of units"
1415 
1416     // populate sensor name from path
1417     std::string name;
1418     size_t nameStart = path.rfind("/");
1419     if (nameStart != std::string::npos)
1420     {
1421         name = path.substr(nameStart + 1, std::string::npos - nameStart);
1422     }
1423 
1424     std::replace(name.begin(), name.end(), '_', ' ');
1425     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
1426     {
1427         // try to not truncate by replacing common words
1428         constexpr std::array<std::pair<const char*, const char*>, 2>
1429             replaceWords = {std::make_pair("Output", "Out"),
1430                             std::make_pair("Input", "In")};
1431         for (const auto& [find, replace] : replaceWords)
1432         {
1433             boost::replace_all(name, find, replace);
1434         }
1435 
1436         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
1437     }
1438     record.body.id_string_info = name.size();
1439     std::strncpy(record.body.id_string, name.c_str(),
1440                  sizeof(record.body.id_string));
1441 
1442     // Remember the sensor name, as determined for this sensor number
1443     details::sdrStatsTable.updateName(sensornumber, name);
1444 
1445     IPMIThresholds thresholdData;
1446     try
1447     {
1448         thresholdData = getIPMIThresholds(sensorMap);
1449     }
1450     catch (std::exception&)
1451     {
1452         phosphor::logging::log<phosphor::logging::level::ERR>(
1453             "getSensorDataRecord: getIPMIThresholds error");
1454         return GENERAL_ERROR;
1455     }
1456 
1457     if (thresholdData.criticalHigh)
1458     {
1459         record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1460         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1461             IPMISensorEventEnableThresholds::criticalThreshold);
1462         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1463             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1464         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1465             IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1466         record.body.discrete_reading_setting_mask[0] |=
1467             static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1468     }
1469     if (thresholdData.warningHigh)
1470     {
1471         record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1472         record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1473             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1474         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1475             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1476         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1477             IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1478         record.body.discrete_reading_setting_mask[0] |=
1479             static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1480     }
1481     if (thresholdData.criticalLow)
1482     {
1483         record.body.lower_critical_threshold = *thresholdData.criticalLow;
1484         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1485             IPMISensorEventEnableThresholds::criticalThreshold);
1486         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1487             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1488         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1489             IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1490         record.body.discrete_reading_setting_mask[0] |=
1491             static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1492     }
1493     if (thresholdData.warningLow)
1494     {
1495         record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1496         record.body.supported_assertions[1] |= static_cast<uint8_t>(
1497             IPMISensorEventEnableThresholds::nonCriticalThreshold);
1498         record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1499             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1500         record.body.supported_assertions[0] |= static_cast<uint8_t>(
1501             IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1502         record.body.discrete_reading_setting_mask[0] |=
1503             static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1504     }
1505 
1506     // everything that is readable is setable
1507     record.body.discrete_reading_setting_mask[1] =
1508         record.body.discrete_reading_setting_mask[0];
1509     recordData.insert(recordData.end(), (uint8_t*)&record,
1510                       ((uint8_t*)&record) + sizeof(record));
1511     return 0;
1512 }
1513 
1514 /** @brief implements the get SDR Info command
1515  *  @param count - Operation
1516  *
1517  *  @returns IPMI completion code plus response data
1518  *   - sdrCount - sensor/SDR count
1519  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1520  */
1521 static ipmi::RspType<uint8_t, // respcount
1522                      uint8_t, // dynamic population flags
1523                      uint32_t // last time a sensor was added
1524                      >
1525     ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1526                                std::optional<uint8_t> count)
1527 {
1528     uint8_t sdrCount = 0;
1529     uint16_t recordID = 0;
1530     std::vector<uint8_t> record;
1531     // Sensors are dynamically allocated, and there is at least one LUN
1532     uint8_t lunsAndDynamicPopulation = 0x80;
1533     constexpr uint8_t getSdrCount = 0x01;
1534     constexpr uint8_t getSensorCount = 0x00;
1535 
1536     if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1537     {
1538         return ipmi::responseResponseError();
1539     }
1540     uint16_t numSensors = getNumberOfSensors();
1541     if (count.value_or(0) == getSdrCount)
1542     {
1543         // Count the number of Type 1 SDR entries assigned to the LUN
1544         while (!getSensorDataRecord(ctx, record, recordID++))
1545         {
1546             get_sdr::SensorDataRecordHeader* hdr =
1547                 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
1548                     record.data());
1549             if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1550             {
1551                 get_sdr::SensorDataFullRecord* recordData =
1552                     reinterpret_cast<get_sdr::SensorDataFullRecord*>(
1553                         record.data());
1554                 if (ctx->lun == recordData->key.owner_lun)
1555                 {
1556                     sdrCount++;
1557                 }
1558             }
1559         }
1560     }
1561     else if (count.value_or(0) == getSensorCount)
1562     {
1563         // Return the number of sensors attached to the LUN
1564         if ((ctx->lun == 0) && (numSensors > 0))
1565         {
1566             sdrCount =
1567                 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
1568         }
1569         else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
1570         {
1571             sdrCount = (numSensors > (2 * maxSensorsPerLUN))
1572                            ? maxSensorsPerLUN
1573                            : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
1574         }
1575         else if (ctx->lun == 3)
1576         {
1577             if (numSensors <= maxIPMISensors)
1578             {
1579                 sdrCount =
1580                     (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
1581             }
1582             else
1583             {
1584                 // error
1585                 throw std::out_of_range(
1586                     "Maximum number of IPMI sensors exceeded.");
1587             }
1588         }
1589     }
1590     else
1591     {
1592         return ipmi::responseInvalidFieldRequest();
1593     }
1594 
1595     // Get Sensor count. This returns the number of sensors
1596     if (numSensors > 0)
1597     {
1598         lunsAndDynamicPopulation |= 1;
1599     }
1600     if (numSensors > maxSensorsPerLUN)
1601     {
1602         lunsAndDynamicPopulation |= 2;
1603     }
1604     if (numSensors >= (maxSensorsPerLUN * 2))
1605     {
1606         lunsAndDynamicPopulation |= 8;
1607     }
1608     if (numSensors > maxIPMISensors)
1609     {
1610         // error
1611         throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
1612     }
1613 
1614     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
1615                                  sdrLastAdd);
1616 }
1617 
1618 /* end sensor commands */
1619 
1620 /* storage commands */
1621 
1622 ipmi::RspType<uint8_t,  // sdr version
1623               uint16_t, // record count
1624               uint16_t, // free space
1625               uint32_t, // most recent addition
1626               uint32_t, // most recent erase
1627               uint8_t   // operationSupport
1628               >
1629     ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
1630 {
1631     constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
1632     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
1633     {
1634         return ipmi::responseResponseError();
1635     }
1636 
1637     size_t fruCount = 0;
1638     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1639     if (ret != ipmi::ccSuccess)
1640     {
1641         return ipmi::response(ret);
1642     }
1643 
1644     uint16_t recordCount = getNumberOfSensors() + fruCount +
1645                            ipmi::storage::type12Count +
1646                            ipmi::storage::nmDiscoverySDRCount;
1647 
1648     uint8_t operationSupport = static_cast<uint8_t>(
1649         SdrRepositoryInfoOps::overflow); // write not supported
1650 
1651     operationSupport |=
1652         static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
1653     operationSupport |= static_cast<uint8_t>(
1654         SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
1655     return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
1656                                  unspecifiedFreeSpace, sdrLastAdd,
1657                                  sdrLastRemove, operationSupport);
1658 }
1659 
1660 /** @brief implements the get SDR allocation info command
1661  *
1662  *  @returns IPMI completion code plus response data
1663  *   - allocUnits    - Number of possible allocation units
1664  *   - allocUnitSize - Allocation unit size in bytes.
1665  *   - allocUnitFree - Number of free allocation units
1666  *   - allocUnitLargestFree - Largest free block in allocation units
1667  *   - maxRecordSize    - Maximum record size in allocation units.
1668  */
1669 ipmi::RspType<uint16_t, // allocUnits
1670               uint16_t, // allocUnitSize
1671               uint16_t, // allocUnitFree
1672               uint16_t, // allocUnitLargestFree
1673               uint8_t   // maxRecordSize
1674               >
1675     ipmiStorageGetSDRAllocationInfo()
1676 {
1677     // 0000h unspecified number of alloc units
1678     constexpr uint16_t allocUnits = 0;
1679 
1680     constexpr uint16_t allocUnitFree = 0;
1681     constexpr uint16_t allocUnitLargestFree = 0;
1682     // only allow one block at a time
1683     constexpr uint8_t maxRecordSize = 1;
1684 
1685     return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
1686                                  allocUnitLargestFree, maxRecordSize);
1687 }
1688 
1689 /** @brief implements the reserve SDR command
1690  *  @returns IPMI completion code plus response data
1691  *   - sdrReservationID
1692  */
1693 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
1694 {
1695     sdrReservationID++;
1696     if (sdrReservationID == 0)
1697     {
1698         sdrReservationID++;
1699     }
1700 
1701     return ipmi::responseSuccess(sdrReservationID);
1702 }
1703 
1704 ipmi::RspType<uint16_t,            // next record ID
1705               std::vector<uint8_t> // payload
1706               >
1707     ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
1708                       uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1709 {
1710     size_t fruCount = 0;
1711     // reservation required for partial reads with non zero offset into
1712     // record
1713     if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
1714     {
1715         phosphor::logging::log<phosphor::logging::level::ERR>(
1716             "ipmiStorageGetSDR: responseInvalidReservationId");
1717         return ipmi::responseInvalidReservationId();
1718     }
1719     ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1720     if (ret != ipmi::ccSuccess)
1721     {
1722         phosphor::logging::log<phosphor::logging::level::ERR>(
1723             "ipmiStorageGetSDR: getFruSdrCount error");
1724         return ipmi::response(ret);
1725     }
1726 
1727     size_t lastRecord = getNumberOfSensors() + fruCount +
1728                         ipmi::storage::type12Count +
1729                         ipmi::storage::nmDiscoverySDRCount - 1;
1730     uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
1731 
1732     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
1733     {
1734         phosphor::logging::log<phosphor::logging::level::ERR>(
1735             "ipmiStorageGetSDR: getSensorSubtree error");
1736         return ipmi::responseResponseError();
1737     }
1738 
1739     std::vector<uint8_t> record;
1740     if (getSensorDataRecord(ctx, record, recordID))
1741     {
1742         phosphor::logging::log<phosphor::logging::level::ERR>(
1743             "ipmiStorageGetSDR: fail to get SDR");
1744         return ipmi::responseInvalidFieldRequest();
1745     }
1746     get_sdr::SensorDataRecordHeader* hdr =
1747         reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
1748     if (!hdr)
1749     {
1750         phosphor::logging::log<phosphor::logging::level::ERR>(
1751             "ipmiStorageGetSDR: record header is null");
1752         return ipmi::responseSuccess(nextRecordId, record);
1753     }
1754 
1755     size_t sdrLength =
1756         sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
1757     if (sdrLength < (offset + bytesToRead))
1758     {
1759         bytesToRead = sdrLength - offset;
1760     }
1761 
1762     uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
1763     if (!respStart)
1764     {
1765         phosphor::logging::log<phosphor::logging::level::ERR>(
1766             "ipmiStorageGetSDR: record is null");
1767         return ipmi::responseSuccess(nextRecordId, record);
1768     }
1769     std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
1770 
1771     return ipmi::responseSuccess(nextRecordId, recordData);
1772 }
1773 /* end storage commands */
1774 
1775 void registerSensorFunctions()
1776 {
1777     // <Platform Event>
1778     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1779                           ipmi::sensor_event::cmdPlatformEvent,
1780                           ipmi::Privilege::Operator, ipmiSenPlatformEvent);
1781 
1782     // <Get Sensor Reading>
1783     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1784                           ipmi::sensor_event::cmdGetSensorReading,
1785                           ipmi::Privilege::User, ipmiSenGetSensorReading);
1786 
1787     // <Get Sensor Threshold>
1788     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1789                           ipmi::sensor_event::cmdGetSensorThreshold,
1790                           ipmi::Privilege::User, ipmiSenGetSensorThresholds);
1791 
1792     // <Set Sensor Threshold>
1793     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1794                           ipmi::sensor_event::cmdSetSensorThreshold,
1795                           ipmi::Privilege::Operator,
1796                           ipmiSenSetSensorThresholds);
1797 
1798     // <Get Sensor Event Enable>
1799     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1800                           ipmi::sensor_event::cmdGetSensorEventEnable,
1801                           ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
1802 
1803     // <Get Sensor Event Status>
1804     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1805                           ipmi::sensor_event::cmdGetSensorEventStatus,
1806                           ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
1807 
1808     // register all storage commands for both Sensor and Storage command
1809     // versions
1810 
1811     // <Get SDR Repository Info>
1812     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1813                           ipmi::storage::cmdGetSdrRepositoryInfo,
1814                           ipmi::Privilege::User,
1815                           ipmiStorageGetSDRRepositoryInfo);
1816 
1817     // <Get Device SDR Info>
1818     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1819                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
1820                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1821 
1822     // <Get SDR Allocation Info>
1823     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1824                           ipmi::storage::cmdGetSdrRepositoryAllocInfo,
1825                           ipmi::Privilege::User,
1826                           ipmiStorageGetSDRAllocationInfo);
1827 
1828     // <Reserve SDR Repo>
1829     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1830                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1831                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1832 
1833     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1834                           ipmi::storage::cmdReserveSdrRepository,
1835                           ipmi::Privilege::User, ipmiStorageReserveSDR);
1836 
1837     // <Get Sdr>
1838     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnSensor,
1839                           ipmi::sensor_event::cmdGetDeviceSdr,
1840                           ipmi::Privilege::User, ipmiStorageGetSDR);
1841 
1842     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1843                           ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
1844                           ipmiStorageGetSDR);
1845 }
1846 } // namespace ipmi
1847