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