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