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