blob: 3cebeb0bfbf93427c50c462ca8f74c7487117369 [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);
Harvey Wuf61c0862021-09-15 08:48:40 +0800543 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700544 {
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
Patrick Williamsef1259b2021-09-02 09:12:33 -0500569 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800570 // 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);
Harvey Wuf61c0862021-09-15 08:48:40 +0800593 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700594 {
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
Patrick Williamsef1259b2021-09-02 09:12:33 -0500610 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800611 // 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 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001101 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001102 {
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
Shakeeb Pasha93889722021-10-14 10:20:13 +05301532 double max = 0;
1533 double min = 0;
1534 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001535
1536 int16_t mValue = 0;
1537 int8_t rExp = 0;
1538 int16_t bValue = 0;
1539 int8_t bExp = 0;
1540 bool bSigned = false;
1541
1542 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1543 {
1544 phosphor::logging::log<phosphor::logging::level::ERR>(
1545 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001546 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001547 }
1548
1549 // The record.body is a struct SensorDataFullRecordBody
1550 // from sensorhandler.hpp in phosphor-ipmi-host.
1551 // The meaning of these bits appears to come from
1552 // table 43.1 of the IPMI spec.
1553 // The above 5 sensor attributes are stuffed in as follows:
1554 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1555 // Byte 22-24 are for other purposes
1556 // Byte 25 = MMMMMMMM = LSB of M
1557 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1558 // Byte 27 = BBBBBBBB = LSB of B
1559 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1560 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1561 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1562
1563 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1564 record.body.m_lsb = mValue & 0xFF;
1565
1566 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1567 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1568
1569 // move the smallest bit of the MSB into place (bit 9)
1570 // the MSbs are bits 7:8 in m_msb_and_tolerance
1571 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1572
1573 record.body.b_lsb = bValue & 0xFF;
1574
1575 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1576 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1577
1578 // move the smallest bit of the MSB into place (bit 9)
1579 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1580 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1581
1582 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1583 uint8_t rExpBits = rExp & 0x07;
1584
1585 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1586 uint8_t bExpBits = bExp & 0x07;
1587
1588 // move rExp and bExp into place
1589 record.body.r_b_exponents =
1590 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1591
1592 // Set the analog reading byte interpretation accordingly
1593 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1594
1595 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1596 // These seem redundant, but derivable from the above 5 attributes
1597 // Original comment said "todo fill out rest of units"
1598
1599 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001600 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001601 record.body.id_string_info = name.size();
1602 std::strncpy(record.body.id_string, name.c_str(),
1603 sizeof(record.body.id_string));
1604
Josh Lehana55c9532020-10-28 21:59:06 -07001605 // Remember the sensor name, as determined for this sensor number
1606 details::sdrStatsTable.updateName(sensornumber, name);
1607
Willy Tu530e2772021-07-02 14:42:06 -07001608#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
1609 // Set the sensor settable state to true by default
1610 get_sdr::body::init_settable_state(true, &record.body);
1611#endif
1612
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001613 IPMIThresholds thresholdData;
1614 try
1615 {
1616 thresholdData = getIPMIThresholds(sensorMap);
1617 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001618 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001619 {
1620 phosphor::logging::log<phosphor::logging::level::ERR>(
1621 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001622 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001623 }
1624
1625 if (thresholdData.criticalHigh)
1626 {
1627 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1628 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1629 IPMISensorEventEnableThresholds::criticalThreshold);
1630 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1631 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1632 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1633 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1634 record.body.discrete_reading_setting_mask[0] |=
1635 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1636 }
1637 if (thresholdData.warningHigh)
1638 {
1639 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1640 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1641 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1642 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1643 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1644 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1645 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1646 record.body.discrete_reading_setting_mask[0] |=
1647 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1648 }
1649 if (thresholdData.criticalLow)
1650 {
1651 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1652 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1653 IPMISensorEventEnableThresholds::criticalThreshold);
1654 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1655 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1656 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1657 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1658 record.body.discrete_reading_setting_mask[0] |=
1659 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1660 }
1661 if (thresholdData.warningLow)
1662 {
1663 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1664 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1665 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1666 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1667 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1668 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1669 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1670 record.body.discrete_reading_setting_mask[0] |=
1671 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1672 }
1673
1674 // everything that is readable is setable
1675 record.body.discrete_reading_setting_mask[1] =
1676 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001677 return true;
1678}
1679
Scron Chang2703b022021-07-06 15:47:45 +08001680#ifdef FEATURE_HYBRID_SENSORS
1681// Construct a type 1 SDR for discrete Sensor typed sensor.
1682void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1683 uint16_t recordID,
1684 ipmi::sensor::IdInfoMap::const_iterator sensor,
1685 get_sdr::SensorDataFullRecord& record)
1686{
1687 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1688
1689 record.body.entity_id = sensor->second.entityType;
1690 record.body.sensor_type = sensor->second.sensorType;
1691 record.body.event_reading_type = sensor->second.sensorReadingType;
1692 record.body.entity_instance = sensor->second.instance;
1693 if (ipmi::sensor::Mutability::Write ==
1694 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1695 {
1696 get_sdr::body::init_settable_state(true, &(record.body));
1697 }
1698
1699 auto id_string = sensor->second.sensorName;
1700
1701 if (id_string.empty())
1702 {
1703 id_string = sensor->second.sensorNameFunc(sensor->second);
1704 }
1705
1706 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1707 {
1708 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1709 &(record.body));
1710 }
1711 else
1712 {
1713 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1714 }
1715 std::strncpy(record.body.id_string, id_string.c_str(),
1716 get_sdr::body::get_id_strlen(&(record.body)));
1717}
1718#endif
1719
Hao Jiange39d4d82021-04-16 17:02:40 -07001720// Construct type 3 SDR header and key (for VR and other discrete sensors)
1721void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1722 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001723{
1724 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1725 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1726
1727 get_sdr::header::set_record_id(
1728 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1729
1730 record.header.sdr_version = ipmiSdrVersion;
1731 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1732 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1733 sizeof(get_sdr::SensorDataRecordHeader);
1734 record.key.owner_id = bmcI2CAddr;
1735 record.key.owner_lun = lun;
1736 record.key.sensor_number = sensornumber;
1737
1738 record.body.entity_id = 0x00;
1739 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001740}
Willy Tu61992ad2021-03-29 15:33:20 -07001741
Hao Jiange39d4d82021-04-16 17:02:40 -07001742// Construct a type 3 SDR for VR typed sensor(daemon).
1743bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1744 uint16_t recordID, const std::string& service,
1745 const std::string& path,
1746 get_sdr::SensorDataEventRecord& record)
1747{
1748 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1749 constructEventSdrHeaderKey(sensorNum, recordID, record);
1750
1751 DbusInterfaceMap sensorMap;
1752 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1753 {
1754 phosphor::logging::log<phosphor::logging::level::ERR>(
1755 "Failed to update sensor map for VR sensor",
1756 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1757 phosphor::logging::entry("PATH=%s", path.c_str()));
1758 return false;
1759 }
Willy Tu61992ad2021-03-29 15:33:20 -07001760 // follow the association chain to get the parent board's entityid and
1761 // entityInstance
1762 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1763 record.body.entity_instance);
1764
1765 // Sensor type is hardcoded as a module/board type instead of parsing from
1766 // sensor path. This is because VR control is allocated in an independent
1767 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1768 // types.
1769 static constexpr const uint8_t module_board_type = 0x15;
1770 record.body.sensor_type = module_board_type;
1771 record.body.event_reading_type = 0x00;
1772
1773 record.body.sensor_record_sharing_1 = 0x00;
1774 record.body.sensor_record_sharing_2 = 0x00;
1775
1776 // populate sensor name from path
1777 auto name = sensor::parseSdrIdFromPath(path);
1778 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1779 record.body.id_string_info = nameSize;
1780 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1781 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1782
1783 // Remember the sensor name, as determined for this sensor number
1784 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001785
1786 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001787}
1788
Hao Jiange39d4d82021-04-16 17:02:40 -07001789static int
1790 getSensorDataRecord(ipmi::Context::ptr ctx,
1791 std::vector<uint8_t>& recordData, uint16_t recordID,
1792 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001793{
1794 size_t fruCount = 0;
1795 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1796 if (ret != ipmi::ccSuccess)
1797 {
1798 phosphor::logging::log<phosphor::logging::level::ERR>(
1799 "getSensorDataRecord: getFruSdrCount error");
1800 return GENERAL_ERROR;
1801 }
1802
1803 auto& sensorTree = getSensorTree();
1804 size_t lastRecord =
1805 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1806 if (recordID == lastRecordIndex)
1807 {
1808 recordID = lastRecord;
1809 }
1810 if (recordID > lastRecord)
1811 {
1812 phosphor::logging::log<phosphor::logging::level::ERR>(
1813 "getSensorDataRecord: recordID > lastRecord error");
1814 return GENERAL_ERROR;
1815 }
1816
1817 if (recordID >= sensorTree.size())
1818 {
1819 size_t fruIndex = recordID - sensorTree.size();
1820
1821 if (fruIndex >= fruCount)
1822 {
1823 // handle type 12 hardcoded records
1824 size_t type12Index = fruIndex - fruCount;
1825 if (type12Index >= ipmi::storage::type12Count)
1826 {
1827 phosphor::logging::log<phosphor::logging::level::ERR>(
1828 "getSensorDataRecord: type12Index error");
1829 return GENERAL_ERROR;
1830 }
1831 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1832 }
1833 else
1834 {
1835 // handle fru records
1836 get_sdr::SensorDataFruRecord data;
1837 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1838 if (ret != IPMI_CC_OK)
1839 {
1840 return GENERAL_ERROR;
1841 }
1842 data.header.record_id_msb = recordID >> 8;
1843 data.header.record_id_lsb = recordID & 0xFF;
1844 recordData.insert(recordData.end(), (uint8_t*)&data,
1845 ((uint8_t*)&data) + sizeof(data));
1846 }
1847
1848 return 0;
1849 }
1850
1851 std::string connection;
1852 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001853 std::vector<std::string> interfaces;
1854
1855 auto status =
1856 getSensorConnection(ctx, recordID, connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001857 if (status)
1858 {
1859 phosphor::logging::log<phosphor::logging::level::ERR>(
1860 "getSensorDataRecord: getSensorConnection error");
1861 return GENERAL_ERROR;
1862 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001863 uint16_t sensorNum = getSensorNumberFromPath(path);
1864 if (sensorNum == invalidSensorNumber)
1865 {
1866 phosphor::logging::log<phosphor::logging::level::ERR>(
1867 "getSensorDataRecord: invalidSensorNumber");
1868 return GENERAL_ERROR;
1869 }
1870
Willy Tu38e7a2b2021-03-29 15:09:56 -07001871 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001872 if (std::find(interfaces.begin(), interfaces.end(),
1873 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001874 {
1875 get_sdr::SensorDataFullRecord record = {0};
1876
Hao Jiange39d4d82021-04-16 17:02:40 -07001877 // If the request doesn't read SDR body, construct only header and key
1878 // part to avoid additional DBus transaction.
1879 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1880 {
1881 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1882 }
1883 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1884 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001885 {
1886 return GENERAL_ERROR;
1887 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001888
Willy Tu38e7a2b2021-03-29 15:09:56 -07001889 recordData.insert(recordData.end(), (uint8_t*)&record,
1890 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001891
1892 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001893 }
Willy Tu61992ad2021-03-29 15:33:20 -07001894
Scron Chang2703b022021-07-06 15:47:45 +08001895#ifdef FEATURE_HYBRID_SENSORS
1896 if (auto sensor = findStaticSensor(path);
1897 sensor != ipmi::sensor::sensors.end() &&
1898 getSensorEventTypeFromPath(path) !=
1899 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1900 {
1901 get_sdr::SensorDataFullRecord record = {0};
1902
1903 // If the request doesn't read SDR body, construct only header and key
1904 // part to avoid additional DBus transaction.
1905 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1906 {
1907 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1908 }
1909 else
1910 {
1911 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1912 }
1913
1914 recordData.insert(recordData.end(), (uint8_t*)&record,
1915 ((uint8_t*)&record) + sizeof(record));
1916
1917 return 0;
1918 }
1919#endif
1920
Willy Tu61992ad2021-03-29 15:33:20 -07001921 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07001922 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1923 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07001924 {
1925 get_sdr::SensorDataEventRecord record = {0};
1926
Hao Jiange39d4d82021-04-16 17:02:40 -07001927 // If the request doesn't read SDR body, construct only header and key
1928 // part to avoid additional DBus transaction.
1929 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1930 {
1931 constructEventSdrHeaderKey(sensorNum, recordID, record);
1932 }
1933 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1934 record))
1935 {
1936 return GENERAL_ERROR;
1937 }
Willy Tu61992ad2021-03-29 15:33:20 -07001938 recordData.insert(recordData.end(), (uint8_t*)&record,
1939 ((uint8_t*)&record) + sizeof(record));
1940 }
1941
Willy Tude54f482021-01-26 15:59:09 -08001942 return 0;
1943}
1944
1945/** @brief implements the get SDR Info command
1946 * @param count - Operation
1947 *
1948 * @returns IPMI completion code plus response data
1949 * - sdrCount - sensor/SDR count
1950 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1951 */
1952static ipmi::RspType<uint8_t, // respcount
1953 uint8_t, // dynamic population flags
1954 uint32_t // last time a sensor was added
1955 >
1956 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1957 std::optional<uint8_t> count)
1958{
1959 auto& sensorTree = getSensorTree();
1960 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001961 uint16_t recordID = 0;
1962 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001963 // Sensors are dynamically allocated, and there is at least one LUN
1964 uint8_t lunsAndDynamicPopulation = 0x80;
1965 constexpr uint8_t getSdrCount = 0x01;
1966 constexpr uint8_t getSensorCount = 0x00;
1967
1968 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1969 {
1970 return ipmi::responseResponseError();
1971 }
Willy Tude54f482021-01-26 15:59:09 -08001972 uint16_t numSensors = sensorTree.size();
1973 if (count.value_or(0) == getSdrCount)
1974 {
1975 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001976 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001977 {
1978 get_sdr::SensorDataRecordHeader* hdr =
1979 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001980 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001981 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1982 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001983 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001984 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001985 record.data());
1986 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08001987 {
1988 sdrCount++;
1989 }
1990 }
1991 }
1992 }
1993 else if (count.value_or(0) == getSensorCount)
1994 {
1995 // Return the number of sensors attached to the LUN
1996 if ((ctx->lun == 0) && (numSensors > 0))
1997 {
1998 sdrCount =
1999 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2000 }
2001 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2002 {
2003 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2004 ? maxSensorsPerLUN
2005 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2006 }
2007 else if (ctx->lun == 3)
2008 {
2009 if (numSensors <= maxIPMISensors)
2010 {
2011 sdrCount =
2012 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2013 }
2014 else
2015 {
2016 // error
2017 throw std::out_of_range(
2018 "Maximum number of IPMI sensors exceeded.");
2019 }
2020 }
2021 }
2022 else
2023 {
2024 return ipmi::responseInvalidFieldRequest();
2025 }
2026
2027 // Get Sensor count. This returns the number of sensors
2028 if (numSensors > 0)
2029 {
2030 lunsAndDynamicPopulation |= 1;
2031 }
2032 if (numSensors > maxSensorsPerLUN)
2033 {
2034 lunsAndDynamicPopulation |= 2;
2035 }
2036 if (numSensors >= (maxSensorsPerLUN * 2))
2037 {
2038 lunsAndDynamicPopulation |= 8;
2039 }
2040 if (numSensors > maxIPMISensors)
2041 {
2042 // error
2043 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2044 }
2045
2046 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2047 sdrLastAdd);
2048}
2049
2050/* end sensor commands */
2051
2052/* storage commands */
2053
2054ipmi::RspType<uint8_t, // sdr version
2055 uint16_t, // record count
2056 uint16_t, // free space
2057 uint32_t, // most recent addition
2058 uint32_t, // most recent erase
2059 uint8_t // operationSupport
2060 >
2061 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2062{
2063 auto& sensorTree = getSensorTree();
2064 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002065 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002066 {
2067 return ipmi::responseResponseError();
2068 }
2069
2070 size_t fruCount = 0;
2071 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2072 if (ret != ipmi::ccSuccess)
2073 {
2074 return ipmi::response(ret);
2075 }
2076
2077 uint16_t recordCount =
2078 sensorTree.size() + fruCount + ipmi::storage::type12Count;
2079
2080 uint8_t operationSupport = static_cast<uint8_t>(
2081 SdrRepositoryInfoOps::overflow); // write not supported
2082
2083 operationSupport |=
2084 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2085 operationSupport |= static_cast<uint8_t>(
2086 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2087 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2088 unspecifiedFreeSpace, sdrLastAdd,
2089 sdrLastRemove, operationSupport);
2090}
2091
2092/** @brief implements the get SDR allocation info command
2093 *
2094 * @returns IPMI completion code plus response data
2095 * - allocUnits - Number of possible allocation units
2096 * - allocUnitSize - Allocation unit size in bytes.
2097 * - allocUnitFree - Number of free allocation units
2098 * - allocUnitLargestFree - Largest free block in allocation units
2099 * - maxRecordSize - Maximum record size in allocation units.
2100 */
2101ipmi::RspType<uint16_t, // allocUnits
2102 uint16_t, // allocUnitSize
2103 uint16_t, // allocUnitFree
2104 uint16_t, // allocUnitLargestFree
2105 uint8_t // maxRecordSize
2106 >
2107 ipmiStorageGetSDRAllocationInfo()
2108{
2109 // 0000h unspecified number of alloc units
2110 constexpr uint16_t allocUnits = 0;
2111
2112 constexpr uint16_t allocUnitFree = 0;
2113 constexpr uint16_t allocUnitLargestFree = 0;
2114 // only allow one block at a time
2115 constexpr uint8_t maxRecordSize = 1;
2116
2117 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2118 allocUnitLargestFree, maxRecordSize);
2119}
2120
2121/** @brief implements the reserve SDR command
2122 * @returns IPMI completion code plus response data
2123 * - sdrReservationID
2124 */
2125ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2126{
2127 sdrReservationID++;
2128 if (sdrReservationID == 0)
2129 {
2130 sdrReservationID++;
2131 }
2132
2133 return ipmi::responseSuccess(sdrReservationID);
2134}
2135
2136ipmi::RspType<uint16_t, // next record ID
2137 std::vector<uint8_t> // payload
2138 >
2139 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2140 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2141{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002142 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002143 // reservation required for partial reads with non zero offset into
2144 // record
2145 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2146 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002147 phosphor::logging::log<phosphor::logging::level::ERR>(
2148 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002149 return ipmi::responseInvalidReservationId();
2150 }
Willy Tude54f482021-01-26 15:59:09 -08002151 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2152 if (ret != ipmi::ccSuccess)
2153 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002154 phosphor::logging::log<phosphor::logging::level::ERR>(
2155 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002156 return ipmi::response(ret);
2157 }
2158
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002159 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002160 size_t lastRecord =
2161 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002162 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2163
2164 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002165 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002166 phosphor::logging::log<phosphor::logging::level::ERR>(
2167 "ipmiStorageGetSDR: getSensorSubtree error");
2168 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002169 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002170
2171 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002172 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002173 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002174 phosphor::logging::log<phosphor::logging::level::ERR>(
2175 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002176 return ipmi::responseInvalidFieldRequest();
2177 }
Willy Tude54f482021-01-26 15:59:09 -08002178 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002179 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002180 if (!hdr)
2181 {
2182 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002183 "ipmiStorageGetSDR: record header is null");
2184 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002185 }
2186
2187 size_t sdrLength =
2188 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2189 if (sdrLength < (offset + bytesToRead))
2190 {
2191 bytesToRead = sdrLength - offset;
2192 }
2193
2194 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2195 if (!respStart)
2196 {
2197 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002198 "ipmiStorageGetSDR: record is null");
2199 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002200 }
2201
2202 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002203
Willy Tude54f482021-01-26 15:59:09 -08002204 return ipmi::responseSuccess(nextRecordId, recordData);
2205}
2206/* end storage commands */
2207
2208void registerSensorFunctions()
2209{
2210 // <Platform Event>
2211 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2212 ipmi::sensor_event::cmdPlatformEvent,
2213 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2214
Willy Tudbafbce2021-03-29 00:37:05 -07002215#ifdef FEATURE_DYNAMIC_SENSORS_WRITE
2216 // <Set Sensor Reading and Event Status>
2217 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2218 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2219 ipmi::Privilege::Operator, ipmiSetSensorReading);
2220#endif
2221
Willy Tude54f482021-01-26 15:59:09 -08002222 // <Get Sensor Reading>
2223 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2224 ipmi::sensor_event::cmdGetSensorReading,
2225 ipmi::Privilege::User, ipmiSenGetSensorReading);
2226
2227 // <Get Sensor Threshold>
2228 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2229 ipmi::sensor_event::cmdGetSensorThreshold,
2230 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2231
2232 // <Set Sensor Threshold>
2233 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2234 ipmi::sensor_event::cmdSetSensorThreshold,
2235 ipmi::Privilege::Operator,
2236 ipmiSenSetSensorThresholds);
2237
2238 // <Get Sensor Event Enable>
2239 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2240 ipmi::sensor_event::cmdGetSensorEventEnable,
2241 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2242
2243 // <Get Sensor Event Status>
2244 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2245 ipmi::sensor_event::cmdGetSensorEventStatus,
2246 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2247
2248 // register all storage commands for both Sensor and Storage command
2249 // versions
2250
2251 // <Get SDR Repository Info>
2252 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2253 ipmi::storage::cmdGetSdrRepositoryInfo,
2254 ipmi::Privilege::User,
2255 ipmiStorageGetSDRRepositoryInfo);
2256
2257 // <Get Device SDR Info>
2258 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2259 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2260 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2261
2262 // <Get SDR Allocation Info>
2263 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2264 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2265 ipmi::Privilege::User,
2266 ipmiStorageGetSDRAllocationInfo);
2267
2268 // <Reserve SDR Repo>
2269 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2270 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2271 ipmi::Privilege::User, ipmiStorageReserveSDR);
2272
2273 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2274 ipmi::storage::cmdReserveSdrRepository,
2275 ipmi::Privilege::User, ipmiStorageReserveSDR);
2276
2277 // <Get Sdr>
2278 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2279 ipmi::sensor_event::cmdGetDeviceSdr,
2280 ipmi::Privilege::User, ipmiStorageGetSDR);
2281
2282 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2283 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2284 ipmiStorageGetSDR);
2285}
2286} // namespace ipmi