blob: a2a5b6547d21e0bb26a62baff682a0c3e6325788 [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
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 "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
23#include <algorithm>
24#include <array>
25#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
27#include <chrono>
28#include <cmath>
29#include <cstring>
30#include <iostream>
31#include <ipmid/api.hpp>
32#include <ipmid/types.hpp>
33#include <ipmid/utils.hpp>
34#include <map>
35#include <memory>
36#include <optional>
37#include <phosphor-logging/log.hpp>
38#include <sdbusplus/bus.hpp>
39#include <stdexcept>
40#include <string>
41#include <utility>
42#include <variant>
43
Scron Chang2703b022021-07-06 15:47:45 +080044#ifdef FEATURE_HYBRID_SENSORS
45
46#include "sensordatahandler.hpp"
47namespace ipmi
48{
49namespace sensor
50{
51extern const IdInfoMap sensors;
52} // namespace sensor
53} // namespace ipmi
54#endif
55
Willy Tude54f482021-01-26 15:59:09 -080056namespace ipmi
57{
Hao Jiangd48c9212021-02-03 15:45:06 -080058
59using phosphor::logging::entry;
60using phosphor::logging::level;
61using phosphor::logging::log;
62
Willy Tude54f482021-01-26 15:59:09 -080063static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070064static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080065
Willy Tu38e7a2b2021-03-29 15:09:56 -070066// BMC I2C address is generally at 0x20
67static constexpr uint8_t bmcI2CAddr = 0x20;
68
Willy Tude54f482021-01-26 15:59:09 -080069constexpr size_t maxSDRTotalSize =
70 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
71constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
72
73static uint16_t sdrReservationID;
74static uint32_t sdrLastAdd = noTimestamp;
75static uint32_t sdrLastRemove = noTimestamp;
76static constexpr size_t lastRecordIndex = 0xFFFF;
77static constexpr int GENERAL_ERROR = -1;
78
Willy Tude54f482021-01-26 15:59:09 -080079static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
80
81// Specify the comparison required to sort and find char* map objects
82struct CmpStr
83{
84 bool operator()(const char* a, const char* b) const
85 {
86 return std::strcmp(a, b) < 0;
87 }
88};
89const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
90 sensorUnits{{{"temperature", SensorUnits::degreesC},
91 {"voltage", SensorUnits::volts},
92 {"current", SensorUnits::amps},
93 {"fan_tach", SensorUnits::rpm},
94 {"power", SensorUnits::watts}}};
95
96void registerSensorFunctions() __attribute__((constructor));
97
98static sdbusplus::bus::match::match sensorAdded(
99 *getSdBus(),
100 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
101 "sensors/'",
102 [](sdbusplus::message::message& m) {
103 getSensorTree().clear();
104 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
105 std::chrono::system_clock::now().time_since_epoch())
106 .count();
107 });
108
109static sdbusplus::bus::match::match sensorRemoved(
110 *getSdBus(),
111 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
112 "sensors/'",
113 [](sdbusplus::message::message& m) {
114 getSensorTree().clear();
115 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
116 std::chrono::system_clock::now().time_since_epoch())
117 .count();
118 });
119
120// this keeps track of deassertions for sensor event status command. A
121// deasertion can only happen if an assertion was seen first.
122static boost::container::flat_map<
123 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
124 thresholdDeassertMap;
125
126static sdbusplus::bus::match::match thresholdChanged(
127 *getSdBus(),
128 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
129 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
130 [](sdbusplus::message::message& m) {
131 boost::container::flat_map<std::string, std::variant<bool, double>>
132 values;
133 m.read(std::string(), values);
134
135 auto findAssert =
136 std::find_if(values.begin(), values.end(), [](const auto& pair) {
137 return pair.first.find("Alarm") != std::string::npos;
138 });
139 if (findAssert != values.end())
140 {
141 auto ptr = std::get_if<bool>(&(findAssert->second));
142 if (ptr == nullptr)
143 {
144 phosphor::logging::log<phosphor::logging::level::ERR>(
145 "thresholdChanged: Assert non bool");
146 return;
147 }
148 if (*ptr)
149 {
150 phosphor::logging::log<phosphor::logging::level::INFO>(
151 "thresholdChanged: Assert",
152 phosphor::logging::entry("SENSOR=%s", m.get_path()));
153 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
154 }
155 else
156 {
157 auto& value =
158 thresholdDeassertMap[m.get_path()][findAssert->first];
159 if (value)
160 {
161 phosphor::logging::log<phosphor::logging::level::INFO>(
162 "thresholdChanged: deassert",
163 phosphor::logging::entry("SENSOR=%s", m.get_path()));
164 value = *ptr;
165 }
166 }
167 }
168 });
169
Hao Jiangd2afd052020-12-10 15:09:32 -0800170namespace sensor
171{
172static constexpr const char* vrInterface =
173 "xyz.openbmc_project.Control.VoltageRegulatorMode";
174static constexpr const char* sensorInterface =
175 "xyz.openbmc_project.Sensor.Value";
176} // namespace sensor
177
Willy Tude54f482021-01-26 15:59:09 -0800178static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
179 double& min)
180{
181 max = 127;
182 min = -128;
183
Hao Jiangd2afd052020-12-10 15:09:32 -0800184 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800185 auto critical =
186 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
187 auto warning =
188 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
189
190 if (sensorObject != sensorMap.end())
191 {
192 auto maxMap = sensorObject->second.find("MaxValue");
193 auto minMap = sensorObject->second.find("MinValue");
194
195 if (maxMap != sensorObject->second.end())
196 {
197 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
198 }
199 if (minMap != sensorObject->second.end())
200 {
201 min = std::visit(VariantToDoubleVisitor(), minMap->second);
202 }
203 }
204 if (critical != sensorMap.end())
205 {
206 auto lower = critical->second.find("CriticalLow");
207 auto upper = critical->second.find("CriticalHigh");
208 if (lower != critical->second.end())
209 {
210 double value = std::visit(VariantToDoubleVisitor(), lower->second);
211 min = std::min(value, min);
212 }
213 if (upper != critical->second.end())
214 {
215 double value = std::visit(VariantToDoubleVisitor(), upper->second);
216 max = std::max(value, max);
217 }
218 }
219 if (warning != sensorMap.end())
220 {
221
222 auto lower = warning->second.find("WarningLow");
223 auto upper = warning->second.find("WarningHigh");
224 if (lower != warning->second.end())
225 {
226 double value = std::visit(VariantToDoubleVisitor(), lower->second);
227 min = std::min(value, min);
228 }
229 if (upper != warning->second.end())
230 {
231 double value = std::visit(VariantToDoubleVisitor(), upper->second);
232 max = std::max(value, max);
233 }
234 }
235}
236
237static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700238 std::string sensorPath, DbusInterfaceMap& sensorMap,
239 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800240{
Scron Chang2703b022021-07-06 15:47:45 +0800241#ifdef FEATURE_HYBRID_SENSORS
242 if (auto sensor = findStaticSensor(sensorPath);
243 sensor != ipmi::sensor::sensors.end() &&
244 getSensorEventTypeFromPath(sensorPath) !=
245 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
246 {
247 // If the incoming sensor is a discrete sensor, it might fail in
248 // getManagedObjects(), return true, and use its own getFunc to get
249 // value.
250 return true;
251 }
252#endif
253
Willy Tude54f482021-01-26 15:59:09 -0800254 static boost::container::flat_map<
255 std::string, std::chrono::time_point<std::chrono::steady_clock>>
256 updateTimeMap;
257
258 auto updateFind = updateTimeMap.find(sensorConnection);
259 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
260 if (updateFind != updateTimeMap.end())
261 {
262 lastUpdate = updateFind->second;
263 }
264
265 auto now = std::chrono::steady_clock::now();
266
267 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700268 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800269 {
Willy Tude54f482021-01-26 15:59:09 -0800270 ObjectValueTree managedObjects;
271 boost::system::error_code ec = getManagedObjects(
272 ctx, sensorConnection.c_str(), "/", managedObjects);
273 if (ec)
274 {
275 phosphor::logging::log<phosphor::logging::level::ERR>(
276 "GetMangagedObjects for getSensorMap failed",
277 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
278
279 return false;
280 }
281
282 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700283 // Update time after finish building the map which allow the
284 // data to be cached for updatePeriod plus the build time.
285 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800286 }
287 auto connection = SensorCache.find(sensorConnection);
288 if (connection == SensorCache.end())
289 {
290 return false;
291 }
292 auto path = connection->second.find(sensorPath);
293 if (path == connection->second.end())
294 {
295 return false;
296 }
297 sensorMap = path->second;
298
299 return true;
300}
301
Hao Jiangd2afd052020-12-10 15:09:32 -0800302namespace sensor
303{
Hao Jiangd48c9212021-02-03 15:45:06 -0800304// Read VR profiles from sensor(daemon) interface
305static std::optional<std::vector<std::string>>
306 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800307{
308 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800309 auto supportedProperty = object.find("Supported");
310 if (supportedProperty == object.end() ||
311 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800312 {
313 phosphor::logging::log<phosphor::logging::level::ERR>(
314 "Missing the required Supported and Selected properties");
315 return std::nullopt;
316 }
317
318 const auto profilesPtr =
319 std::get_if<std::vector<std::string>>(&supportedProperty->second);
320
321 if (profilesPtr == nullptr)
322 {
323 phosphor::logging::log<phosphor::logging::level::ERR>(
324 "property is not array of string");
325 return std::nullopt;
326 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800327 return *profilesPtr;
328}
329
330// Calculate VR Mode from input IPMI discrete event bytes
331static std::optional<std::string>
332 calculateVRMode(uint15_t assertOffset,
333 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
334{
335 // get VR mode profiles from Supported Interface
336 auto profiles = getSupportedVrProfiles(VRObject);
337 if (!profiles)
338 {
339 return std::nullopt;
340 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800341
342 // interpret IPMI cmd bits into profiles' index
343 long unsigned int index = 0;
344 // only one bit should be set and the highest bit should not be used.
345 if (assertOffset == 0 || assertOffset == (1u << 15) ||
346 (assertOffset & (assertOffset - 1)))
347 {
348 phosphor::logging::log<phosphor::logging::level::ERR>(
349 "IPMI cmd format incorrect",
350
351 phosphor::logging::entry("BYTES=%#02x",
352 static_cast<uint16_t>(assertOffset)));
353 return std::nullopt;
354 }
355
356 while (assertOffset != 1)
357 {
358 assertOffset >>= 1;
359 index++;
360 }
361
Hao Jiangd48c9212021-02-03 15:45:06 -0800362 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800363 {
364 phosphor::logging::log<phosphor::logging::level::ERR>(
365 "profile index out of boundary");
366 return std::nullopt;
367 }
368
Hao Jiangd48c9212021-02-03 15:45:06 -0800369 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800370}
371
372// Calculate sensor value from IPMI reading byte
373static std::optional<double>
374 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
375 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
376{
377 if (valueObject.find("Value") == valueObject.end())
378 {
379 phosphor::logging::log<phosphor::logging::level::ERR>(
380 "Missing the required Value property");
381 return std::nullopt;
382 }
383
384 double max = 0;
385 double min = 0;
386 getSensorMaxMin(sensorMap, max, min);
387
388 int16_t mValue = 0;
389 int16_t bValue = 0;
390 int8_t rExp = 0;
391 int8_t bExp = 0;
392 bool bSigned = false;
393
394 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
395 {
396 return std::nullopt;
397 }
398
399 double value = bSigned ? ((int8_t)reading) : reading;
400
401 value *= ((double)mValue);
402 value += ((double)bValue) * std::pow(10.0, bExp);
403 value *= std::pow(10.0, rExp);
404
405 return value;
406}
407
Willy Tu38e7a2b2021-03-29 15:09:56 -0700408// Extract file name from sensor path as the sensors SDR ID. Simplify the name
409// if it is too long.
410std::string parseSdrIdFromPath(const std::string& path)
411{
412 std::string name;
413 size_t nameStart = path.rfind("/");
414 if (nameStart != std::string::npos)
415 {
416 name = path.substr(nameStart + 1, std::string::npos - nameStart);
417 }
418
419 std::replace(name.begin(), name.end(), '_', ' ');
420 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
421 {
422 // try to not truncate by replacing common words
423 constexpr std::array<std::pair<const char*, const char*>, 2>
424 replaceWords = {std::make_pair("Output", "Out"),
425 std::make_pair("Input", "In")};
426 for (const auto& [find, replace] : replaceWords)
427 {
428 boost::replace_all(name, find, replace);
429 }
430
431 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
432 }
433 return name;
434}
435
Hao Jiangd48c9212021-02-03 15:45:06 -0800436bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
437 const std::string& path,
438 const ipmi::DbusInterfaceMap::mapped_type& object,
439 std::bitset<16>& assertions)
440{
441 auto profiles = sensor::getSupportedVrProfiles(object);
442 if (!profiles)
443 {
444 return false;
445 }
446 ipmi::Value modeVariant;
447
448 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
449 "Selected", modeVariant);
450 if (ec)
451 {
452 log<level::ERR>("Failed to get property",
453 entry("PROPERTY=%s", "Selected"),
454 entry("PATH=%s", path.c_str()),
455 entry("INTERFACE=%s", sensor::sensorInterface),
456 entry("WHAT=%s", ec.message().c_str()));
457 return false;
458 }
459
460 auto mode = std::get_if<std::string>(&modeVariant);
461 if (mode == nullptr)
462 {
463 log<level::ERR>("property is not a string",
464 entry("PROPERTY=%s", "Selected"),
465 entry("PATH=%s", path.c_str()),
466 entry("INTERFACE=%s", sensor::sensorInterface));
467 return false;
468 }
469
470 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
471 if (itr == profiles->end())
472 {
473 using namespace phosphor::logging;
474 log<level::ERR>("VR mode doesn't match any of its profiles",
475 entry("PATH=%s", path.c_str()));
476 return false;
477 }
478 std::size_t index =
479 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
480
481 // map index to reponse event assertion bit.
482 if (index < 8)
483 {
484 assertions.set(1u << index);
485 }
486 else if (index < 15)
487 {
488 assertions.set(1u << (index - 8));
489 }
490 else
491 {
492 log<level::ERR>("VR profile index reaches max assertion bit",
493 entry("PATH=%s", path.c_str()),
494 entry("INDEX=%uz", index));
495 return false;
496 }
497 if constexpr (debug)
498 {
499 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
500 << " mode is: [" << index << "] " << *mode << std::endl;
501 }
502 return true;
503}
Hao Jiangd2afd052020-12-10 15:09:32 -0800504} // namespace sensor
505
Willy Tude54f482021-01-26 15:59:09 -0800506ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
507 uint8_t sensorType, uint8_t sensorNum,
508 uint8_t eventType, uint8_t eventData1,
509 std::optional<uint8_t> eventData2,
510 std::optional<uint8_t> eventData3)
511{
512 return ipmi::responseSuccess();
513}
514
Willy Tudbafbce2021-03-29 00:37:05 -0700515ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
516 uint8_t sensorNumber, uint8_t operation,
517 uint8_t reading, uint15_t assertOffset,
518 bool resvd1, uint15_t deassertOffset,
519 bool resvd2, uint8_t eventData1,
520 uint8_t eventData2, uint8_t eventData3)
521{
522 std::string connection;
523 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700524 std::vector<std::string> interfaces;
525
526 ipmi::Cc status =
527 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700528 if (status)
529 {
530 return ipmi::response(status);
531 }
532
Hao Jiangd2afd052020-12-10 15:09:32 -0800533 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700534 if (std::find(interfaces.begin(), interfaces.end(),
535 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700536 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700537 DbusInterfaceMap sensorMap;
538 if (!getSensorMap(ctx, connection, path, sensorMap))
539 {
540 return ipmi::responseResponseError();
541 }
542 auto sensorObject = sensorMap.find(sensor::sensorInterface);
543 if (sensorObject != sensorMap.end())
544 {
545 return ipmi::responseResponseError();
546 }
547
Hao Jiangd2afd052020-12-10 15:09:32 -0800548 auto value =
549 sensor::calculateValue(reading, sensorMap, sensorObject->second);
550 if (!value)
551 {
552 return ipmi::responseResponseError();
553 }
554
555 if constexpr (debug)
556 {
557 phosphor::logging::log<phosphor::logging::level::INFO>(
558 "IPMI SET_SENSOR",
559 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
560 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
561 phosphor::logging::entry("VALUE=%f", *value));
562 }
563
564 boost::system::error_code ec =
565 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
566 "Value", ipmi::Value(*value));
567
568 // setDbusProperty intended to resolve dbus exception/rc within the
569 // function but failed to achieve that. Catch SdBusError in the ipmi
570 // callback functions for now (e.g. ipmiSetSensorReading).
571 if (ec)
572 {
573 using namespace phosphor::logging;
574 log<level::ERR>("Failed to set property",
575 entry("PROPERTY=%s", "Value"),
576 entry("PATH=%s", path.c_str()),
577 entry("INTERFACE=%s", sensor::sensorInterface),
578 entry("WHAT=%s", ec.message().c_str()));
579 return ipmi::responseResponseError();
580 }
581 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700582 }
583
Hao Jiange39d4d82021-04-16 17:02:40 -0700584 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
585 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700586 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700587 DbusInterfaceMap sensorMap;
588 if (!getSensorMap(ctx, connection, path, sensorMap))
589 {
590 return ipmi::responseResponseError();
591 }
592 auto sensorObject = sensorMap.find(sensor::vrInterface);
593 if (sensorObject != sensorMap.end())
594 {
595 return ipmi::responseResponseError();
596 }
597
Hao Jiangd2afd052020-12-10 15:09:32 -0800598 // VR sensors are treated as a special case and we will not check the
599 // write permission for VR sensors, since they always deemed writable
600 // and permission table are not applied to VR sensors.
601 auto vrMode =
602 sensor::calculateVRMode(assertOffset, sensorObject->second);
603 if (!vrMode)
604 {
605 return ipmi::responseResponseError();
606 }
607 boost::system::error_code ec = setDbusProperty(
608 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
609 // setDbusProperty intended to resolve dbus exception/rc within the
610 // function but failed to achieve that. Catch SdBusError in the ipmi
611 // callback functions for now (e.g. ipmiSetSensorReading).
612 if (ec)
613 {
614 using namespace phosphor::logging;
615 log<level::ERR>("Failed to set property",
616 entry("PROPERTY=%s", "Selected"),
617 entry("PATH=%s", path.c_str()),
618 entry("INTERFACE=%s", sensor::sensorInterface),
619 entry("WHAT=%s", ec.message().c_str()));
620 return ipmi::responseResponseError();
621 }
622 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700623 }
624
Hao Jiangd2afd052020-12-10 15:09:32 -0800625 phosphor::logging::log<phosphor::logging::level::ERR>(
626 "unknown sensor type",
627 phosphor::logging::entry("PATH=%s", path.c_str()));
628 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700629}
630
Willy Tude54f482021-01-26 15:59:09 -0800631ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
632 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
633{
634 std::string connection;
635 std::string path;
636
637 auto status = getSensorConnection(ctx, sensnum, connection, path);
638 if (status)
639 {
640 return ipmi::response(status);
641 }
642
Scron Chang2703b022021-07-06 15:47:45 +0800643#ifdef FEATURE_HYBRID_SENSORS
644 if (auto sensor = findStaticSensor(path);
645 sensor != ipmi::sensor::sensors.end() &&
646 getSensorEventTypeFromPath(path) !=
647 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
648 {
649 if (ipmi::sensor::Mutability::Read !=
650 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
651 {
652 return ipmi::responseIllegalCommand();
653 }
654
655 uint8_t operation;
656 try
657 {
658 ipmi::sensor::GetSensorResponse getResponse =
659 sensor->second.getFunc(sensor->second);
660
661 if (getResponse.readingOrStateUnavailable)
662 {
663 operation |= static_cast<uint8_t>(
664 IPMISensorReadingByte2::readingStateUnavailable);
665 }
666 if (getResponse.scanningEnabled)
667 {
668 operation |= static_cast<uint8_t>(
669 IPMISensorReadingByte2::sensorScanningEnable);
670 }
671 if (getResponse.allEventMessagesEnabled)
672 {
673 operation |= static_cast<uint8_t>(
674 IPMISensorReadingByte2::eventMessagesEnable);
675 }
676 return ipmi::responseSuccess(
677 getResponse.reading, operation,
678 getResponse.thresholdLevelsStates,
679 getResponse.discreteReadingSensorStates);
680 }
681 catch (const std::exception& e)
682 {
683 operation |= static_cast<uint8_t>(
684 IPMISensorReadingByte2::readingStateUnavailable);
685 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
686 }
687 }
688#endif
689
Willy Tude54f482021-01-26 15:59:09 -0800690 DbusInterfaceMap sensorMap;
691 if (!getSensorMap(ctx, connection, path, sensorMap))
692 {
693 return ipmi::responseResponseError();
694 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800695 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800696
697 if (sensorObject == sensorMap.end() ||
698 sensorObject->second.find("Value") == sensorObject->second.end())
699 {
700 return ipmi::responseResponseError();
701 }
702 auto& valueVariant = sensorObject->second["Value"];
703 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
704
705 double max = 0;
706 double min = 0;
707 getSensorMaxMin(sensorMap, max, min);
708
709 int16_t mValue = 0;
710 int16_t bValue = 0;
711 int8_t rExp = 0;
712 int8_t bExp = 0;
713 bool bSigned = false;
714
715 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
716 {
717 return ipmi::responseResponseError();
718 }
719
720 uint8_t value =
721 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
722 uint8_t operation =
723 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
724 operation |=
725 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
726 bool notReading = std::isnan(reading);
727
728 if (!notReading)
729 {
730 auto availableObject =
731 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
732 if (availableObject != sensorMap.end())
733 {
734 auto findAvailable = availableObject->second.find("Available");
735 if (findAvailable != availableObject->second.end())
736 {
737 bool* available = std::get_if<bool>(&(findAvailable->second));
738 if (available && !(*available))
739 {
740 notReading = true;
741 }
742 }
743 }
744 }
745
746 if (notReading)
747 {
748 operation |= static_cast<uint8_t>(
749 IPMISensorReadingByte2::readingStateUnavailable);
750 }
751
Josh Lehana55c9532020-10-28 21:59:06 -0700752 if constexpr (details::enableInstrumentation)
753 {
754 int byteValue;
755 if (bSigned)
756 {
757 byteValue = static_cast<int>(static_cast<int8_t>(value));
758 }
759 else
760 {
761 byteValue = static_cast<int>(static_cast<uint8_t>(value));
762 }
763
764 // Keep stats on the reading just obtained, even if it is "NaN"
765 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
766 {
767 // This is the first reading, show the coefficients
768 double step = (max - min) / 255.0;
769 std::cerr << "IPMI sensor "
770 << details::sdrStatsTable.getName(sensnum)
771 << ": Range min=" << min << " max=" << max
772 << ", step=" << step
773 << ", Coefficients mValue=" << static_cast<int>(mValue)
774 << " rExp=" << static_cast<int>(rExp)
775 << " bValue=" << static_cast<int>(bValue)
776 << " bExp=" << static_cast<int>(bExp)
777 << " bSigned=" << static_cast<int>(bSigned) << "\n";
778 }
779 }
780
Willy Tude54f482021-01-26 15:59:09 -0800781 uint8_t thresholds = 0;
782
783 auto warningObject =
784 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
785 if (warningObject != sensorMap.end())
786 {
787 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
788 auto alarmLow = warningObject->second.find("WarningAlarmLow");
789 if (alarmHigh != warningObject->second.end())
790 {
791 if (std::get<bool>(alarmHigh->second))
792 {
793 thresholds |= static_cast<uint8_t>(
794 IPMISensorReadingByte3::upperNonCritical);
795 }
796 }
797 if (alarmLow != warningObject->second.end())
798 {
799 if (std::get<bool>(alarmLow->second))
800 {
801 thresholds |= static_cast<uint8_t>(
802 IPMISensorReadingByte3::lowerNonCritical);
803 }
804 }
805 }
806
807 auto criticalObject =
808 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
809 if (criticalObject != sensorMap.end())
810 {
811 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
812 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
813 if (alarmHigh != criticalObject->second.end())
814 {
815 if (std::get<bool>(alarmHigh->second))
816 {
817 thresholds |=
818 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
819 }
820 }
821 if (alarmLow != criticalObject->second.end())
822 {
823 if (std::get<bool>(alarmLow->second))
824 {
825 thresholds |=
826 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
827 }
828 }
829 }
830
831 // no discrete as of today so optional byte is never returned
832 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
833}
834
835/** @brief implements the Set Sensor threshold command
836 * @param sensorNumber - sensor number
837 * @param lowerNonCriticalThreshMask
838 * @param lowerCriticalThreshMask
839 * @param lowerNonRecovThreshMask
840 * @param upperNonCriticalThreshMask
841 * @param upperCriticalThreshMask
842 * @param upperNonRecovThreshMask
843 * @param reserved
844 * @param lowerNonCritical - lower non-critical threshold
845 * @param lowerCritical - Lower critical threshold
846 * @param lowerNonRecoverable - Lower non recovarable threshold
847 * @param upperNonCritical - Upper non-critical threshold
848 * @param upperCritical - Upper critical
849 * @param upperNonRecoverable - Upper Non-recoverable
850 *
851 * @returns IPMI completion code
852 */
853ipmi::RspType<> ipmiSenSetSensorThresholds(
854 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
855 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
856 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
857 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
858 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
859 uint8_t upperNonCritical, uint8_t upperCritical,
860 uint8_t upperNonRecoverable)
861{
862 if (reserved)
863 {
864 return ipmi::responseInvalidFieldRequest();
865 }
866
867 // lower nc and upper nc not suppported on any sensor
868 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
869 {
870 return ipmi::responseInvalidFieldRequest();
871 }
872
873 // if none of the threshold mask are set, nothing to do
874 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
875 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
876 upperCriticalThreshMask | upperNonRecovThreshMask))
877 {
878 return ipmi::responseSuccess();
879 }
880
881 std::string connection;
882 std::string path;
883
884 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
885 if (status)
886 {
887 return ipmi::response(status);
888 }
889 DbusInterfaceMap sensorMap;
890 if (!getSensorMap(ctx, connection, path, sensorMap))
891 {
892 return ipmi::responseResponseError();
893 }
894
895 double max = 0;
896 double min = 0;
897 getSensorMaxMin(sensorMap, max, min);
898
899 int16_t mValue = 0;
900 int16_t bValue = 0;
901 int8_t rExp = 0;
902 int8_t bExp = 0;
903 bool bSigned = false;
904
905 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
906 {
907 return ipmi::responseResponseError();
908 }
909
910 // store a vector of property name, value to set, and interface
911 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
912
913 // define the indexes of the tuple
914 constexpr uint8_t propertyName = 0;
915 constexpr uint8_t thresholdValue = 1;
916 constexpr uint8_t interface = 2;
917 // verifiy all needed fields are present
918 if (lowerCriticalThreshMask || upperCriticalThreshMask)
919 {
920 auto findThreshold =
921 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
922 if (findThreshold == sensorMap.end())
923 {
924 return ipmi::responseInvalidFieldRequest();
925 }
926 if (lowerCriticalThreshMask)
927 {
928 auto findLower = findThreshold->second.find("CriticalLow");
929 if (findLower == findThreshold->second.end())
930 {
931 return ipmi::responseInvalidFieldRequest();
932 }
933 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
934 findThreshold->first);
935 }
936 if (upperCriticalThreshMask)
937 {
938 auto findUpper = findThreshold->second.find("CriticalHigh");
939 if (findUpper == findThreshold->second.end())
940 {
941 return ipmi::responseInvalidFieldRequest();
942 }
943 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
944 findThreshold->first);
945 }
946 }
947 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
948 {
949 auto findThreshold =
950 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
951 if (findThreshold == sensorMap.end())
952 {
953 return ipmi::responseInvalidFieldRequest();
954 }
955 if (lowerNonCriticalThreshMask)
956 {
957 auto findLower = findThreshold->second.find("WarningLow");
958 if (findLower == findThreshold->second.end())
959 {
960 return ipmi::responseInvalidFieldRequest();
961 }
962 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
963 findThreshold->first);
964 }
965 if (upperNonCriticalThreshMask)
966 {
967 auto findUpper = findThreshold->second.find("WarningHigh");
968 if (findUpper == findThreshold->second.end())
969 {
970 return ipmi::responseInvalidFieldRequest();
971 }
972 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
973 findThreshold->first);
974 }
975 }
976 for (const auto& property : thresholdsToSet)
977 {
978 // from section 36.3 in the IPMI Spec, assume all linear
979 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
980 (bValue * std::pow(10.0, bExp))) *
981 std::pow(10.0, rExp);
982 setDbusProperty(
983 *getSdBus(), connection, path, std::get<interface>(property),
984 std::get<propertyName>(property), ipmi::Value(valueToSet));
985 }
986 return ipmi::responseSuccess();
987}
988
989IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
990{
991 IPMIThresholds resp;
992 auto warningInterface =
993 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
994 auto criticalInterface =
995 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
996
997 if ((warningInterface != sensorMap.end()) ||
998 (criticalInterface != sensorMap.end()))
999 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001000 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001001
1002 if (sensorPair == sensorMap.end())
1003 {
1004 // should not have been able to find a sensor not implementing
1005 // the sensor object
1006 throw std::runtime_error("Invalid sensor map");
1007 }
1008
1009 double max = 0;
1010 double min = 0;
1011 getSensorMaxMin(sensorMap, max, min);
1012
1013 int16_t mValue = 0;
1014 int16_t bValue = 0;
1015 int8_t rExp = 0;
1016 int8_t bExp = 0;
1017 bool bSigned = false;
1018
1019 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1020 {
1021 throw std::runtime_error("Invalid sensor atrributes");
1022 }
1023 if (warningInterface != sensorMap.end())
1024 {
1025 auto& warningMap = warningInterface->second;
1026
1027 auto warningHigh = warningMap.find("WarningHigh");
1028 auto warningLow = warningMap.find("WarningLow");
1029
1030 if (warningHigh != warningMap.end())
1031 {
1032
1033 double value =
1034 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1035 resp.warningHigh = scaleIPMIValueFromDouble(
1036 value, mValue, rExp, bValue, bExp, bSigned);
1037 }
1038 if (warningLow != warningMap.end())
1039 {
1040 double value =
1041 std::visit(VariantToDoubleVisitor(), warningLow->second);
1042 resp.warningLow = scaleIPMIValueFromDouble(
1043 value, mValue, rExp, bValue, bExp, bSigned);
1044 }
1045 }
1046 if (criticalInterface != sensorMap.end())
1047 {
1048 auto& criticalMap = criticalInterface->second;
1049
1050 auto criticalHigh = criticalMap.find("CriticalHigh");
1051 auto criticalLow = criticalMap.find("CriticalLow");
1052
1053 if (criticalHigh != criticalMap.end())
1054 {
1055 double value =
1056 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1057 resp.criticalHigh = scaleIPMIValueFromDouble(
1058 value, mValue, rExp, bValue, bExp, bSigned);
1059 }
1060 if (criticalLow != criticalMap.end())
1061 {
1062 double value =
1063 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1064 resp.criticalLow = scaleIPMIValueFromDouble(
1065 value, mValue, rExp, bValue, bExp, bSigned);
1066 }
1067 }
1068 }
1069 return resp;
1070}
1071
1072ipmi::RspType<uint8_t, // readable
1073 uint8_t, // lowerNCrit
1074 uint8_t, // lowerCrit
1075 uint8_t, // lowerNrecoverable
1076 uint8_t, // upperNC
1077 uint8_t, // upperCrit
1078 uint8_t> // upperNRecoverable
1079 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1080{
1081 std::string connection;
1082 std::string path;
1083
1084 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1085 if (status)
1086 {
1087 return ipmi::response(status);
1088 }
1089
1090 DbusInterfaceMap sensorMap;
1091 if (!getSensorMap(ctx, connection, path, sensorMap))
1092 {
1093 return ipmi::responseResponseError();
1094 }
1095
1096 IPMIThresholds thresholdData;
1097 try
1098 {
1099 thresholdData = getIPMIThresholds(sensorMap);
1100 }
1101 catch (std::exception&)
1102 {
1103 return ipmi::responseResponseError();
1104 }
1105
1106 uint8_t readable = 0;
1107 uint8_t lowerNC = 0;
1108 uint8_t lowerCritical = 0;
1109 uint8_t lowerNonRecoverable = 0;
1110 uint8_t upperNC = 0;
1111 uint8_t upperCritical = 0;
1112 uint8_t upperNonRecoverable = 0;
1113
1114 if (thresholdData.warningHigh)
1115 {
1116 readable |=
1117 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1118 upperNC = *thresholdData.warningHigh;
1119 }
1120 if (thresholdData.warningLow)
1121 {
1122 readable |=
1123 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1124 lowerNC = *thresholdData.warningLow;
1125 }
1126
1127 if (thresholdData.criticalHigh)
1128 {
1129 readable |=
1130 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1131 upperCritical = *thresholdData.criticalHigh;
1132 }
1133 if (thresholdData.criticalLow)
1134 {
1135 readable |=
1136 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1137 lowerCritical = *thresholdData.criticalLow;
1138 }
1139
1140 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1141 lowerNonRecoverable, upperNC, upperCritical,
1142 upperNonRecoverable);
1143}
1144
1145/** @brief implements the get Sensor event enable command
1146 * @param sensorNumber - sensor number
1147 *
1148 * @returns IPMI completion code plus response data
1149 * - enabled - Sensor Event messages
1150 * - assertionEnabledLsb - Assertion event messages
1151 * - assertionEnabledMsb - Assertion event messages
1152 * - deassertionEnabledLsb - Deassertion event messages
1153 * - deassertionEnabledMsb - Deassertion event messages
1154 */
1155
1156ipmi::RspType<uint8_t, // enabled
1157 uint8_t, // assertionEnabledLsb
1158 uint8_t, // assertionEnabledMsb
1159 uint8_t, // deassertionEnabledLsb
1160 uint8_t> // deassertionEnabledMsb
1161 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1162{
1163 std::string connection;
1164 std::string path;
1165
1166 uint8_t enabled = 0;
1167 uint8_t assertionEnabledLsb = 0;
1168 uint8_t assertionEnabledMsb = 0;
1169 uint8_t deassertionEnabledLsb = 0;
1170 uint8_t deassertionEnabledMsb = 0;
1171
1172 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1173 if (status)
1174 {
1175 return ipmi::response(status);
1176 }
1177
Scron Chang2703b022021-07-06 15:47:45 +08001178#ifdef FEATURE_HYBRID_SENSORS
1179 if (auto sensor = findStaticSensor(path);
1180 sensor != ipmi::sensor::sensors.end() &&
1181 getSensorEventTypeFromPath(path) !=
1182 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1183 {
1184 enabled = static_cast<uint8_t>(
1185 IPMISensorEventEnableByte2::sensorScanningEnable);
1186 uint16_t assertionEnabled = 0;
1187 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1188 ->second.begin()
1189 ->second.second)
1190 {
1191 assertionEnabled |= (1 << offsetValMap.first);
1192 }
1193 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1194 assertionEnabledMsb =
1195 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1196
1197 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1198 assertionEnabledMsb, deassertionEnabledLsb,
1199 deassertionEnabledMsb);
1200 }
1201#endif
1202
Willy Tude54f482021-01-26 15:59:09 -08001203 DbusInterfaceMap sensorMap;
1204 if (!getSensorMap(ctx, connection, path, sensorMap))
1205 {
1206 return ipmi::responseResponseError();
1207 }
1208
1209 auto warningInterface =
1210 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1211 auto criticalInterface =
1212 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1213 if ((warningInterface != sensorMap.end()) ||
1214 (criticalInterface != sensorMap.end()))
1215 {
1216 enabled = static_cast<uint8_t>(
1217 IPMISensorEventEnableByte2::sensorScanningEnable);
1218 if (warningInterface != sensorMap.end())
1219 {
1220 auto& warningMap = warningInterface->second;
1221
1222 auto warningHigh = warningMap.find("WarningHigh");
1223 auto warningLow = warningMap.find("WarningLow");
1224 if (warningHigh != warningMap.end())
1225 {
1226 assertionEnabledLsb |= static_cast<uint8_t>(
1227 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1228 deassertionEnabledLsb |= static_cast<uint8_t>(
1229 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1230 }
1231 if (warningLow != warningMap.end())
1232 {
1233 assertionEnabledLsb |= static_cast<uint8_t>(
1234 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1235 deassertionEnabledLsb |= static_cast<uint8_t>(
1236 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1237 }
1238 }
1239 if (criticalInterface != sensorMap.end())
1240 {
1241 auto& criticalMap = criticalInterface->second;
1242
1243 auto criticalHigh = criticalMap.find("CriticalHigh");
1244 auto criticalLow = criticalMap.find("CriticalLow");
1245
1246 if (criticalHigh != criticalMap.end())
1247 {
1248 assertionEnabledMsb |= static_cast<uint8_t>(
1249 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1250 deassertionEnabledMsb |= static_cast<uint8_t>(
1251 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1252 }
1253 if (criticalLow != criticalMap.end())
1254 {
1255 assertionEnabledLsb |= static_cast<uint8_t>(
1256 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1257 deassertionEnabledLsb |= static_cast<uint8_t>(
1258 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1259 }
1260 }
1261 }
1262
1263 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1264 assertionEnabledMsb, deassertionEnabledLsb,
1265 deassertionEnabledMsb);
1266}
1267
1268/** @brief implements the get Sensor event status command
1269 * @param sensorNumber - sensor number, FFh = reserved
1270 *
1271 * @returns IPMI completion code plus response data
1272 * - sensorEventStatus - Sensor Event messages state
1273 * - assertions - Assertion event messages
1274 * - deassertions - Deassertion event messages
1275 */
1276ipmi::RspType<uint8_t, // sensorEventStatus
1277 std::bitset<16>, // assertions
1278 std::bitset<16> // deassertion
1279 >
1280 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1281{
1282 if (sensorNum == reservedSensorNumber)
1283 {
1284 return ipmi::responseInvalidFieldRequest();
1285 }
1286
1287 std::string connection;
1288 std::string path;
1289 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1290 if (status)
1291 {
1292 phosphor::logging::log<phosphor::logging::level::ERR>(
1293 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1294 phosphor::logging::entry("SENSOR=%d", sensorNum));
1295 return ipmi::response(status);
1296 }
1297
Scron Chang2703b022021-07-06 15:47:45 +08001298#ifdef FEATURE_HYBRID_SENSORS
1299 if (auto sensor = findStaticSensor(path);
1300 sensor != ipmi::sensor::sensors.end() &&
1301 getSensorEventTypeFromPath(path) !=
1302 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1303 {
1304 auto response = ipmi::sensor::get::mapDbusToAssertion(
1305 sensor->second, path, sensor->second.sensorInterface);
1306 std::bitset<16> assertions;
1307 // deassertions are not used.
1308 std::bitset<16> deassertions = 0;
1309 uint8_t sensorEventStatus;
1310 if (response.readingOrStateUnavailable)
1311 {
1312 sensorEventStatus |= static_cast<uint8_t>(
1313 IPMISensorReadingByte2::readingStateUnavailable);
1314 }
1315 if (response.scanningEnabled)
1316 {
1317 sensorEventStatus |= static_cast<uint8_t>(
1318 IPMISensorReadingByte2::sensorScanningEnable);
1319 }
1320 if (response.allEventMessagesEnabled)
1321 {
1322 sensorEventStatus |= static_cast<uint8_t>(
1323 IPMISensorReadingByte2::eventMessagesEnable);
1324 }
1325 assertions |= response.discreteReadingSensorStates << 8;
1326 assertions |= response.thresholdLevelsStates;
1327 return ipmi::responseSuccess(sensorEventStatus, assertions,
1328 deassertions);
1329 }
1330#endif
1331
Willy Tude54f482021-01-26 15:59:09 -08001332 DbusInterfaceMap sensorMap;
1333 if (!getSensorMap(ctx, connection, path, sensorMap))
1334 {
1335 phosphor::logging::log<phosphor::logging::level::ERR>(
1336 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1337 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1338 return ipmi::responseResponseError();
1339 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001340
1341 uint8_t sensorEventStatus =
1342 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1343 std::bitset<16> assertions = 0;
1344 std::bitset<16> deassertions = 0;
1345
1346 // handle VR typed sensor
1347 auto vrInterface = sensorMap.find(sensor::vrInterface);
1348 if (vrInterface != sensorMap.end())
1349 {
1350 if (!sensor::getVrEventStatus(ctx, connection, path,
1351 vrInterface->second, assertions))
1352 {
1353 return ipmi::responseResponseError();
1354 }
1355
1356 // both Event Message and Sensor Scanning are disable for VR.
1357 sensorEventStatus = 0;
1358 return ipmi::responseSuccess(sensorEventStatus, assertions,
1359 deassertions);
1360 }
1361
Willy Tude54f482021-01-26 15:59:09 -08001362 auto warningInterface =
1363 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1364 auto criticalInterface =
1365 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1366
Willy Tude54f482021-01-26 15:59:09 -08001367 std::optional<bool> criticalDeassertHigh =
1368 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1369 std::optional<bool> criticalDeassertLow =
1370 thresholdDeassertMap[path]["CriticalAlarmLow"];
1371 std::optional<bool> warningDeassertHigh =
1372 thresholdDeassertMap[path]["WarningAlarmHigh"];
1373 std::optional<bool> warningDeassertLow =
1374 thresholdDeassertMap[path]["WarningAlarmLow"];
1375
Willy Tude54f482021-01-26 15:59:09 -08001376 if (criticalDeassertHigh && !*criticalDeassertHigh)
1377 {
1378 deassertions.set(static_cast<size_t>(
1379 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1380 }
1381 if (criticalDeassertLow && !*criticalDeassertLow)
1382 {
1383 deassertions.set(static_cast<size_t>(
1384 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1385 }
1386 if (warningDeassertHigh && !*warningDeassertHigh)
1387 {
1388 deassertions.set(static_cast<size_t>(
1389 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1390 }
1391 if (warningDeassertLow && !*warningDeassertLow)
1392 {
1393 deassertions.set(static_cast<size_t>(
1394 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1395 }
1396 if ((warningInterface != sensorMap.end()) ||
1397 (criticalInterface != sensorMap.end()))
1398 {
1399 sensorEventStatus = static_cast<size_t>(
1400 IPMISensorEventEnableByte2::eventMessagesEnable);
1401 if (warningInterface != sensorMap.end())
1402 {
1403 auto& warningMap = warningInterface->second;
1404
1405 auto warningHigh = warningMap.find("WarningAlarmHigh");
1406 auto warningLow = warningMap.find("WarningAlarmLow");
1407 auto warningHighAlarm = false;
1408 auto warningLowAlarm = false;
1409
1410 if (warningHigh != warningMap.end())
1411 {
1412 warningHighAlarm = std::get<bool>(warningHigh->second);
1413 }
1414 if (warningLow != warningMap.end())
1415 {
1416 warningLowAlarm = std::get<bool>(warningLow->second);
1417 }
1418 if (warningHighAlarm)
1419 {
1420 assertions.set(
1421 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1422 upperNonCriticalGoingHigh));
1423 }
1424 if (warningLowAlarm)
1425 {
1426 assertions.set(
1427 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1428 lowerNonCriticalGoingLow));
1429 }
1430 }
1431 if (criticalInterface != sensorMap.end())
1432 {
1433 auto& criticalMap = criticalInterface->second;
1434
1435 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1436 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1437 auto criticalHighAlarm = false;
1438 auto criticalLowAlarm = false;
1439
1440 if (criticalHigh != criticalMap.end())
1441 {
1442 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1443 }
1444 if (criticalLow != criticalMap.end())
1445 {
1446 criticalLowAlarm = std::get<bool>(criticalLow->second);
1447 }
1448 if (criticalHighAlarm)
1449 {
1450 assertions.set(
1451 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1452 upperCriticalGoingHigh));
1453 }
1454 if (criticalLowAlarm)
1455 {
1456 assertions.set(static_cast<size_t>(
1457 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1458 }
1459 }
1460 }
1461
1462 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1463}
1464
Willy Tu38e7a2b2021-03-29 15:09:56 -07001465// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001466void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1467 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001468{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001469 get_sdr::header::set_record_id(
1470 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1471
Willy Tu38e7a2b2021-03-29 15:09:56 -07001472 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1473 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1474
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001475 record.header.sdr_version = ipmiSdrVersion;
1476 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1477 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1478 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001479 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001480 record.key.owner_lun = lun;
1481 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001482}
1483bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1484 uint16_t recordID, const std::string& service,
1485 const std::string& path,
1486 get_sdr::SensorDataFullRecord& record)
1487{
1488 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1489 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1490
1491 DbusInterfaceMap sensorMap;
1492 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1493 {
1494 phosphor::logging::log<phosphor::logging::level::ERR>(
1495 "Failed to update sensor map for threshold sensor",
1496 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1497 phosphor::logging::entry("PATH=%s", path.c_str()));
1498 return false;
1499 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001500
1501 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1502 record.body.sensor_type = getSensorTypeFromPath(path);
1503 std::string type = getSensorTypeStringFromPath(path);
1504 auto typeCstr = type.c_str();
1505 auto findUnits = sensorUnits.find(typeCstr);
1506 if (findUnits != sensorUnits.end())
1507 {
1508 record.body.sensor_units_2_base =
1509 static_cast<uint8_t>(findUnits->second);
1510 } // else default 0x0 unspecified
1511
1512 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1513
Hao Jiangd2afd052020-12-10 15:09:32 -08001514 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001515 if (sensorObject == sensorMap.end())
1516 {
1517 phosphor::logging::log<phosphor::logging::level::ERR>(
1518 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001519 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001520 }
1521
1522 uint8_t entityId = 0;
1523 uint8_t entityInstance = 0x01;
1524
1525 // follow the association chain to get the parent board's entityid and
1526 // entityInstance
1527 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1528
1529 record.body.entity_id = entityId;
1530 record.body.entity_instance = entityInstance;
1531
1532 auto maxObject = sensorObject->second.find("MaxValue");
1533 auto minObject = sensorObject->second.find("MinValue");
1534
1535 // If min and/or max are left unpopulated,
1536 // then default to what a signed byte would be, namely (-128,127) range.
1537 auto max = static_cast<double>(std::numeric_limits<int8_t>::max());
1538 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest());
1539 if (maxObject != sensorObject->second.end())
1540 {
1541 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
1542 }
1543
1544 if (minObject != sensorObject->second.end())
1545 {
1546 min = std::visit(VariantToDoubleVisitor(), minObject->second);
1547 }
1548
1549 int16_t mValue = 0;
1550 int8_t rExp = 0;
1551 int16_t bValue = 0;
1552 int8_t bExp = 0;
1553 bool bSigned = false;
1554
1555 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1556 {
1557 phosphor::logging::log<phosphor::logging::level::ERR>(
1558 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001559 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001560 }
1561
1562 // The record.body is a struct SensorDataFullRecordBody
1563 // from sensorhandler.hpp in phosphor-ipmi-host.
1564 // The meaning of these bits appears to come from
1565 // table 43.1 of the IPMI spec.
1566 // The above 5 sensor attributes are stuffed in as follows:
1567 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1568 // Byte 22-24 are for other purposes
1569 // Byte 25 = MMMMMMMM = LSB of M
1570 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1571 // Byte 27 = BBBBBBBB = LSB of B
1572 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1573 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1574 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1575
1576 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1577 record.body.m_lsb = mValue & 0xFF;
1578
1579 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1580 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1581
1582 // move the smallest bit of the MSB into place (bit 9)
1583 // the MSbs are bits 7:8 in m_msb_and_tolerance
1584 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1585
1586 record.body.b_lsb = bValue & 0xFF;
1587
1588 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1589 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1590
1591 // move the smallest bit of the MSB into place (bit 9)
1592 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1593 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1594
1595 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1596 uint8_t rExpBits = rExp & 0x07;
1597
1598 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1599 uint8_t bExpBits = bExp & 0x07;
1600
1601 // move rExp and bExp into place
1602 record.body.r_b_exponents =
1603 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1604
1605 // Set the analog reading byte interpretation accordingly
1606 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1607
1608 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1609 // These seem redundant, but derivable from the above 5 attributes
1610 // Original comment said "todo fill out rest of units"
1611
1612 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001613 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001614 record.body.id_string_info = name.size();
1615 std::strncpy(record.body.id_string, name.c_str(),
1616 sizeof(record.body.id_string));
1617
Josh Lehana55c9532020-10-28 21:59:06 -07001618 // Remember the sensor name, as determined for this sensor number
1619 details::sdrStatsTable.updateName(sensornumber, name);
1620
Willy Tu530e2772021-07-02 14:42:06 -07001621#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1622 // Set the sensor settable state to true by default
1623 get_sdr::body::init_settable_state(true, &record.body);
1624#endif
1625
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001626 IPMIThresholds thresholdData;
1627 try
1628 {
1629 thresholdData = getIPMIThresholds(sensorMap);
1630 }
1631 catch (std::exception&)
1632 {
1633 phosphor::logging::log<phosphor::logging::level::ERR>(
1634 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001635 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001636 }
1637
1638 if (thresholdData.criticalHigh)
1639 {
1640 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1641 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1642 IPMISensorEventEnableThresholds::criticalThreshold);
1643 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1644 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1645 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1646 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1647 record.body.discrete_reading_setting_mask[0] |=
1648 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1649 }
1650 if (thresholdData.warningHigh)
1651 {
1652 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1653 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1654 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1655 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1656 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1657 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1658 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1659 record.body.discrete_reading_setting_mask[0] |=
1660 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1661 }
1662 if (thresholdData.criticalLow)
1663 {
1664 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1665 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1666 IPMISensorEventEnableThresholds::criticalThreshold);
1667 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1668 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1669 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1670 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1671 record.body.discrete_reading_setting_mask[0] |=
1672 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1673 }
1674 if (thresholdData.warningLow)
1675 {
1676 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1677 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1678 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1679 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1680 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1681 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1682 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1683 record.body.discrete_reading_setting_mask[0] |=
1684 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1685 }
1686
1687 // everything that is readable is setable
1688 record.body.discrete_reading_setting_mask[1] =
1689 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001690 return true;
1691}
1692
Scron Chang2703b022021-07-06 15:47:45 +08001693#ifdef FEATURE_HYBRID_SENSORS
1694// Construct a type 1 SDR for discrete Sensor typed sensor.
1695void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1696 uint16_t recordID,
1697 ipmi::sensor::IdInfoMap::const_iterator sensor,
1698 get_sdr::SensorDataFullRecord& record)
1699{
1700 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1701
1702 record.body.entity_id = sensor->second.entityType;
1703 record.body.sensor_type = sensor->second.sensorType;
1704 record.body.event_reading_type = sensor->second.sensorReadingType;
1705 record.body.entity_instance = sensor->second.instance;
1706 if (ipmi::sensor::Mutability::Write ==
1707 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1708 {
1709 get_sdr::body::init_settable_state(true, &(record.body));
1710 }
1711
1712 auto id_string = sensor->second.sensorName;
1713
1714 if (id_string.empty())
1715 {
1716 id_string = sensor->second.sensorNameFunc(sensor->second);
1717 }
1718
1719 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1720 {
1721 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1722 &(record.body));
1723 }
1724 else
1725 {
1726 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1727 }
1728 std::strncpy(record.body.id_string, id_string.c_str(),
1729 get_sdr::body::get_id_strlen(&(record.body)));
1730}
1731#endif
1732
Hao Jiange39d4d82021-04-16 17:02:40 -07001733// Construct type 3 SDR header and key (for VR and other discrete sensors)
1734void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1735 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001736{
1737 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1738 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1739
1740 get_sdr::header::set_record_id(
1741 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1742
1743 record.header.sdr_version = ipmiSdrVersion;
1744 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1745 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1746 sizeof(get_sdr::SensorDataRecordHeader);
1747 record.key.owner_id = bmcI2CAddr;
1748 record.key.owner_lun = lun;
1749 record.key.sensor_number = sensornumber;
1750
1751 record.body.entity_id = 0x00;
1752 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001753}
Willy Tu61992ad2021-03-29 15:33:20 -07001754
Hao Jiange39d4d82021-04-16 17:02:40 -07001755// Construct a type 3 SDR for VR typed sensor(daemon).
1756bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1757 uint16_t recordID, const std::string& service,
1758 const std::string& path,
1759 get_sdr::SensorDataEventRecord& record)
1760{
1761 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1762 constructEventSdrHeaderKey(sensorNum, recordID, record);
1763
1764 DbusInterfaceMap sensorMap;
1765 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1766 {
1767 phosphor::logging::log<phosphor::logging::level::ERR>(
1768 "Failed to update sensor map for VR sensor",
1769 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1770 phosphor::logging::entry("PATH=%s", path.c_str()));
1771 return false;
1772 }
Willy Tu61992ad2021-03-29 15:33:20 -07001773 // follow the association chain to get the parent board's entityid and
1774 // entityInstance
1775 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1776 record.body.entity_instance);
1777
1778 // Sensor type is hardcoded as a module/board type instead of parsing from
1779 // sensor path. This is because VR control is allocated in an independent
1780 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1781 // types.
1782 static constexpr const uint8_t module_board_type = 0x15;
1783 record.body.sensor_type = module_board_type;
1784 record.body.event_reading_type = 0x00;
1785
1786 record.body.sensor_record_sharing_1 = 0x00;
1787 record.body.sensor_record_sharing_2 = 0x00;
1788
1789 // populate sensor name from path
1790 auto name = sensor::parseSdrIdFromPath(path);
1791 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1792 record.body.id_string_info = nameSize;
1793 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1794 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1795
1796 // Remember the sensor name, as determined for this sensor number
1797 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001798
1799 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001800}
1801
Hao Jiange39d4d82021-04-16 17:02:40 -07001802static int
1803 getSensorDataRecord(ipmi::Context::ptr ctx,
1804 std::vector<uint8_t>& recordData, uint16_t recordID,
1805 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001806{
1807 size_t fruCount = 0;
1808 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1809 if (ret != ipmi::ccSuccess)
1810 {
1811 phosphor::logging::log<phosphor::logging::level::ERR>(
1812 "getSensorDataRecord: getFruSdrCount error");
1813 return GENERAL_ERROR;
1814 }
1815
1816 auto& sensorTree = getSensorTree();
1817 size_t lastRecord =
1818 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1819 if (recordID == lastRecordIndex)
1820 {
1821 recordID = lastRecord;
1822 }
1823 if (recordID > lastRecord)
1824 {
1825 phosphor::logging::log<phosphor::logging::level::ERR>(
1826 "getSensorDataRecord: recordID > lastRecord error");
1827 return GENERAL_ERROR;
1828 }
1829
1830 if (recordID >= sensorTree.size())
1831 {
1832 size_t fruIndex = recordID - sensorTree.size();
1833
1834 if (fruIndex >= fruCount)
1835 {
1836 // handle type 12 hardcoded records
1837 size_t type12Index = fruIndex - fruCount;
1838 if (type12Index >= ipmi::storage::type12Count)
1839 {
1840 phosphor::logging::log<phosphor::logging::level::ERR>(
1841 "getSensorDataRecord: type12Index error");
1842 return GENERAL_ERROR;
1843 }
1844 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1845 }
1846 else
1847 {
1848 // handle fru records
1849 get_sdr::SensorDataFruRecord data;
1850 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1851 if (ret != IPMI_CC_OK)
1852 {
1853 return GENERAL_ERROR;
1854 }
1855 data.header.record_id_msb = recordID >> 8;
1856 data.header.record_id_lsb = recordID & 0xFF;
1857 recordData.insert(recordData.end(), (uint8_t*)&data,
1858 ((uint8_t*)&data) + sizeof(data));
1859 }
1860
1861 return 0;
1862 }
1863
1864 std::string connection;
1865 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001866 std::vector<std::string> interfaces;
1867
1868 auto status =
1869 getSensorConnection(ctx, recordID, connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001870 if (status)
1871 {
1872 phosphor::logging::log<phosphor::logging::level::ERR>(
1873 "getSensorDataRecord: getSensorConnection error");
1874 return GENERAL_ERROR;
1875 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001876 uint16_t sensorNum = getSensorNumberFromPath(path);
1877 if (sensorNum == invalidSensorNumber)
1878 {
1879 phosphor::logging::log<phosphor::logging::level::ERR>(
1880 "getSensorDataRecord: invalidSensorNumber");
1881 return GENERAL_ERROR;
1882 }
1883
Willy Tu38e7a2b2021-03-29 15:09:56 -07001884 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001885 if (std::find(interfaces.begin(), interfaces.end(),
1886 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001887 {
1888 get_sdr::SensorDataFullRecord record = {0};
1889
Hao Jiange39d4d82021-04-16 17:02:40 -07001890 // If the request doesn't read SDR body, construct only header and key
1891 // part to avoid additional DBus transaction.
1892 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1893 {
1894 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1895 }
1896 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1897 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001898 {
1899 return GENERAL_ERROR;
1900 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001901
Willy Tu38e7a2b2021-03-29 15:09:56 -07001902 recordData.insert(recordData.end(), (uint8_t*)&record,
1903 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001904
1905 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001906 }
Willy Tu61992ad2021-03-29 15:33:20 -07001907
Scron Chang2703b022021-07-06 15:47:45 +08001908#ifdef FEATURE_HYBRID_SENSORS
1909 if (auto sensor = findStaticSensor(path);
1910 sensor != ipmi::sensor::sensors.end() &&
1911 getSensorEventTypeFromPath(path) !=
1912 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1913 {
1914 get_sdr::SensorDataFullRecord record = {0};
1915
1916 // If the request doesn't read SDR body, construct only header and key
1917 // part to avoid additional DBus transaction.
1918 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1919 {
1920 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1921 }
1922 else
1923 {
1924 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1925 }
1926
1927 recordData.insert(recordData.end(), (uint8_t*)&record,
1928 ((uint8_t*)&record) + sizeof(record));
1929
1930 return 0;
1931 }
1932#endif
1933
Willy Tu61992ad2021-03-29 15:33:20 -07001934 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07001935 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1936 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07001937 {
1938 get_sdr::SensorDataEventRecord record = {0};
1939
Hao Jiange39d4d82021-04-16 17:02:40 -07001940 // If the request doesn't read SDR body, construct only header and key
1941 // part to avoid additional DBus transaction.
1942 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1943 {
1944 constructEventSdrHeaderKey(sensorNum, recordID, record);
1945 }
1946 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1947 record))
1948 {
1949 return GENERAL_ERROR;
1950 }
Willy Tu61992ad2021-03-29 15:33:20 -07001951 recordData.insert(recordData.end(), (uint8_t*)&record,
1952 ((uint8_t*)&record) + sizeof(record));
1953 }
1954
Willy Tude54f482021-01-26 15:59:09 -08001955 return 0;
1956}
1957
1958/** @brief implements the get SDR Info command
1959 * @param count - Operation
1960 *
1961 * @returns IPMI completion code plus response data
1962 * - sdrCount - sensor/SDR count
1963 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1964 */
1965static ipmi::RspType<uint8_t, // respcount
1966 uint8_t, // dynamic population flags
1967 uint32_t // last time a sensor was added
1968 >
1969 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1970 std::optional<uint8_t> count)
1971{
1972 auto& sensorTree = getSensorTree();
1973 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001974 uint16_t recordID = 0;
1975 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001976 // Sensors are dynamically allocated, and there is at least one LUN
1977 uint8_t lunsAndDynamicPopulation = 0x80;
1978 constexpr uint8_t getSdrCount = 0x01;
1979 constexpr uint8_t getSensorCount = 0x00;
1980
1981 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1982 {
1983 return ipmi::responseResponseError();
1984 }
Willy Tude54f482021-01-26 15:59:09 -08001985 uint16_t numSensors = sensorTree.size();
1986 if (count.value_or(0) == getSdrCount)
1987 {
1988 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001989 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001990 {
1991 get_sdr::SensorDataRecordHeader* hdr =
1992 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001993 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001994 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1995 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001996 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001997 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001998 record.data());
1999 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002000 {
2001 sdrCount++;
2002 }
2003 }
2004 }
2005 }
2006 else if (count.value_or(0) == getSensorCount)
2007 {
2008 // Return the number of sensors attached to the LUN
2009 if ((ctx->lun == 0) && (numSensors > 0))
2010 {
2011 sdrCount =
2012 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2013 }
2014 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2015 {
2016 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2017 ? maxSensorsPerLUN
2018 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2019 }
2020 else if (ctx->lun == 3)
2021 {
2022 if (numSensors <= maxIPMISensors)
2023 {
2024 sdrCount =
2025 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2026 }
2027 else
2028 {
2029 // error
2030 throw std::out_of_range(
2031 "Maximum number of IPMI sensors exceeded.");
2032 }
2033 }
2034 }
2035 else
2036 {
2037 return ipmi::responseInvalidFieldRequest();
2038 }
2039
2040 // Get Sensor count. This returns the number of sensors
2041 if (numSensors > 0)
2042 {
2043 lunsAndDynamicPopulation |= 1;
2044 }
2045 if (numSensors > maxSensorsPerLUN)
2046 {
2047 lunsAndDynamicPopulation |= 2;
2048 }
2049 if (numSensors >= (maxSensorsPerLUN * 2))
2050 {
2051 lunsAndDynamicPopulation |= 8;
2052 }
2053 if (numSensors > maxIPMISensors)
2054 {
2055 // error
2056 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2057 }
2058
2059 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2060 sdrLastAdd);
2061}
2062
2063/* end sensor commands */
2064
2065/* storage commands */
2066
2067ipmi::RspType<uint8_t, // sdr version
2068 uint16_t, // record count
2069 uint16_t, // free space
2070 uint32_t, // most recent addition
2071 uint32_t, // most recent erase
2072 uint8_t // operationSupport
2073 >
2074 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2075{
2076 auto& sensorTree = getSensorTree();
2077 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002078 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002079 {
2080 return ipmi::responseResponseError();
2081 }
2082
2083 size_t fruCount = 0;
2084 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2085 if (ret != ipmi::ccSuccess)
2086 {
2087 return ipmi::response(ret);
2088 }
2089
2090 uint16_t recordCount =
2091 sensorTree.size() + fruCount + ipmi::storage::type12Count;
2092
2093 uint8_t operationSupport = static_cast<uint8_t>(
2094 SdrRepositoryInfoOps::overflow); // write not supported
2095
2096 operationSupport |=
2097 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2098 operationSupport |= static_cast<uint8_t>(
2099 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2100 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2101 unspecifiedFreeSpace, sdrLastAdd,
2102 sdrLastRemove, operationSupport);
2103}
2104
2105/** @brief implements the get SDR allocation info command
2106 *
2107 * @returns IPMI completion code plus response data
2108 * - allocUnits - Number of possible allocation units
2109 * - allocUnitSize - Allocation unit size in bytes.
2110 * - allocUnitFree - Number of free allocation units
2111 * - allocUnitLargestFree - Largest free block in allocation units
2112 * - maxRecordSize - Maximum record size in allocation units.
2113 */
2114ipmi::RspType<uint16_t, // allocUnits
2115 uint16_t, // allocUnitSize
2116 uint16_t, // allocUnitFree
2117 uint16_t, // allocUnitLargestFree
2118 uint8_t // maxRecordSize
2119 >
2120 ipmiStorageGetSDRAllocationInfo()
2121{
2122 // 0000h unspecified number of alloc units
2123 constexpr uint16_t allocUnits = 0;
2124
2125 constexpr uint16_t allocUnitFree = 0;
2126 constexpr uint16_t allocUnitLargestFree = 0;
2127 // only allow one block at a time
2128 constexpr uint8_t maxRecordSize = 1;
2129
2130 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2131 allocUnitLargestFree, maxRecordSize);
2132}
2133
2134/** @brief implements the reserve SDR command
2135 * @returns IPMI completion code plus response data
2136 * - sdrReservationID
2137 */
2138ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2139{
2140 sdrReservationID++;
2141 if (sdrReservationID == 0)
2142 {
2143 sdrReservationID++;
2144 }
2145
2146 return ipmi::responseSuccess(sdrReservationID);
2147}
2148
2149ipmi::RspType<uint16_t, // next record ID
2150 std::vector<uint8_t> // payload
2151 >
2152 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2153 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2154{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002155 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002156 // reservation required for partial reads with non zero offset into
2157 // record
2158 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2159 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002160 phosphor::logging::log<phosphor::logging::level::ERR>(
2161 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002162 return ipmi::responseInvalidReservationId();
2163 }
Willy Tude54f482021-01-26 15:59:09 -08002164 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2165 if (ret != ipmi::ccSuccess)
2166 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002167 phosphor::logging::log<phosphor::logging::level::ERR>(
2168 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002169 return ipmi::response(ret);
2170 }
2171
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002172 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002173 size_t lastRecord =
2174 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002175 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2176
2177 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002178 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002179 phosphor::logging::log<phosphor::logging::level::ERR>(
2180 "ipmiStorageGetSDR: getSensorSubtree error");
2181 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002182 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002183
2184 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002185 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002186 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002187 phosphor::logging::log<phosphor::logging::level::ERR>(
2188 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002189 return ipmi::responseInvalidFieldRequest();
2190 }
Willy Tude54f482021-01-26 15:59:09 -08002191 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002192 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002193 if (!hdr)
2194 {
2195 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002196 "ipmiStorageGetSDR: record header is null");
2197 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002198 }
2199
2200 size_t sdrLength =
2201 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2202 if (sdrLength < (offset + bytesToRead))
2203 {
2204 bytesToRead = sdrLength - offset;
2205 }
2206
2207 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2208 if (!respStart)
2209 {
2210 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002211 "ipmiStorageGetSDR: record is null");
2212 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002213 }
2214
2215 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002216
Willy Tude54f482021-01-26 15:59:09 -08002217 return ipmi::responseSuccess(nextRecordId, recordData);
2218}
2219/* end storage commands */
2220
2221void registerSensorFunctions()
2222{
2223 // <Platform Event>
2224 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2225 ipmi::sensor_event::cmdPlatformEvent,
2226 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2227
Willy Tudbafbce2021-03-29 00:37:05 -07002228#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
2229 // <Set Sensor Reading and Event Status>
2230 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2231 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2232 ipmi::Privilege::Operator, ipmiSetSensorReading);
2233#endif
2234
Willy Tude54f482021-01-26 15:59:09 -08002235 // <Get Sensor Reading>
2236 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2237 ipmi::sensor_event::cmdGetSensorReading,
2238 ipmi::Privilege::User, ipmiSenGetSensorReading);
2239
2240 // <Get Sensor Threshold>
2241 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2242 ipmi::sensor_event::cmdGetSensorThreshold,
2243 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2244
2245 // <Set Sensor Threshold>
2246 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2247 ipmi::sensor_event::cmdSetSensorThreshold,
2248 ipmi::Privilege::Operator,
2249 ipmiSenSetSensorThresholds);
2250
2251 // <Get Sensor Event Enable>
2252 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2253 ipmi::sensor_event::cmdGetSensorEventEnable,
2254 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2255
2256 // <Get Sensor Event Status>
2257 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2258 ipmi::sensor_event::cmdGetSensorEventStatus,
2259 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2260
2261 // register all storage commands for both Sensor and Storage command
2262 // versions
2263
2264 // <Get SDR Repository Info>
2265 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2266 ipmi::storage::cmdGetSdrRepositoryInfo,
2267 ipmi::Privilege::User,
2268 ipmiStorageGetSDRRepositoryInfo);
2269
2270 // <Get Device SDR Info>
2271 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2272 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2273 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2274
2275 // <Get SDR Allocation Info>
2276 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2277 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2278 ipmi::Privilege::User,
2279 ipmiStorageGetSDRAllocationInfo);
2280
2281 // <Reserve SDR Repo>
2282 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2283 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2284 ipmi::Privilege::User, ipmiStorageReserveSDR);
2285
2286 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2287 ipmi::storage::cmdReserveSdrRepository,
2288 ipmi::Privilege::User, ipmiStorageReserveSDR);
2289
2290 // <Get Sdr>
2291 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2292 ipmi::sensor_event::cmdGetDeviceSdr,
2293 ipmi::Privilege::User, ipmiStorageGetSDR);
2294
2295 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2296 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2297 ipmiStorageGetSDR);
2298}
2299} // namespace ipmi