blob: 854d55f815727a2d136f5bee29effcf9b692f2bc [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
Jie Yangf0a89942021-07-29 15:30:25 -0700548 // Only allow external SetSensor if write permission granted
549 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
550 {
551 return ipmi::responseResponseError();
552 }
553
Hao Jiangd2afd052020-12-10 15:09:32 -0800554 auto value =
555 sensor::calculateValue(reading, sensorMap, sensorObject->second);
556 if (!value)
557 {
558 return ipmi::responseResponseError();
559 }
560
561 if constexpr (debug)
562 {
563 phosphor::logging::log<phosphor::logging::level::INFO>(
564 "IPMI SET_SENSOR",
565 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
566 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
567 phosphor::logging::entry("VALUE=%f", *value));
568 }
569
570 boost::system::error_code ec =
571 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
572 "Value", ipmi::Value(*value));
573
574 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500575 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800576 // callback functions for now (e.g. ipmiSetSensorReading).
577 if (ec)
578 {
579 using namespace phosphor::logging;
580 log<level::ERR>("Failed to set property",
581 entry("PROPERTY=%s", "Value"),
582 entry("PATH=%s", path.c_str()),
583 entry("INTERFACE=%s", sensor::sensorInterface),
584 entry("WHAT=%s", ec.message().c_str()));
585 return ipmi::responseResponseError();
586 }
587 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700588 }
589
Hao Jiange39d4d82021-04-16 17:02:40 -0700590 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
591 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700592 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700593 DbusInterfaceMap sensorMap;
594 if (!getSensorMap(ctx, connection, path, sensorMap))
595 {
596 return ipmi::responseResponseError();
597 }
598 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800599 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700600 {
601 return ipmi::responseResponseError();
602 }
603
Hao Jiangd2afd052020-12-10 15:09:32 -0800604 // VR sensors are treated as a special case and we will not check the
605 // write permission for VR sensors, since they always deemed writable
606 // and permission table are not applied to VR sensors.
607 auto vrMode =
608 sensor::calculateVRMode(assertOffset, sensorObject->second);
609 if (!vrMode)
610 {
611 return ipmi::responseResponseError();
612 }
613 boost::system::error_code ec = setDbusProperty(
614 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
615 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500616 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800617 // callback functions for now (e.g. ipmiSetSensorReading).
618 if (ec)
619 {
620 using namespace phosphor::logging;
621 log<level::ERR>("Failed to set property",
622 entry("PROPERTY=%s", "Selected"),
623 entry("PATH=%s", path.c_str()),
624 entry("INTERFACE=%s", sensor::sensorInterface),
625 entry("WHAT=%s", ec.message().c_str()));
626 return ipmi::responseResponseError();
627 }
628 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700629 }
630
Hao Jiangd2afd052020-12-10 15:09:32 -0800631 phosphor::logging::log<phosphor::logging::level::ERR>(
632 "unknown sensor type",
633 phosphor::logging::entry("PATH=%s", path.c_str()));
634 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700635}
636
Willy Tude54f482021-01-26 15:59:09 -0800637ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
638 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
639{
640 std::string connection;
641 std::string path;
642
643 auto status = getSensorConnection(ctx, sensnum, connection, path);
644 if (status)
645 {
646 return ipmi::response(status);
647 }
648
Scron Chang2703b022021-07-06 15:47:45 +0800649#ifdef FEATURE_HYBRID_SENSORS
650 if (auto sensor = findStaticSensor(path);
651 sensor != ipmi::sensor::sensors.end() &&
652 getSensorEventTypeFromPath(path) !=
653 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
654 {
655 if (ipmi::sensor::Mutability::Read !=
656 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
657 {
658 return ipmi::responseIllegalCommand();
659 }
660
661 uint8_t operation;
662 try
663 {
664 ipmi::sensor::GetSensorResponse getResponse =
665 sensor->second.getFunc(sensor->second);
666
667 if (getResponse.readingOrStateUnavailable)
668 {
669 operation |= static_cast<uint8_t>(
670 IPMISensorReadingByte2::readingStateUnavailable);
671 }
672 if (getResponse.scanningEnabled)
673 {
674 operation |= static_cast<uint8_t>(
675 IPMISensorReadingByte2::sensorScanningEnable);
676 }
677 if (getResponse.allEventMessagesEnabled)
678 {
679 operation |= static_cast<uint8_t>(
680 IPMISensorReadingByte2::eventMessagesEnable);
681 }
682 return ipmi::responseSuccess(
683 getResponse.reading, operation,
684 getResponse.thresholdLevelsStates,
685 getResponse.discreteReadingSensorStates);
686 }
687 catch (const std::exception& e)
688 {
689 operation |= static_cast<uint8_t>(
690 IPMISensorReadingByte2::readingStateUnavailable);
691 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
692 }
693 }
694#endif
695
Willy Tude54f482021-01-26 15:59:09 -0800696 DbusInterfaceMap sensorMap;
697 if (!getSensorMap(ctx, connection, path, sensorMap))
698 {
699 return ipmi::responseResponseError();
700 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800701 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800702
703 if (sensorObject == sensorMap.end() ||
704 sensorObject->second.find("Value") == sensorObject->second.end())
705 {
706 return ipmi::responseResponseError();
707 }
708 auto& valueVariant = sensorObject->second["Value"];
709 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
710
711 double max = 0;
712 double min = 0;
713 getSensorMaxMin(sensorMap, max, min);
714
715 int16_t mValue = 0;
716 int16_t bValue = 0;
717 int8_t rExp = 0;
718 int8_t bExp = 0;
719 bool bSigned = false;
720
721 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
722 {
723 return ipmi::responseResponseError();
724 }
725
726 uint8_t value =
727 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
728 uint8_t operation =
729 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
730 operation |=
731 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
732 bool notReading = std::isnan(reading);
733
734 if (!notReading)
735 {
736 auto availableObject =
737 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
738 if (availableObject != sensorMap.end())
739 {
740 auto findAvailable = availableObject->second.find("Available");
741 if (findAvailable != availableObject->second.end())
742 {
743 bool* available = std::get_if<bool>(&(findAvailable->second));
744 if (available && !(*available))
745 {
746 notReading = true;
747 }
748 }
749 }
750 }
751
752 if (notReading)
753 {
754 operation |= static_cast<uint8_t>(
755 IPMISensorReadingByte2::readingStateUnavailable);
756 }
757
Josh Lehana55c9532020-10-28 21:59:06 -0700758 if constexpr (details::enableInstrumentation)
759 {
760 int byteValue;
761 if (bSigned)
762 {
763 byteValue = static_cast<int>(static_cast<int8_t>(value));
764 }
765 else
766 {
767 byteValue = static_cast<int>(static_cast<uint8_t>(value));
768 }
769
770 // Keep stats on the reading just obtained, even if it is "NaN"
771 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
772 {
773 // This is the first reading, show the coefficients
774 double step = (max - min) / 255.0;
775 std::cerr << "IPMI sensor "
776 << details::sdrStatsTable.getName(sensnum)
777 << ": Range min=" << min << " max=" << max
778 << ", step=" << step
779 << ", Coefficients mValue=" << static_cast<int>(mValue)
780 << " rExp=" << static_cast<int>(rExp)
781 << " bValue=" << static_cast<int>(bValue)
782 << " bExp=" << static_cast<int>(bExp)
783 << " bSigned=" << static_cast<int>(bSigned) << "\n";
784 }
785 }
786
Willy Tude54f482021-01-26 15:59:09 -0800787 uint8_t thresholds = 0;
788
789 auto warningObject =
790 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
791 if (warningObject != sensorMap.end())
792 {
793 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
794 auto alarmLow = warningObject->second.find("WarningAlarmLow");
795 if (alarmHigh != warningObject->second.end())
796 {
797 if (std::get<bool>(alarmHigh->second))
798 {
799 thresholds |= static_cast<uint8_t>(
800 IPMISensorReadingByte3::upperNonCritical);
801 }
802 }
803 if (alarmLow != warningObject->second.end())
804 {
805 if (std::get<bool>(alarmLow->second))
806 {
807 thresholds |= static_cast<uint8_t>(
808 IPMISensorReadingByte3::lowerNonCritical);
809 }
810 }
811 }
812
813 auto criticalObject =
814 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
815 if (criticalObject != sensorMap.end())
816 {
817 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
818 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
819 if (alarmHigh != criticalObject->second.end())
820 {
821 if (std::get<bool>(alarmHigh->second))
822 {
823 thresholds |=
824 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
825 }
826 }
827 if (alarmLow != criticalObject->second.end())
828 {
829 if (std::get<bool>(alarmLow->second))
830 {
831 thresholds |=
832 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
833 }
834 }
835 }
836
837 // no discrete as of today so optional byte is never returned
838 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
839}
840
841/** @brief implements the Set Sensor threshold command
842 * @param sensorNumber - sensor number
843 * @param lowerNonCriticalThreshMask
844 * @param lowerCriticalThreshMask
845 * @param lowerNonRecovThreshMask
846 * @param upperNonCriticalThreshMask
847 * @param upperCriticalThreshMask
848 * @param upperNonRecovThreshMask
849 * @param reserved
850 * @param lowerNonCritical - lower non-critical threshold
851 * @param lowerCritical - Lower critical threshold
852 * @param lowerNonRecoverable - Lower non recovarable threshold
853 * @param upperNonCritical - Upper non-critical threshold
854 * @param upperCritical - Upper critical
855 * @param upperNonRecoverable - Upper Non-recoverable
856 *
857 * @returns IPMI completion code
858 */
859ipmi::RspType<> ipmiSenSetSensorThresholds(
860 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
861 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
862 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
863 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
864 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
865 uint8_t upperNonCritical, uint8_t upperCritical,
866 uint8_t upperNonRecoverable)
867{
868 if (reserved)
869 {
870 return ipmi::responseInvalidFieldRequest();
871 }
872
873 // lower nc and upper nc not suppported on any sensor
874 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
875 {
876 return ipmi::responseInvalidFieldRequest();
877 }
878
879 // if none of the threshold mask are set, nothing to do
880 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
881 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
882 upperCriticalThreshMask | upperNonRecovThreshMask))
883 {
884 return ipmi::responseSuccess();
885 }
886
887 std::string connection;
888 std::string path;
889
890 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
891 if (status)
892 {
893 return ipmi::response(status);
894 }
895 DbusInterfaceMap sensorMap;
896 if (!getSensorMap(ctx, connection, path, sensorMap))
897 {
898 return ipmi::responseResponseError();
899 }
900
901 double max = 0;
902 double min = 0;
903 getSensorMaxMin(sensorMap, max, min);
904
905 int16_t mValue = 0;
906 int16_t bValue = 0;
907 int8_t rExp = 0;
908 int8_t bExp = 0;
909 bool bSigned = false;
910
911 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
912 {
913 return ipmi::responseResponseError();
914 }
915
916 // store a vector of property name, value to set, and interface
917 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
918
919 // define the indexes of the tuple
920 constexpr uint8_t propertyName = 0;
921 constexpr uint8_t thresholdValue = 1;
922 constexpr uint8_t interface = 2;
923 // verifiy all needed fields are present
924 if (lowerCriticalThreshMask || upperCriticalThreshMask)
925 {
926 auto findThreshold =
927 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
928 if (findThreshold == sensorMap.end())
929 {
930 return ipmi::responseInvalidFieldRequest();
931 }
932 if (lowerCriticalThreshMask)
933 {
934 auto findLower = findThreshold->second.find("CriticalLow");
935 if (findLower == findThreshold->second.end())
936 {
937 return ipmi::responseInvalidFieldRequest();
938 }
939 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
940 findThreshold->first);
941 }
942 if (upperCriticalThreshMask)
943 {
944 auto findUpper = findThreshold->second.find("CriticalHigh");
945 if (findUpper == findThreshold->second.end())
946 {
947 return ipmi::responseInvalidFieldRequest();
948 }
949 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
950 findThreshold->first);
951 }
952 }
953 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
954 {
955 auto findThreshold =
956 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
957 if (findThreshold == sensorMap.end())
958 {
959 return ipmi::responseInvalidFieldRequest();
960 }
961 if (lowerNonCriticalThreshMask)
962 {
963 auto findLower = findThreshold->second.find("WarningLow");
964 if (findLower == findThreshold->second.end())
965 {
966 return ipmi::responseInvalidFieldRequest();
967 }
968 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
969 findThreshold->first);
970 }
971 if (upperNonCriticalThreshMask)
972 {
973 auto findUpper = findThreshold->second.find("WarningHigh");
974 if (findUpper == findThreshold->second.end())
975 {
976 return ipmi::responseInvalidFieldRequest();
977 }
978 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
979 findThreshold->first);
980 }
981 }
982 for (const auto& property : thresholdsToSet)
983 {
984 // from section 36.3 in the IPMI Spec, assume all linear
985 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
986 (bValue * std::pow(10.0, bExp))) *
987 std::pow(10.0, rExp);
988 setDbusProperty(
989 *getSdBus(), connection, path, std::get<interface>(property),
990 std::get<propertyName>(property), ipmi::Value(valueToSet));
991 }
992 return ipmi::responseSuccess();
993}
994
995IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
996{
997 IPMIThresholds resp;
998 auto warningInterface =
999 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1000 auto criticalInterface =
1001 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1002
1003 if ((warningInterface != sensorMap.end()) ||
1004 (criticalInterface != sensorMap.end()))
1005 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001006 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001007
1008 if (sensorPair == sensorMap.end())
1009 {
1010 // should not have been able to find a sensor not implementing
1011 // the sensor object
1012 throw std::runtime_error("Invalid sensor map");
1013 }
1014
1015 double max = 0;
1016 double min = 0;
1017 getSensorMaxMin(sensorMap, max, min);
1018
1019 int16_t mValue = 0;
1020 int16_t bValue = 0;
1021 int8_t rExp = 0;
1022 int8_t bExp = 0;
1023 bool bSigned = false;
1024
1025 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1026 {
1027 throw std::runtime_error("Invalid sensor atrributes");
1028 }
1029 if (warningInterface != sensorMap.end())
1030 {
1031 auto& warningMap = warningInterface->second;
1032
1033 auto warningHigh = warningMap.find("WarningHigh");
1034 auto warningLow = warningMap.find("WarningLow");
1035
1036 if (warningHigh != warningMap.end())
1037 {
1038
1039 double value =
1040 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1041 resp.warningHigh = scaleIPMIValueFromDouble(
1042 value, mValue, rExp, bValue, bExp, bSigned);
1043 }
1044 if (warningLow != warningMap.end())
1045 {
1046 double value =
1047 std::visit(VariantToDoubleVisitor(), warningLow->second);
1048 resp.warningLow = scaleIPMIValueFromDouble(
1049 value, mValue, rExp, bValue, bExp, bSigned);
1050 }
1051 }
1052 if (criticalInterface != sensorMap.end())
1053 {
1054 auto& criticalMap = criticalInterface->second;
1055
1056 auto criticalHigh = criticalMap.find("CriticalHigh");
1057 auto criticalLow = criticalMap.find("CriticalLow");
1058
1059 if (criticalHigh != criticalMap.end())
1060 {
1061 double value =
1062 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1063 resp.criticalHigh = scaleIPMIValueFromDouble(
1064 value, mValue, rExp, bValue, bExp, bSigned);
1065 }
1066 if (criticalLow != criticalMap.end())
1067 {
1068 double value =
1069 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1070 resp.criticalLow = scaleIPMIValueFromDouble(
1071 value, mValue, rExp, bValue, bExp, bSigned);
1072 }
1073 }
1074 }
1075 return resp;
1076}
1077
1078ipmi::RspType<uint8_t, // readable
1079 uint8_t, // lowerNCrit
1080 uint8_t, // lowerCrit
1081 uint8_t, // lowerNrecoverable
1082 uint8_t, // upperNC
1083 uint8_t, // upperCrit
1084 uint8_t> // upperNRecoverable
1085 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1086{
1087 std::string connection;
1088 std::string path;
1089
1090 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1091 if (status)
1092 {
1093 return ipmi::response(status);
1094 }
1095
1096 DbusInterfaceMap sensorMap;
1097 if (!getSensorMap(ctx, connection, path, sensorMap))
1098 {
1099 return ipmi::responseResponseError();
1100 }
1101
1102 IPMIThresholds thresholdData;
1103 try
1104 {
1105 thresholdData = getIPMIThresholds(sensorMap);
1106 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001107 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001108 {
1109 return ipmi::responseResponseError();
1110 }
1111
1112 uint8_t readable = 0;
1113 uint8_t lowerNC = 0;
1114 uint8_t lowerCritical = 0;
1115 uint8_t lowerNonRecoverable = 0;
1116 uint8_t upperNC = 0;
1117 uint8_t upperCritical = 0;
1118 uint8_t upperNonRecoverable = 0;
1119
1120 if (thresholdData.warningHigh)
1121 {
1122 readable |=
1123 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1124 upperNC = *thresholdData.warningHigh;
1125 }
1126 if (thresholdData.warningLow)
1127 {
1128 readable |=
1129 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1130 lowerNC = *thresholdData.warningLow;
1131 }
1132
1133 if (thresholdData.criticalHigh)
1134 {
1135 readable |=
1136 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1137 upperCritical = *thresholdData.criticalHigh;
1138 }
1139 if (thresholdData.criticalLow)
1140 {
1141 readable |=
1142 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1143 lowerCritical = *thresholdData.criticalLow;
1144 }
1145
1146 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1147 lowerNonRecoverable, upperNC, upperCritical,
1148 upperNonRecoverable);
1149}
1150
1151/** @brief implements the get Sensor event enable command
1152 * @param sensorNumber - sensor number
1153 *
1154 * @returns IPMI completion code plus response data
1155 * - enabled - Sensor Event messages
1156 * - assertionEnabledLsb - Assertion event messages
1157 * - assertionEnabledMsb - Assertion event messages
1158 * - deassertionEnabledLsb - Deassertion event messages
1159 * - deassertionEnabledMsb - Deassertion event messages
1160 */
1161
1162ipmi::RspType<uint8_t, // enabled
1163 uint8_t, // assertionEnabledLsb
1164 uint8_t, // assertionEnabledMsb
1165 uint8_t, // deassertionEnabledLsb
1166 uint8_t> // deassertionEnabledMsb
1167 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1168{
1169 std::string connection;
1170 std::string path;
1171
1172 uint8_t enabled = 0;
1173 uint8_t assertionEnabledLsb = 0;
1174 uint8_t assertionEnabledMsb = 0;
1175 uint8_t deassertionEnabledLsb = 0;
1176 uint8_t deassertionEnabledMsb = 0;
1177
1178 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1179 if (status)
1180 {
1181 return ipmi::response(status);
1182 }
1183
Scron Chang2703b022021-07-06 15:47:45 +08001184#ifdef FEATURE_HYBRID_SENSORS
1185 if (auto sensor = findStaticSensor(path);
1186 sensor != ipmi::sensor::sensors.end() &&
1187 getSensorEventTypeFromPath(path) !=
1188 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1189 {
1190 enabled = static_cast<uint8_t>(
1191 IPMISensorEventEnableByte2::sensorScanningEnable);
1192 uint16_t assertionEnabled = 0;
1193 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1194 ->second.begin()
1195 ->second.second)
1196 {
1197 assertionEnabled |= (1 << offsetValMap.first);
1198 }
1199 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1200 assertionEnabledMsb =
1201 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1202
1203 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1204 assertionEnabledMsb, deassertionEnabledLsb,
1205 deassertionEnabledMsb);
1206 }
1207#endif
1208
Willy Tude54f482021-01-26 15:59:09 -08001209 DbusInterfaceMap sensorMap;
1210 if (!getSensorMap(ctx, connection, path, sensorMap))
1211 {
1212 return ipmi::responseResponseError();
1213 }
1214
1215 auto warningInterface =
1216 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1217 auto criticalInterface =
1218 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1219 if ((warningInterface != sensorMap.end()) ||
1220 (criticalInterface != sensorMap.end()))
1221 {
1222 enabled = static_cast<uint8_t>(
1223 IPMISensorEventEnableByte2::sensorScanningEnable);
1224 if (warningInterface != sensorMap.end())
1225 {
1226 auto& warningMap = warningInterface->second;
1227
1228 auto warningHigh = warningMap.find("WarningHigh");
1229 auto warningLow = warningMap.find("WarningLow");
1230 if (warningHigh != warningMap.end())
1231 {
1232 assertionEnabledLsb |= static_cast<uint8_t>(
1233 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1234 deassertionEnabledLsb |= static_cast<uint8_t>(
1235 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1236 }
1237 if (warningLow != warningMap.end())
1238 {
1239 assertionEnabledLsb |= static_cast<uint8_t>(
1240 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1241 deassertionEnabledLsb |= static_cast<uint8_t>(
1242 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1243 }
1244 }
1245 if (criticalInterface != sensorMap.end())
1246 {
1247 auto& criticalMap = criticalInterface->second;
1248
1249 auto criticalHigh = criticalMap.find("CriticalHigh");
1250 auto criticalLow = criticalMap.find("CriticalLow");
1251
1252 if (criticalHigh != criticalMap.end())
1253 {
1254 assertionEnabledMsb |= static_cast<uint8_t>(
1255 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1256 deassertionEnabledMsb |= static_cast<uint8_t>(
1257 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1258 }
1259 if (criticalLow != criticalMap.end())
1260 {
1261 assertionEnabledLsb |= static_cast<uint8_t>(
1262 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1263 deassertionEnabledLsb |= static_cast<uint8_t>(
1264 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1265 }
1266 }
1267 }
1268
1269 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1270 assertionEnabledMsb, deassertionEnabledLsb,
1271 deassertionEnabledMsb);
1272}
1273
1274/** @brief implements the get Sensor event status command
1275 * @param sensorNumber - sensor number, FFh = reserved
1276 *
1277 * @returns IPMI completion code plus response data
1278 * - sensorEventStatus - Sensor Event messages state
1279 * - assertions - Assertion event messages
1280 * - deassertions - Deassertion event messages
1281 */
1282ipmi::RspType<uint8_t, // sensorEventStatus
1283 std::bitset<16>, // assertions
1284 std::bitset<16> // deassertion
1285 >
1286 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1287{
1288 if (sensorNum == reservedSensorNumber)
1289 {
1290 return ipmi::responseInvalidFieldRequest();
1291 }
1292
1293 std::string connection;
1294 std::string path;
1295 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1296 if (status)
1297 {
1298 phosphor::logging::log<phosphor::logging::level::ERR>(
1299 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1300 phosphor::logging::entry("SENSOR=%d", sensorNum));
1301 return ipmi::response(status);
1302 }
1303
Scron Chang2703b022021-07-06 15:47:45 +08001304#ifdef FEATURE_HYBRID_SENSORS
1305 if (auto sensor = findStaticSensor(path);
1306 sensor != ipmi::sensor::sensors.end() &&
1307 getSensorEventTypeFromPath(path) !=
1308 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1309 {
1310 auto response = ipmi::sensor::get::mapDbusToAssertion(
1311 sensor->second, path, sensor->second.sensorInterface);
1312 std::bitset<16> assertions;
1313 // deassertions are not used.
1314 std::bitset<16> deassertions = 0;
1315 uint8_t sensorEventStatus;
1316 if (response.readingOrStateUnavailable)
1317 {
1318 sensorEventStatus |= static_cast<uint8_t>(
1319 IPMISensorReadingByte2::readingStateUnavailable);
1320 }
1321 if (response.scanningEnabled)
1322 {
1323 sensorEventStatus |= static_cast<uint8_t>(
1324 IPMISensorReadingByte2::sensorScanningEnable);
1325 }
1326 if (response.allEventMessagesEnabled)
1327 {
1328 sensorEventStatus |= static_cast<uint8_t>(
1329 IPMISensorReadingByte2::eventMessagesEnable);
1330 }
1331 assertions |= response.discreteReadingSensorStates << 8;
1332 assertions |= response.thresholdLevelsStates;
1333 return ipmi::responseSuccess(sensorEventStatus, assertions,
1334 deassertions);
1335 }
1336#endif
1337
Willy Tude54f482021-01-26 15:59:09 -08001338 DbusInterfaceMap sensorMap;
1339 if (!getSensorMap(ctx, connection, path, sensorMap))
1340 {
1341 phosphor::logging::log<phosphor::logging::level::ERR>(
1342 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1343 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1344 return ipmi::responseResponseError();
1345 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001346
1347 uint8_t sensorEventStatus =
1348 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1349 std::bitset<16> assertions = 0;
1350 std::bitset<16> deassertions = 0;
1351
1352 // handle VR typed sensor
1353 auto vrInterface = sensorMap.find(sensor::vrInterface);
1354 if (vrInterface != sensorMap.end())
1355 {
1356 if (!sensor::getVrEventStatus(ctx, connection, path,
1357 vrInterface->second, assertions))
1358 {
1359 return ipmi::responseResponseError();
1360 }
1361
1362 // both Event Message and Sensor Scanning are disable for VR.
1363 sensorEventStatus = 0;
1364 return ipmi::responseSuccess(sensorEventStatus, assertions,
1365 deassertions);
1366 }
1367
Willy Tude54f482021-01-26 15:59:09 -08001368 auto warningInterface =
1369 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1370 auto criticalInterface =
1371 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1372
Willy Tude54f482021-01-26 15:59:09 -08001373 std::optional<bool> criticalDeassertHigh =
1374 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1375 std::optional<bool> criticalDeassertLow =
1376 thresholdDeassertMap[path]["CriticalAlarmLow"];
1377 std::optional<bool> warningDeassertHigh =
1378 thresholdDeassertMap[path]["WarningAlarmHigh"];
1379 std::optional<bool> warningDeassertLow =
1380 thresholdDeassertMap[path]["WarningAlarmLow"];
1381
Willy Tude54f482021-01-26 15:59:09 -08001382 if (criticalDeassertHigh && !*criticalDeassertHigh)
1383 {
1384 deassertions.set(static_cast<size_t>(
1385 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1386 }
1387 if (criticalDeassertLow && !*criticalDeassertLow)
1388 {
1389 deassertions.set(static_cast<size_t>(
1390 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1391 }
1392 if (warningDeassertHigh && !*warningDeassertHigh)
1393 {
1394 deassertions.set(static_cast<size_t>(
1395 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1396 }
1397 if (warningDeassertLow && !*warningDeassertLow)
1398 {
1399 deassertions.set(static_cast<size_t>(
1400 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1401 }
1402 if ((warningInterface != sensorMap.end()) ||
1403 (criticalInterface != sensorMap.end()))
1404 {
1405 sensorEventStatus = static_cast<size_t>(
1406 IPMISensorEventEnableByte2::eventMessagesEnable);
1407 if (warningInterface != sensorMap.end())
1408 {
1409 auto& warningMap = warningInterface->second;
1410
1411 auto warningHigh = warningMap.find("WarningAlarmHigh");
1412 auto warningLow = warningMap.find("WarningAlarmLow");
1413 auto warningHighAlarm = false;
1414 auto warningLowAlarm = false;
1415
1416 if (warningHigh != warningMap.end())
1417 {
1418 warningHighAlarm = std::get<bool>(warningHigh->second);
1419 }
1420 if (warningLow != warningMap.end())
1421 {
1422 warningLowAlarm = std::get<bool>(warningLow->second);
1423 }
1424 if (warningHighAlarm)
1425 {
1426 assertions.set(
1427 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1428 upperNonCriticalGoingHigh));
1429 }
1430 if (warningLowAlarm)
1431 {
1432 assertions.set(
1433 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1434 lowerNonCriticalGoingLow));
1435 }
1436 }
1437 if (criticalInterface != sensorMap.end())
1438 {
1439 auto& criticalMap = criticalInterface->second;
1440
1441 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1442 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1443 auto criticalHighAlarm = false;
1444 auto criticalLowAlarm = false;
1445
1446 if (criticalHigh != criticalMap.end())
1447 {
1448 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1449 }
1450 if (criticalLow != criticalMap.end())
1451 {
1452 criticalLowAlarm = std::get<bool>(criticalLow->second);
1453 }
1454 if (criticalHighAlarm)
1455 {
1456 assertions.set(
1457 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1458 upperCriticalGoingHigh));
1459 }
1460 if (criticalLowAlarm)
1461 {
1462 assertions.set(static_cast<size_t>(
1463 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1464 }
1465 }
1466 }
1467
1468 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1469}
1470
Willy Tu38e7a2b2021-03-29 15:09:56 -07001471// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001472void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1473 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001474{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001475 get_sdr::header::set_record_id(
1476 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1477
Willy Tu38e7a2b2021-03-29 15:09:56 -07001478 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1479 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1480
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001481 record.header.sdr_version = ipmiSdrVersion;
1482 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1483 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1484 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001485 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001486 record.key.owner_lun = lun;
1487 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001488}
1489bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1490 uint16_t recordID, const std::string& service,
1491 const std::string& path,
1492 get_sdr::SensorDataFullRecord& record)
1493{
1494 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1495 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1496
1497 DbusInterfaceMap sensorMap;
1498 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1499 {
1500 phosphor::logging::log<phosphor::logging::level::ERR>(
1501 "Failed to update sensor map for threshold sensor",
1502 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1503 phosphor::logging::entry("PATH=%s", path.c_str()));
1504 return false;
1505 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001506
1507 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1508 record.body.sensor_type = getSensorTypeFromPath(path);
1509 std::string type = getSensorTypeStringFromPath(path);
1510 auto typeCstr = type.c_str();
1511 auto findUnits = sensorUnits.find(typeCstr);
1512 if (findUnits != sensorUnits.end())
1513 {
1514 record.body.sensor_units_2_base =
1515 static_cast<uint8_t>(findUnits->second);
1516 } // else default 0x0 unspecified
1517
1518 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1519
Hao Jiangd2afd052020-12-10 15:09:32 -08001520 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001521 if (sensorObject == sensorMap.end())
1522 {
1523 phosphor::logging::log<phosphor::logging::level::ERR>(
1524 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001525 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001526 }
1527
1528 uint8_t entityId = 0;
1529 uint8_t entityInstance = 0x01;
1530
1531 // follow the association chain to get the parent board's entityid and
1532 // entityInstance
1533 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1534
1535 record.body.entity_id = entityId;
1536 record.body.entity_instance = entityInstance;
1537
Shakeeb Pasha93889722021-10-14 10:20:13 +05301538 double max = 0;
1539 double min = 0;
1540 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001541
1542 int16_t mValue = 0;
1543 int8_t rExp = 0;
1544 int16_t bValue = 0;
1545 int8_t bExp = 0;
1546 bool bSigned = false;
1547
1548 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1549 {
1550 phosphor::logging::log<phosphor::logging::level::ERR>(
1551 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001552 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001553 }
1554
1555 // The record.body is a struct SensorDataFullRecordBody
1556 // from sensorhandler.hpp in phosphor-ipmi-host.
1557 // The meaning of these bits appears to come from
1558 // table 43.1 of the IPMI spec.
1559 // The above 5 sensor attributes are stuffed in as follows:
1560 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1561 // Byte 22-24 are for other purposes
1562 // Byte 25 = MMMMMMMM = LSB of M
1563 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1564 // Byte 27 = BBBBBBBB = LSB of B
1565 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1566 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1567 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1568
1569 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1570 record.body.m_lsb = mValue & 0xFF;
1571
1572 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1573 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1574
1575 // move the smallest bit of the MSB into place (bit 9)
1576 // the MSbs are bits 7:8 in m_msb_and_tolerance
1577 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1578
1579 record.body.b_lsb = bValue & 0xFF;
1580
1581 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1582 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1583
1584 // move the smallest bit of the MSB into place (bit 9)
1585 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1586 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1587
1588 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1589 uint8_t rExpBits = rExp & 0x07;
1590
1591 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1592 uint8_t bExpBits = bExp & 0x07;
1593
1594 // move rExp and bExp into place
1595 record.body.r_b_exponents =
1596 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1597
1598 // Set the analog reading byte interpretation accordingly
1599 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1600
1601 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1602 // These seem redundant, but derivable from the above 5 attributes
1603 // Original comment said "todo fill out rest of units"
1604
1605 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001606 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001607 record.body.id_string_info = name.size();
1608 std::strncpy(record.body.id_string, name.c_str(),
1609 sizeof(record.body.id_string));
1610
Josh Lehana55c9532020-10-28 21:59:06 -07001611 // Remember the sensor name, as determined for this sensor number
1612 details::sdrStatsTable.updateName(sensornumber, name);
1613
Jie Yangf0a89942021-07-29 15:30:25 -07001614 bool sensorSettable = false;
1615 auto mutability =
1616 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1617 if (mutability != sensorMap.end())
1618 {
1619 sensorSettable =
1620 mappedVariant<bool>(mutability->second, "Mutable", false);
1621 }
1622 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1623
1624 // Grant write permission to sensors deemed externally settable
1625 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001626
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001627 IPMIThresholds thresholdData;
1628 try
1629 {
1630 thresholdData = getIPMIThresholds(sensorMap);
1631 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001632 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001633 {
1634 phosphor::logging::log<phosphor::logging::level::ERR>(
1635 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001636 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001637 }
1638
1639 if (thresholdData.criticalHigh)
1640 {
1641 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1642 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1643 IPMISensorEventEnableThresholds::criticalThreshold);
1644 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1645 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1646 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1647 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1648 record.body.discrete_reading_setting_mask[0] |=
1649 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1650 }
1651 if (thresholdData.warningHigh)
1652 {
1653 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1654 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1655 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1656 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1657 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1658 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1659 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1660 record.body.discrete_reading_setting_mask[0] |=
1661 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1662 }
1663 if (thresholdData.criticalLow)
1664 {
1665 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1666 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1667 IPMISensorEventEnableThresholds::criticalThreshold);
1668 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1669 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1670 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1671 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1672 record.body.discrete_reading_setting_mask[0] |=
1673 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1674 }
1675 if (thresholdData.warningLow)
1676 {
1677 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1678 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1679 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1680 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1681 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1682 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1683 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1684 record.body.discrete_reading_setting_mask[0] |=
1685 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1686 }
1687
1688 // everything that is readable is setable
1689 record.body.discrete_reading_setting_mask[1] =
1690 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001691 return true;
1692}
1693
Scron Chang2703b022021-07-06 15:47:45 +08001694#ifdef FEATURE_HYBRID_SENSORS
1695// Construct a type 1 SDR for discrete Sensor typed sensor.
1696void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1697 uint16_t recordID,
1698 ipmi::sensor::IdInfoMap::const_iterator sensor,
1699 get_sdr::SensorDataFullRecord& record)
1700{
1701 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1702
1703 record.body.entity_id = sensor->second.entityType;
1704 record.body.sensor_type = sensor->second.sensorType;
1705 record.body.event_reading_type = sensor->second.sensorReadingType;
1706 record.body.entity_instance = sensor->second.instance;
1707 if (ipmi::sensor::Mutability::Write ==
1708 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1709 {
1710 get_sdr::body::init_settable_state(true, &(record.body));
1711 }
1712
1713 auto id_string = sensor->second.sensorName;
1714
1715 if (id_string.empty())
1716 {
1717 id_string = sensor->second.sensorNameFunc(sensor->second);
1718 }
1719
1720 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1721 {
1722 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1723 &(record.body));
1724 }
1725 else
1726 {
1727 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1728 }
1729 std::strncpy(record.body.id_string, id_string.c_str(),
1730 get_sdr::body::get_id_strlen(&(record.body)));
1731}
1732#endif
1733
Hao Jiange39d4d82021-04-16 17:02:40 -07001734// Construct type 3 SDR header and key (for VR and other discrete sensors)
1735void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1736 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001737{
1738 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1739 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1740
1741 get_sdr::header::set_record_id(
1742 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1743
1744 record.header.sdr_version = ipmiSdrVersion;
1745 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1746 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1747 sizeof(get_sdr::SensorDataRecordHeader);
1748 record.key.owner_id = bmcI2CAddr;
1749 record.key.owner_lun = lun;
1750 record.key.sensor_number = sensornumber;
1751
1752 record.body.entity_id = 0x00;
1753 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001754}
Willy Tu61992ad2021-03-29 15:33:20 -07001755
Hao Jiange39d4d82021-04-16 17:02:40 -07001756// Construct a type 3 SDR for VR typed sensor(daemon).
1757bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1758 uint16_t recordID, const std::string& service,
1759 const std::string& path,
1760 get_sdr::SensorDataEventRecord& record)
1761{
1762 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1763 constructEventSdrHeaderKey(sensorNum, recordID, record);
1764
1765 DbusInterfaceMap sensorMap;
1766 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1767 {
1768 phosphor::logging::log<phosphor::logging::level::ERR>(
1769 "Failed to update sensor map for VR sensor",
1770 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1771 phosphor::logging::entry("PATH=%s", path.c_str()));
1772 return false;
1773 }
Willy Tu61992ad2021-03-29 15:33:20 -07001774 // follow the association chain to get the parent board's entityid and
1775 // entityInstance
1776 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1777 record.body.entity_instance);
1778
1779 // Sensor type is hardcoded as a module/board type instead of parsing from
1780 // sensor path. This is because VR control is allocated in an independent
1781 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1782 // types.
1783 static constexpr const uint8_t module_board_type = 0x15;
1784 record.body.sensor_type = module_board_type;
1785 record.body.event_reading_type = 0x00;
1786
1787 record.body.sensor_record_sharing_1 = 0x00;
1788 record.body.sensor_record_sharing_2 = 0x00;
1789
1790 // populate sensor name from path
1791 auto name = sensor::parseSdrIdFromPath(path);
1792 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1793 record.body.id_string_info = nameSize;
1794 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1795 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1796
1797 // Remember the sensor name, as determined for this sensor number
1798 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001799
1800 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001801}
1802
Hao Jiange39d4d82021-04-16 17:02:40 -07001803static int
1804 getSensorDataRecord(ipmi::Context::ptr ctx,
1805 std::vector<uint8_t>& recordData, uint16_t recordID,
1806 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001807{
1808 size_t fruCount = 0;
1809 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1810 if (ret != ipmi::ccSuccess)
1811 {
1812 phosphor::logging::log<phosphor::logging::level::ERR>(
1813 "getSensorDataRecord: getFruSdrCount error");
1814 return GENERAL_ERROR;
1815 }
1816
1817 auto& sensorTree = getSensorTree();
1818 size_t lastRecord =
1819 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1820 if (recordID == lastRecordIndex)
1821 {
1822 recordID = lastRecord;
1823 }
1824 if (recordID > lastRecord)
1825 {
1826 phosphor::logging::log<phosphor::logging::level::ERR>(
1827 "getSensorDataRecord: recordID > lastRecord error");
1828 return GENERAL_ERROR;
1829 }
1830
1831 if (recordID >= sensorTree.size())
1832 {
1833 size_t fruIndex = recordID - sensorTree.size();
1834
1835 if (fruIndex >= fruCount)
1836 {
1837 // handle type 12 hardcoded records
1838 size_t type12Index = fruIndex - fruCount;
1839 if (type12Index >= ipmi::storage::type12Count)
1840 {
1841 phosphor::logging::log<phosphor::logging::level::ERR>(
1842 "getSensorDataRecord: type12Index error");
1843 return GENERAL_ERROR;
1844 }
1845 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1846 }
1847 else
1848 {
1849 // handle fru records
1850 get_sdr::SensorDataFruRecord data;
1851 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1852 if (ret != IPMI_CC_OK)
1853 {
1854 return GENERAL_ERROR;
1855 }
1856 data.header.record_id_msb = recordID >> 8;
1857 data.header.record_id_lsb = recordID & 0xFF;
1858 recordData.insert(recordData.end(), (uint8_t*)&data,
1859 ((uint8_t*)&data) + sizeof(data));
1860 }
1861
1862 return 0;
1863 }
1864
1865 std::string connection;
1866 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001867 std::vector<std::string> interfaces;
1868
1869 auto status =
1870 getSensorConnection(ctx, recordID, connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001871 if (status)
1872 {
1873 phosphor::logging::log<phosphor::logging::level::ERR>(
1874 "getSensorDataRecord: getSensorConnection error");
1875 return GENERAL_ERROR;
1876 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001877 uint16_t sensorNum = getSensorNumberFromPath(path);
1878 if (sensorNum == invalidSensorNumber)
1879 {
1880 phosphor::logging::log<phosphor::logging::level::ERR>(
1881 "getSensorDataRecord: invalidSensorNumber");
1882 return GENERAL_ERROR;
1883 }
1884
Willy Tu38e7a2b2021-03-29 15:09:56 -07001885 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001886 if (std::find(interfaces.begin(), interfaces.end(),
1887 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001888 {
1889 get_sdr::SensorDataFullRecord record = {0};
1890
Hao Jiange39d4d82021-04-16 17:02:40 -07001891 // If the request doesn't read SDR body, construct only header and key
1892 // part to avoid additional DBus transaction.
1893 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1894 {
1895 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1896 }
1897 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1898 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001899 {
1900 return GENERAL_ERROR;
1901 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001902
Willy Tu38e7a2b2021-03-29 15:09:56 -07001903 recordData.insert(recordData.end(), (uint8_t*)&record,
1904 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001905
1906 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001907 }
Willy Tu61992ad2021-03-29 15:33:20 -07001908
Scron Chang2703b022021-07-06 15:47:45 +08001909#ifdef FEATURE_HYBRID_SENSORS
1910 if (auto sensor = findStaticSensor(path);
1911 sensor != ipmi::sensor::sensors.end() &&
1912 getSensorEventTypeFromPath(path) !=
1913 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1914 {
1915 get_sdr::SensorDataFullRecord record = {0};
1916
1917 // If the request doesn't read SDR body, construct only header and key
1918 // part to avoid additional DBus transaction.
1919 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1920 {
1921 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1922 }
1923 else
1924 {
1925 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1926 }
1927
1928 recordData.insert(recordData.end(), (uint8_t*)&record,
1929 ((uint8_t*)&record) + sizeof(record));
1930
1931 return 0;
1932 }
1933#endif
1934
Willy Tu61992ad2021-03-29 15:33:20 -07001935 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07001936 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
1937 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07001938 {
1939 get_sdr::SensorDataEventRecord record = {0};
1940
Hao Jiange39d4d82021-04-16 17:02:40 -07001941 // If the request doesn't read SDR body, construct only header and key
1942 // part to avoid additional DBus transaction.
1943 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1944 {
1945 constructEventSdrHeaderKey(sensorNum, recordID, record);
1946 }
1947 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
1948 record))
1949 {
1950 return GENERAL_ERROR;
1951 }
Willy Tu61992ad2021-03-29 15:33:20 -07001952 recordData.insert(recordData.end(), (uint8_t*)&record,
1953 ((uint8_t*)&record) + sizeof(record));
1954 }
1955
Willy Tude54f482021-01-26 15:59:09 -08001956 return 0;
1957}
1958
1959/** @brief implements the get SDR Info command
1960 * @param count - Operation
1961 *
1962 * @returns IPMI completion code plus response data
1963 * - sdrCount - sensor/SDR count
1964 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1965 */
1966static ipmi::RspType<uint8_t, // respcount
1967 uint8_t, // dynamic population flags
1968 uint32_t // last time a sensor was added
1969 >
1970 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
1971 std::optional<uint8_t> count)
1972{
1973 auto& sensorTree = getSensorTree();
1974 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001975 uint16_t recordID = 0;
1976 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08001977 // Sensors are dynamically allocated, and there is at least one LUN
1978 uint8_t lunsAndDynamicPopulation = 0x80;
1979 constexpr uint8_t getSdrCount = 0x01;
1980 constexpr uint8_t getSensorCount = 0x00;
1981
1982 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
1983 {
1984 return ipmi::responseResponseError();
1985 }
Willy Tude54f482021-01-26 15:59:09 -08001986 uint16_t numSensors = sensorTree.size();
1987 if (count.value_or(0) == getSdrCount)
1988 {
1989 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001990 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08001991 {
1992 get_sdr::SensorDataRecordHeader* hdr =
1993 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001994 record.data());
Willy Tude54f482021-01-26 15:59:09 -08001995 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
1996 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001997 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08001998 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001999 record.data());
2000 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002001 {
2002 sdrCount++;
2003 }
2004 }
2005 }
2006 }
2007 else if (count.value_or(0) == getSensorCount)
2008 {
2009 // Return the number of sensors attached to the LUN
2010 if ((ctx->lun == 0) && (numSensors > 0))
2011 {
2012 sdrCount =
2013 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2014 }
2015 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2016 {
2017 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2018 ? maxSensorsPerLUN
2019 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2020 }
2021 else if (ctx->lun == 3)
2022 {
2023 if (numSensors <= maxIPMISensors)
2024 {
2025 sdrCount =
2026 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2027 }
2028 else
2029 {
2030 // error
2031 throw std::out_of_range(
2032 "Maximum number of IPMI sensors exceeded.");
2033 }
2034 }
2035 }
2036 else
2037 {
2038 return ipmi::responseInvalidFieldRequest();
2039 }
2040
2041 // Get Sensor count. This returns the number of sensors
2042 if (numSensors > 0)
2043 {
2044 lunsAndDynamicPopulation |= 1;
2045 }
2046 if (numSensors > maxSensorsPerLUN)
2047 {
2048 lunsAndDynamicPopulation |= 2;
2049 }
2050 if (numSensors >= (maxSensorsPerLUN * 2))
2051 {
2052 lunsAndDynamicPopulation |= 8;
2053 }
2054 if (numSensors > maxIPMISensors)
2055 {
2056 // error
2057 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2058 }
2059
2060 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2061 sdrLastAdd);
2062}
2063
2064/* end sensor commands */
2065
2066/* storage commands */
2067
2068ipmi::RspType<uint8_t, // sdr version
2069 uint16_t, // record count
2070 uint16_t, // free space
2071 uint32_t, // most recent addition
2072 uint32_t, // most recent erase
2073 uint8_t // operationSupport
2074 >
2075 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2076{
2077 auto& sensorTree = getSensorTree();
2078 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002079 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002080 {
2081 return ipmi::responseResponseError();
2082 }
2083
2084 size_t fruCount = 0;
2085 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2086 if (ret != ipmi::ccSuccess)
2087 {
2088 return ipmi::response(ret);
2089 }
2090
2091 uint16_t recordCount =
2092 sensorTree.size() + fruCount + ipmi::storage::type12Count;
2093
2094 uint8_t operationSupport = static_cast<uint8_t>(
2095 SdrRepositoryInfoOps::overflow); // write not supported
2096
2097 operationSupport |=
2098 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2099 operationSupport |= static_cast<uint8_t>(
2100 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2101 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2102 unspecifiedFreeSpace, sdrLastAdd,
2103 sdrLastRemove, operationSupport);
2104}
2105
2106/** @brief implements the get SDR allocation info command
2107 *
2108 * @returns IPMI completion code plus response data
2109 * - allocUnits - Number of possible allocation units
2110 * - allocUnitSize - Allocation unit size in bytes.
2111 * - allocUnitFree - Number of free allocation units
2112 * - allocUnitLargestFree - Largest free block in allocation units
2113 * - maxRecordSize - Maximum record size in allocation units.
2114 */
2115ipmi::RspType<uint16_t, // allocUnits
2116 uint16_t, // allocUnitSize
2117 uint16_t, // allocUnitFree
2118 uint16_t, // allocUnitLargestFree
2119 uint8_t // maxRecordSize
2120 >
2121 ipmiStorageGetSDRAllocationInfo()
2122{
2123 // 0000h unspecified number of alloc units
2124 constexpr uint16_t allocUnits = 0;
2125
2126 constexpr uint16_t allocUnitFree = 0;
2127 constexpr uint16_t allocUnitLargestFree = 0;
2128 // only allow one block at a time
2129 constexpr uint8_t maxRecordSize = 1;
2130
2131 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2132 allocUnitLargestFree, maxRecordSize);
2133}
2134
2135/** @brief implements the reserve SDR command
2136 * @returns IPMI completion code plus response data
2137 * - sdrReservationID
2138 */
2139ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2140{
2141 sdrReservationID++;
2142 if (sdrReservationID == 0)
2143 {
2144 sdrReservationID++;
2145 }
2146
2147 return ipmi::responseSuccess(sdrReservationID);
2148}
2149
2150ipmi::RspType<uint16_t, // next record ID
2151 std::vector<uint8_t> // payload
2152 >
2153 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2154 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2155{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002156 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002157 // reservation required for partial reads with non zero offset into
2158 // record
2159 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2160 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002161 phosphor::logging::log<phosphor::logging::level::ERR>(
2162 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002163 return ipmi::responseInvalidReservationId();
2164 }
Willy Tude54f482021-01-26 15:59:09 -08002165 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2166 if (ret != ipmi::ccSuccess)
2167 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002168 phosphor::logging::log<phosphor::logging::level::ERR>(
2169 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002170 return ipmi::response(ret);
2171 }
2172
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002173 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002174 size_t lastRecord =
2175 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002176 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2177
2178 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002179 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002180 phosphor::logging::log<phosphor::logging::level::ERR>(
2181 "ipmiStorageGetSDR: getSensorSubtree error");
2182 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002183 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002184
2185 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002186 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002187 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002188 phosphor::logging::log<phosphor::logging::level::ERR>(
2189 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002190 return ipmi::responseInvalidFieldRequest();
2191 }
Willy Tude54f482021-01-26 15:59:09 -08002192 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002193 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002194 if (!hdr)
2195 {
2196 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002197 "ipmiStorageGetSDR: record header is null");
2198 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002199 }
2200
2201 size_t sdrLength =
2202 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2203 if (sdrLength < (offset + bytesToRead))
2204 {
2205 bytesToRead = sdrLength - offset;
2206 }
2207
2208 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2209 if (!respStart)
2210 {
2211 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002212 "ipmiStorageGetSDR: record is null");
2213 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002214 }
2215
2216 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002217
Willy Tude54f482021-01-26 15:59:09 -08002218 return ipmi::responseSuccess(nextRecordId, recordData);
2219}
2220/* end storage commands */
2221
2222void registerSensorFunctions()
2223{
2224 // <Platform Event>
2225 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2226 ipmi::sensor_event::cmdPlatformEvent,
2227 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2228
Willy Tudbafbce2021-03-29 00:37:05 -07002229 // <Set Sensor Reading and Event Status>
2230 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2231 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2232 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002233
Willy Tude54f482021-01-26 15:59:09 -08002234 // <Get Sensor Reading>
2235 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2236 ipmi::sensor_event::cmdGetSensorReading,
2237 ipmi::Privilege::User, ipmiSenGetSensorReading);
2238
2239 // <Get Sensor Threshold>
2240 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2241 ipmi::sensor_event::cmdGetSensorThreshold,
2242 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2243
2244 // <Set Sensor Threshold>
2245 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2246 ipmi::sensor_event::cmdSetSensorThreshold,
2247 ipmi::Privilege::Operator,
2248 ipmiSenSetSensorThresholds);
2249
2250 // <Get Sensor Event Enable>
2251 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2252 ipmi::sensor_event::cmdGetSensorEventEnable,
2253 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2254
2255 // <Get Sensor Event Status>
2256 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2257 ipmi::sensor_event::cmdGetSensorEventStatus,
2258 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2259
2260 // register all storage commands for both Sensor and Storage command
2261 // versions
2262
2263 // <Get SDR Repository Info>
2264 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2265 ipmi::storage::cmdGetSdrRepositoryInfo,
2266 ipmi::Privilege::User,
2267 ipmiStorageGetSDRRepositoryInfo);
2268
2269 // <Get Device SDR Info>
2270 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2271 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2272 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2273
2274 // <Get SDR Allocation Info>
2275 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2276 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2277 ipmi::Privilege::User,
2278 ipmiStorageGetSDRAllocationInfo);
2279
2280 // <Reserve SDR Repo>
2281 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2282 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2283 ipmi::Privilege::User, ipmiStorageReserveSDR);
2284
2285 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2286 ipmi::storage::cmdReserveSdrRepository,
2287 ipmi::Privilege::User, ipmiStorageReserveSDR);
2288
2289 // <Get Sdr>
2290 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2291 ipmi::sensor_event::cmdGetDeviceSdr,
2292 ipmi::Privilege::User, ipmiStorageGetSDR);
2293
2294 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2295 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2296 ipmiStorageGetSDR);
2297}
2298} // namespace ipmi