blob: 5a78ddb06f77fd03303331e1764947197d2cab4f [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"
Harvey Wu05d17c02021-09-15 08:46:59 +080022#include "entity_map_json.hpp"
Willy Tude54f482021-01-26 15:59:09 -080023
24#include <algorithm>
25#include <array>
26#include <boost/algorithm/string.hpp>
27#include <boost/container/flat_map.hpp>
28#include <chrono>
29#include <cmath>
30#include <cstring>
31#include <iostream>
32#include <ipmid/api.hpp>
33#include <ipmid/types.hpp>
34#include <ipmid/utils.hpp>
35#include <map>
36#include <memory>
37#include <optional>
38#include <phosphor-logging/log.hpp>
39#include <sdbusplus/bus.hpp>
40#include <stdexcept>
41#include <string>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000042#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080043#include <utility>
44#include <variant>
45
Scron Chang2703b022021-07-06 15:47:45 +080046#ifdef FEATURE_HYBRID_SENSORS
47
48#include "sensordatahandler.hpp"
49namespace ipmi
50{
51namespace sensor
52{
53extern const IdInfoMap sensors;
54} // namespace sensor
55} // namespace ipmi
56#endif
adarshgrami042e9db2022-09-15 10:34:34 +053057namespace ipmi
58{
59namespace dcmi
60{
61// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
62static const std::map<uint8_t, uint8_t> validEntityId{
63 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
64 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
65constexpr uint8_t temperatureSensorType = 0x01;
66constexpr uint8_t maxRecords = 8;
67} // namespace dcmi
68} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080069constexpr std::array<const char*, 7> suffixes = {
70 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
71 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080072namespace ipmi
73{
Hao Jiangd48c9212021-02-03 15:45:06 -080074
75using phosphor::logging::entry;
76using phosphor::logging::level;
77using phosphor::logging::log;
78
Willy Tude54f482021-01-26 15:59:09 -080079static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070080static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080081
Willy Tu38e7a2b2021-03-29 15:09:56 -070082// BMC I2C address is generally at 0x20
83static constexpr uint8_t bmcI2CAddr = 0x20;
84
Willy Tude54f482021-01-26 15:59:09 -080085constexpr size_t maxSDRTotalSize =
86 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
87constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
88
89static uint16_t sdrReservationID;
90static uint32_t sdrLastAdd = noTimestamp;
91static uint32_t sdrLastRemove = noTimestamp;
92static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070093
94// The IPMI spec defines four Logical Units (LUN), each capable of supporting
95// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
96// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
97// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
98// location.
99static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
100
101static constexpr size_t lun0MaxSensorNum = 0xfe;
102static constexpr size_t lun1MaxSensorNum = 0x1fe;
103static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800104static constexpr int GENERAL_ERROR = -1;
105
Willy Tude54f482021-01-26 15:59:09 -0800106static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
107
108// Specify the comparison required to sort and find char* map objects
109struct CmpStr
110{
111 bool operator()(const char* a, const char* b) const
112 {
113 return std::strcmp(a, b) < 0;
114 }
115};
116const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
117 sensorUnits{{{"temperature", SensorUnits::degreesC},
118 {"voltage", SensorUnits::volts},
119 {"current", SensorUnits::amps},
120 {"fan_tach", SensorUnits::rpm},
121 {"power", SensorUnits::watts}}};
122
123void registerSensorFunctions() __attribute__((constructor));
124
Patrick Williams5d82f472022-07-22 19:26:53 -0500125static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800126 *getSdBus(),
127 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
128 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500129 [](sdbusplus::message_t&) {
Willy Tude54f482021-01-26 15:59:09 -0800130 getSensorTree().clear();
Willy Tu4eca2512022-06-20 21:14:51 -0700131 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
Willy Tude54f482021-01-26 15:59:09 -0800132 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
133 std::chrono::system_clock::now().time_since_epoch())
134 .count();
135 });
136
Patrick Williams5d82f472022-07-22 19:26:53 -0500137static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800138 *getSdBus(),
139 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
140 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500141 [](sdbusplus::message_t&) {
Willy Tude54f482021-01-26 15:59:09 -0800142 getSensorTree().clear();
Willy Tu4eca2512022-06-20 21:14:51 -0700143 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
Willy Tude54f482021-01-26 15:59:09 -0800144 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
145 std::chrono::system_clock::now().time_since_epoch())
146 .count();
147 });
148
149// this keeps track of deassertions for sensor event status command. A
150// deasertion can only happen if an assertion was seen first.
151static boost::container::flat_map<
152 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
153 thresholdDeassertMap;
154
Patrick Williams5d82f472022-07-22 19:26:53 -0500155static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800156 *getSdBus(),
157 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
158 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500159 [](sdbusplus::message_t& m) {
Willy Tude54f482021-01-26 15:59:09 -0800160 boost::container::flat_map<std::string, std::variant<bool, double>>
161 values;
162 m.read(std::string(), values);
163
164 auto findAssert =
165 std::find_if(values.begin(), values.end(), [](const auto& pair) {
166 return pair.first.find("Alarm") != std::string::npos;
167 });
168 if (findAssert != values.end())
169 {
170 auto ptr = std::get_if<bool>(&(findAssert->second));
171 if (ptr == nullptr)
172 {
173 phosphor::logging::log<phosphor::logging::level::ERR>(
174 "thresholdChanged: Assert non bool");
175 return;
176 }
177 if (*ptr)
178 {
179 phosphor::logging::log<phosphor::logging::level::INFO>(
180 "thresholdChanged: Assert",
181 phosphor::logging::entry("SENSOR=%s", m.get_path()));
182 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
183 }
184 else
185 {
186 auto& value =
187 thresholdDeassertMap[m.get_path()][findAssert->first];
188 if (value)
189 {
190 phosphor::logging::log<phosphor::logging::level::INFO>(
191 "thresholdChanged: deassert",
192 phosphor::logging::entry("SENSOR=%s", m.get_path()));
193 value = *ptr;
194 }
195 }
196 }
197 });
198
Hao Jiangd2afd052020-12-10 15:09:32 -0800199namespace sensor
200{
201static constexpr const char* vrInterface =
202 "xyz.openbmc_project.Control.VoltageRegulatorMode";
203static constexpr const char* sensorInterface =
204 "xyz.openbmc_project.Sensor.Value";
205} // namespace sensor
206
Willy Tude54f482021-01-26 15:59:09 -0800207static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
208 double& min)
209{
210 max = 127;
211 min = -128;
212
Hao Jiangd2afd052020-12-10 15:09:32 -0800213 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800214 auto critical =
215 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
216 auto warning =
217 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
218
219 if (sensorObject != sensorMap.end())
220 {
221 auto maxMap = sensorObject->second.find("MaxValue");
222 auto minMap = sensorObject->second.find("MinValue");
223
224 if (maxMap != sensorObject->second.end())
225 {
226 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
227 }
228 if (minMap != sensorObject->second.end())
229 {
230 min = std::visit(VariantToDoubleVisitor(), minMap->second);
231 }
232 }
233 if (critical != sensorMap.end())
234 {
235 auto lower = critical->second.find("CriticalLow");
236 auto upper = critical->second.find("CriticalHigh");
237 if (lower != critical->second.end())
238 {
239 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300240 if (std::isfinite(value))
241 {
242 min = std::min(value, min);
243 }
Willy Tude54f482021-01-26 15:59:09 -0800244 }
245 if (upper != critical->second.end())
246 {
247 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300248 if (std::isfinite(value))
249 {
250 max = std::max(value, max);
251 }
Willy Tude54f482021-01-26 15:59:09 -0800252 }
253 }
254 if (warning != sensorMap.end())
255 {
256
257 auto lower = warning->second.find("WarningLow");
258 auto upper = warning->second.find("WarningHigh");
259 if (lower != warning->second.end())
260 {
261 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300262 if (std::isfinite(value))
263 {
264 min = std::min(value, min);
265 }
Willy Tude54f482021-01-26 15:59:09 -0800266 }
267 if (upper != warning->second.end())
268 {
269 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300270 if (std::isfinite(value))
271 {
272 max = std::max(value, max);
273 }
Willy Tude54f482021-01-26 15:59:09 -0800274 }
275 }
276}
277
278static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700279 std::string sensorPath, DbusInterfaceMap& sensorMap,
280 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800281{
Scron Chang2703b022021-07-06 15:47:45 +0800282#ifdef FEATURE_HYBRID_SENSORS
283 if (auto sensor = findStaticSensor(sensorPath);
284 sensor != ipmi::sensor::sensors.end() &&
285 getSensorEventTypeFromPath(sensorPath) !=
286 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
287 {
288 // If the incoming sensor is a discrete sensor, it might fail in
289 // getManagedObjects(), return true, and use its own getFunc to get
290 // value.
291 return true;
292 }
293#endif
294
Willy Tude54f482021-01-26 15:59:09 -0800295 static boost::container::flat_map<
296 std::string, std::chrono::time_point<std::chrono::steady_clock>>
297 updateTimeMap;
298
299 auto updateFind = updateTimeMap.find(sensorConnection);
300 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
301 if (updateFind != updateTimeMap.end())
302 {
303 lastUpdate = updateFind->second;
304 }
305
306 auto now = std::chrono::steady_clock::now();
307
308 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700309 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800310 {
Willy Tude54f482021-01-26 15:59:09 -0800311 ObjectValueTree managedObjects;
Ed Tanous9a9ac0b2022-10-04 17:18:57 -0700312 boost::system::error_code ec =
313 getManagedObjects(ctx, sensorConnection.c_str(),
314 "/xyz/openbmc_project/sensors", managedObjects);
Willy Tude54f482021-01-26 15:59:09 -0800315 if (ec)
316 {
317 phosphor::logging::log<phosphor::logging::level::ERR>(
318 "GetMangagedObjects for getSensorMap failed",
319 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
320
321 return false;
322 }
323
324 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700325 // Update time after finish building the map which allow the
326 // data to be cached for updatePeriod plus the build time.
327 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800328 }
329 auto connection = SensorCache.find(sensorConnection);
330 if (connection == SensorCache.end())
331 {
332 return false;
333 }
334 auto path = connection->second.find(sensorPath);
335 if (path == connection->second.end())
336 {
337 return false;
338 }
339 sensorMap = path->second;
340
341 return true;
342}
343
Hao Jiangd2afd052020-12-10 15:09:32 -0800344namespace sensor
345{
Hao Jiangd48c9212021-02-03 15:45:06 -0800346// Read VR profiles from sensor(daemon) interface
347static std::optional<std::vector<std::string>>
348 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800349{
350 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800351 auto supportedProperty = object.find("Supported");
352 if (supportedProperty == object.end() ||
353 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800354 {
355 phosphor::logging::log<phosphor::logging::level::ERR>(
356 "Missing the required Supported and Selected properties");
357 return std::nullopt;
358 }
359
360 const auto profilesPtr =
361 std::get_if<std::vector<std::string>>(&supportedProperty->second);
362
363 if (profilesPtr == nullptr)
364 {
365 phosphor::logging::log<phosphor::logging::level::ERR>(
366 "property is not array of string");
367 return std::nullopt;
368 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800369 return *profilesPtr;
370}
371
372// Calculate VR Mode from input IPMI discrete event bytes
373static std::optional<std::string>
374 calculateVRMode(uint15_t assertOffset,
375 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
376{
377 // get VR mode profiles from Supported Interface
378 auto profiles = getSupportedVrProfiles(VRObject);
379 if (!profiles)
380 {
381 return std::nullopt;
382 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800383
384 // interpret IPMI cmd bits into profiles' index
385 long unsigned int index = 0;
386 // only one bit should be set and the highest bit should not be used.
387 if (assertOffset == 0 || assertOffset == (1u << 15) ||
388 (assertOffset & (assertOffset - 1)))
389 {
390 phosphor::logging::log<phosphor::logging::level::ERR>(
391 "IPMI cmd format incorrect",
392
393 phosphor::logging::entry("BYTES=%#02x",
394 static_cast<uint16_t>(assertOffset)));
395 return std::nullopt;
396 }
397
398 while (assertOffset != 1)
399 {
400 assertOffset >>= 1;
401 index++;
402 }
403
Hao Jiangd48c9212021-02-03 15:45:06 -0800404 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800405 {
406 phosphor::logging::log<phosphor::logging::level::ERR>(
407 "profile index out of boundary");
408 return std::nullopt;
409 }
410
Hao Jiangd48c9212021-02-03 15:45:06 -0800411 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800412}
413
414// Calculate sensor value from IPMI reading byte
415static std::optional<double>
416 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
417 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
418{
419 if (valueObject.find("Value") == valueObject.end())
420 {
421 phosphor::logging::log<phosphor::logging::level::ERR>(
422 "Missing the required Value property");
423 return std::nullopt;
424 }
425
426 double max = 0;
427 double min = 0;
428 getSensorMaxMin(sensorMap, max, min);
429
430 int16_t mValue = 0;
431 int16_t bValue = 0;
432 int8_t rExp = 0;
433 int8_t bExp = 0;
434 bool bSigned = false;
435
436 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
437 {
438 return std::nullopt;
439 }
440
441 double value = bSigned ? ((int8_t)reading) : reading;
442
443 value *= ((double)mValue);
444 value += ((double)bValue) * std::pow(10.0, bExp);
445 value *= std::pow(10.0, rExp);
446
447 return value;
448}
449
Willy Tu38e7a2b2021-03-29 15:09:56 -0700450// Extract file name from sensor path as the sensors SDR ID. Simplify the name
451// if it is too long.
452std::string parseSdrIdFromPath(const std::string& path)
453{
454 std::string name;
455 size_t nameStart = path.rfind("/");
456 if (nameStart != std::string::npos)
457 {
458 name = path.substr(nameStart + 1, std::string::npos - nameStart);
459 }
460
Willy Tu38e7a2b2021-03-29 15:09:56 -0700461 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
462 {
463 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800464 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700465 {
JeffLind950f412021-10-20 18:49:34 +0800466 if (boost::ends_with(name, suffix))
467 {
468 boost::replace_all(name, suffix, "");
469 break;
470 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700471 }
Duke Du97014f52021-12-16 17:21:01 +0800472 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
473 {
474 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
475 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700476 }
JeffLind950f412021-10-20 18:49:34 +0800477 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700478 return name;
479}
480
Hao Jiangd48c9212021-02-03 15:45:06 -0800481bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
482 const std::string& path,
483 const ipmi::DbusInterfaceMap::mapped_type& object,
484 std::bitset<16>& assertions)
485{
486 auto profiles = sensor::getSupportedVrProfiles(object);
487 if (!profiles)
488 {
489 return false;
490 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700491 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800492
493 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700494 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800495 if (ec)
496 {
497 log<level::ERR>("Failed to get property",
498 entry("PROPERTY=%s", "Selected"),
499 entry("PATH=%s", path.c_str()),
500 entry("INTERFACE=%s", sensor::sensorInterface),
501 entry("WHAT=%s", ec.message().c_str()));
502 return false;
503 }
504
Willy Tu8366f0b2022-04-29 05:00:17 -0700505 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800506 if (itr == profiles->end())
507 {
508 using namespace phosphor::logging;
509 log<level::ERR>("VR mode doesn't match any of its profiles",
510 entry("PATH=%s", path.c_str()));
511 return false;
512 }
513 std::size_t index =
514 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
515
Willy Tubef102a2022-06-09 15:36:09 -0700516 // map index to response event assertion bit.
517 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800518 {
Willy Tubef102a2022-06-09 15:36:09 -0700519 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800520 }
521 else
522 {
523 log<level::ERR>("VR profile index reaches max assertion bit",
524 entry("PATH=%s", path.c_str()),
525 entry("INDEX=%uz", index));
526 return false;
527 }
528 if constexpr (debug)
529 {
530 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700531 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800532 }
533 return true;
534}
Hao Jiangd2afd052020-12-10 15:09:32 -0800535} // namespace sensor
536
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000537ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
538 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800539{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000540 constexpr const uint8_t validEnvmRev = 0x04;
541 constexpr const uint8_t lastSensorType = 0x2C;
542 constexpr const uint8_t oemReserved = 0xC0;
543
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700544 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000545 uint8_t evmRev = 0;
546 uint8_t sensorType = 0;
547 uint8_t sensorNum = 0;
548 uint8_t eventType = 0;
549 uint8_t eventData1 = 0;
550 std::optional<uint8_t> eventData2 = 0;
551 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700552 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000553 ipmi::ChannelInfo chInfo;
554
555 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
556 {
557 phosphor::logging::log<phosphor::logging::level::ERR>(
558 "Failed to get Channel Info",
559 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
560 return ipmi::responseUnspecifiedError();
561 }
562
563 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
564 ipmi::EChannelMediumType::systemInterface)
565 {
566
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700567 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000568 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700569 // Refer to IPMI Spec Table 32: SEL Event Records
570 generatorID = (ctx->channel << 12) // Channel
571 | (0x0 << 10) // Reserved
572 | (0x0 << 8) // 0x0 for sys-soft ID
573 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000574 }
575 else
576 {
577
578 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
579 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700580 // Refer to IPMI Spec Table 32: SEL Event Records
581 generatorID = (ctx->channel << 12) // Channel
582 | (0x0 << 10) // Reserved
583 | ((ctx->lun & 0x3) << 8) // Lun
584 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000585 }
586
587 if (!p.fullyUnpacked())
588 {
589 return ipmi::responseReqDataLenInvalid();
590 }
591
592 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
593 if (evmRev != validEnvmRev)
594 {
595 return ipmi::responseInvalidFieldRequest();
596 }
597 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
598 {
599 return ipmi::responseInvalidFieldRequest();
600 }
601
Willy Tude54f482021-01-26 15:59:09 -0800602 return ipmi::responseSuccess();
603}
604
Willy Tudbafbce2021-03-29 00:37:05 -0700605ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800606 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700607 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800608 bool, uint15_t, bool, uint8_t, uint8_t,
609 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700610{
611 std::string connection;
612 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700613 std::vector<std::string> interfaces;
614
615 ipmi::Cc status =
616 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700617 if (status)
618 {
619 return ipmi::response(status);
620 }
621
Hao Jiangd2afd052020-12-10 15:09:32 -0800622 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700623 if (std::find(interfaces.begin(), interfaces.end(),
624 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700625 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700626 DbusInterfaceMap sensorMap;
627 if (!getSensorMap(ctx, connection, path, sensorMap))
628 {
629 return ipmi::responseResponseError();
630 }
631 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800632 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700633 {
634 return ipmi::responseResponseError();
635 }
636
Jie Yangf0a89942021-07-29 15:30:25 -0700637 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800638 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
639 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700640 {
641 return ipmi::responseResponseError();
642 }
643
Hao Jiangd2afd052020-12-10 15:09:32 -0800644 auto value =
645 sensor::calculateValue(reading, sensorMap, sensorObject->second);
646 if (!value)
647 {
648 return ipmi::responseResponseError();
649 }
650
651 if constexpr (debug)
652 {
653 phosphor::logging::log<phosphor::logging::level::INFO>(
654 "IPMI SET_SENSOR",
655 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
656 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
657 phosphor::logging::entry("VALUE=%f", *value));
658 }
659
660 boost::system::error_code ec =
661 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
662 "Value", ipmi::Value(*value));
663
664 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500665 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800666 // callback functions for now (e.g. ipmiSetSensorReading).
667 if (ec)
668 {
669 using namespace phosphor::logging;
670 log<level::ERR>("Failed to set property",
671 entry("PROPERTY=%s", "Value"),
672 entry("PATH=%s", path.c_str()),
673 entry("INTERFACE=%s", sensor::sensorInterface),
674 entry("WHAT=%s", ec.message().c_str()));
675 return ipmi::responseResponseError();
676 }
677 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700678 }
679
Hao Jiange39d4d82021-04-16 17:02:40 -0700680 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
681 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700682 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700683 DbusInterfaceMap sensorMap;
684 if (!getSensorMap(ctx, connection, path, sensorMap))
685 {
686 return ipmi::responseResponseError();
687 }
688 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800689 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700690 {
691 return ipmi::responseResponseError();
692 }
693
Hao Jiangd2afd052020-12-10 15:09:32 -0800694 // VR sensors are treated as a special case and we will not check the
695 // write permission for VR sensors, since they always deemed writable
696 // and permission table are not applied to VR sensors.
697 auto vrMode =
698 sensor::calculateVRMode(assertOffset, sensorObject->second);
699 if (!vrMode)
700 {
701 return ipmi::responseResponseError();
702 }
703 boost::system::error_code ec = setDbusProperty(
704 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
705 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500706 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800707 // callback functions for now (e.g. ipmiSetSensorReading).
708 if (ec)
709 {
710 using namespace phosphor::logging;
711 log<level::ERR>("Failed to set property",
712 entry("PROPERTY=%s", "Selected"),
713 entry("PATH=%s", path.c_str()),
714 entry("INTERFACE=%s", sensor::sensorInterface),
715 entry("WHAT=%s", ec.message().c_str()));
716 return ipmi::responseResponseError();
717 }
718 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700719 }
720
Hao Jiangd2afd052020-12-10 15:09:32 -0800721 phosphor::logging::log<phosphor::logging::level::ERR>(
722 "unknown sensor type",
723 phosphor::logging::entry("PATH=%s", path.c_str()));
724 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700725}
726
Willy Tude54f482021-01-26 15:59:09 -0800727ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
728 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
729{
730 std::string connection;
731 std::string path;
732
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000733 if (sensnum == reservedSensorNumber)
734 {
735 return ipmi::responseInvalidFieldRequest();
736 }
737
Willy Tude54f482021-01-26 15:59:09 -0800738 auto status = getSensorConnection(ctx, sensnum, connection, path);
739 if (status)
740 {
741 return ipmi::response(status);
742 }
743
Scron Chang2703b022021-07-06 15:47:45 +0800744#ifdef FEATURE_HYBRID_SENSORS
745 if (auto sensor = findStaticSensor(path);
746 sensor != ipmi::sensor::sensors.end() &&
747 getSensorEventTypeFromPath(path) !=
748 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
749 {
750 if (ipmi::sensor::Mutability::Read !=
751 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
752 {
753 return ipmi::responseIllegalCommand();
754 }
755
756 uint8_t operation;
757 try
758 {
759 ipmi::sensor::GetSensorResponse getResponse =
760 sensor->second.getFunc(sensor->second);
761
762 if (getResponse.readingOrStateUnavailable)
763 {
764 operation |= static_cast<uint8_t>(
765 IPMISensorReadingByte2::readingStateUnavailable);
766 }
767 if (getResponse.scanningEnabled)
768 {
769 operation |= static_cast<uint8_t>(
770 IPMISensorReadingByte2::sensorScanningEnable);
771 }
772 if (getResponse.allEventMessagesEnabled)
773 {
774 operation |= static_cast<uint8_t>(
775 IPMISensorReadingByte2::eventMessagesEnable);
776 }
777 return ipmi::responseSuccess(
778 getResponse.reading, operation,
779 getResponse.thresholdLevelsStates,
780 getResponse.discreteReadingSensorStates);
781 }
782 catch (const std::exception& e)
783 {
784 operation |= static_cast<uint8_t>(
785 IPMISensorReadingByte2::readingStateUnavailable);
786 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
787 }
788 }
789#endif
790
Willy Tude54f482021-01-26 15:59:09 -0800791 DbusInterfaceMap sensorMap;
792 if (!getSensorMap(ctx, connection, path, sensorMap))
793 {
794 return ipmi::responseResponseError();
795 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800796 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800797
798 if (sensorObject == sensorMap.end() ||
799 sensorObject->second.find("Value") == sensorObject->second.end())
800 {
801 return ipmi::responseResponseError();
802 }
803 auto& valueVariant = sensorObject->second["Value"];
804 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
805
806 double max = 0;
807 double min = 0;
808 getSensorMaxMin(sensorMap, max, min);
809
810 int16_t mValue = 0;
811 int16_t bValue = 0;
812 int8_t rExp = 0;
813 int8_t bExp = 0;
814 bool bSigned = false;
815
816 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
817 {
818 return ipmi::responseResponseError();
819 }
820
821 uint8_t value =
822 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
823 uint8_t operation =
824 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
825 operation |=
826 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
827 bool notReading = std::isnan(reading);
828
829 if (!notReading)
830 {
831 auto availableObject =
832 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
833 if (availableObject != sensorMap.end())
834 {
835 auto findAvailable = availableObject->second.find("Available");
836 if (findAvailable != availableObject->second.end())
837 {
838 bool* available = std::get_if<bool>(&(findAvailable->second));
839 if (available && !(*available))
840 {
841 notReading = true;
842 }
843 }
844 }
845 }
846
847 if (notReading)
848 {
849 operation |= static_cast<uint8_t>(
850 IPMISensorReadingByte2::readingStateUnavailable);
851 }
852
Josh Lehana55c9532020-10-28 21:59:06 -0700853 if constexpr (details::enableInstrumentation)
854 {
855 int byteValue;
856 if (bSigned)
857 {
858 byteValue = static_cast<int>(static_cast<int8_t>(value));
859 }
860 else
861 {
862 byteValue = static_cast<int>(static_cast<uint8_t>(value));
863 }
864
865 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800866 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
867 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700868 {
869 // This is the first reading, show the coefficients
870 double step = (max - min) / 255.0;
871 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800872 << details::sdrStatsTable.getName((ctx->lun << 8) |
873 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700874 << ": Range min=" << min << " max=" << max
875 << ", step=" << step
876 << ", Coefficients mValue=" << static_cast<int>(mValue)
877 << " rExp=" << static_cast<int>(rExp)
878 << " bValue=" << static_cast<int>(bValue)
879 << " bExp=" << static_cast<int>(bExp)
880 << " bSigned=" << static_cast<int>(bSigned) << "\n";
881 }
882 }
883
Willy Tude54f482021-01-26 15:59:09 -0800884 uint8_t thresholds = 0;
885
886 auto warningObject =
887 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
888 if (warningObject != sensorMap.end())
889 {
890 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
891 auto alarmLow = warningObject->second.find("WarningAlarmLow");
892 if (alarmHigh != warningObject->second.end())
893 {
894 if (std::get<bool>(alarmHigh->second))
895 {
896 thresholds |= static_cast<uint8_t>(
897 IPMISensorReadingByte3::upperNonCritical);
898 }
899 }
900 if (alarmLow != warningObject->second.end())
901 {
902 if (std::get<bool>(alarmLow->second))
903 {
904 thresholds |= static_cast<uint8_t>(
905 IPMISensorReadingByte3::lowerNonCritical);
906 }
907 }
908 }
909
910 auto criticalObject =
911 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
912 if (criticalObject != sensorMap.end())
913 {
914 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
915 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
916 if (alarmHigh != criticalObject->second.end())
917 {
918 if (std::get<bool>(alarmHigh->second))
919 {
920 thresholds |=
921 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
922 }
923 }
924 if (alarmLow != criticalObject->second.end())
925 {
926 if (std::get<bool>(alarmLow->second))
927 {
928 thresholds |=
929 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
930 }
931 }
932 }
933
934 // no discrete as of today so optional byte is never returned
935 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
936}
937
938/** @brief implements the Set Sensor threshold command
939 * @param sensorNumber - sensor number
940 * @param lowerNonCriticalThreshMask
941 * @param lowerCriticalThreshMask
942 * @param lowerNonRecovThreshMask
943 * @param upperNonCriticalThreshMask
944 * @param upperCriticalThreshMask
945 * @param upperNonRecovThreshMask
946 * @param reserved
947 * @param lowerNonCritical - lower non-critical threshold
948 * @param lowerCritical - Lower critical threshold
949 * @param lowerNonRecoverable - Lower non recovarable threshold
950 * @param upperNonCritical - Upper non-critical threshold
951 * @param upperCritical - Upper critical
952 * @param upperNonRecoverable - Upper Non-recoverable
953 *
954 * @returns IPMI completion code
955 */
956ipmi::RspType<> ipmiSenSetSensorThresholds(
957 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
958 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
959 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
960 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800961 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800962 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800963 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800964{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000965 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800966 {
967 return ipmi::responseInvalidFieldRequest();
968 }
969
970 // lower nc and upper nc not suppported on any sensor
971 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
972 {
973 return ipmi::responseInvalidFieldRequest();
974 }
975
976 // if none of the threshold mask are set, nothing to do
977 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
978 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
979 upperCriticalThreshMask | upperNonRecovThreshMask))
980 {
981 return ipmi::responseSuccess();
982 }
983
984 std::string connection;
985 std::string path;
986
987 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
988 if (status)
989 {
990 return ipmi::response(status);
991 }
992 DbusInterfaceMap sensorMap;
993 if (!getSensorMap(ctx, connection, path, sensorMap))
994 {
995 return ipmi::responseResponseError();
996 }
997
998 double max = 0;
999 double min = 0;
1000 getSensorMaxMin(sensorMap, max, min);
1001
1002 int16_t mValue = 0;
1003 int16_t bValue = 0;
1004 int8_t rExp = 0;
1005 int8_t bExp = 0;
1006 bool bSigned = false;
1007
1008 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1009 {
1010 return ipmi::responseResponseError();
1011 }
1012
1013 // store a vector of property name, value to set, and interface
1014 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1015
1016 // define the indexes of the tuple
1017 constexpr uint8_t propertyName = 0;
1018 constexpr uint8_t thresholdValue = 1;
1019 constexpr uint8_t interface = 2;
1020 // verifiy all needed fields are present
1021 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1022 {
1023 auto findThreshold =
1024 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1025 if (findThreshold == sensorMap.end())
1026 {
1027 return ipmi::responseInvalidFieldRequest();
1028 }
1029 if (lowerCriticalThreshMask)
1030 {
1031 auto findLower = findThreshold->second.find("CriticalLow");
1032 if (findLower == findThreshold->second.end())
1033 {
1034 return ipmi::responseInvalidFieldRequest();
1035 }
1036 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1037 findThreshold->first);
1038 }
1039 if (upperCriticalThreshMask)
1040 {
1041 auto findUpper = findThreshold->second.find("CriticalHigh");
1042 if (findUpper == findThreshold->second.end())
1043 {
1044 return ipmi::responseInvalidFieldRequest();
1045 }
1046 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1047 findThreshold->first);
1048 }
1049 }
1050 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1051 {
1052 auto findThreshold =
1053 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1054 if (findThreshold == sensorMap.end())
1055 {
1056 return ipmi::responseInvalidFieldRequest();
1057 }
1058 if (lowerNonCriticalThreshMask)
1059 {
1060 auto findLower = findThreshold->second.find("WarningLow");
1061 if (findLower == findThreshold->second.end())
1062 {
1063 return ipmi::responseInvalidFieldRequest();
1064 }
1065 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1066 findThreshold->first);
1067 }
1068 if (upperNonCriticalThreshMask)
1069 {
1070 auto findUpper = findThreshold->second.find("WarningHigh");
1071 if (findUpper == findThreshold->second.end())
1072 {
1073 return ipmi::responseInvalidFieldRequest();
1074 }
1075 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1076 findThreshold->first);
1077 }
1078 }
1079 for (const auto& property : thresholdsToSet)
1080 {
1081 // from section 36.3 in the IPMI Spec, assume all linear
1082 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1083 (bValue * std::pow(10.0, bExp))) *
1084 std::pow(10.0, rExp);
1085 setDbusProperty(
1086 *getSdBus(), connection, path, std::get<interface>(property),
1087 std::get<propertyName>(property), ipmi::Value(valueToSet));
1088 }
1089 return ipmi::responseSuccess();
1090}
1091
1092IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1093{
1094 IPMIThresholds resp;
1095 auto warningInterface =
1096 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1097 auto criticalInterface =
1098 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1099
1100 if ((warningInterface != sensorMap.end()) ||
1101 (criticalInterface != sensorMap.end()))
1102 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001103 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001104
1105 if (sensorPair == sensorMap.end())
1106 {
1107 // should not have been able to find a sensor not implementing
1108 // the sensor object
1109 throw std::runtime_error("Invalid sensor map");
1110 }
1111
1112 double max = 0;
1113 double min = 0;
1114 getSensorMaxMin(sensorMap, max, min);
1115
1116 int16_t mValue = 0;
1117 int16_t bValue = 0;
1118 int8_t rExp = 0;
1119 int8_t bExp = 0;
1120 bool bSigned = false;
1121
1122 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1123 {
1124 throw std::runtime_error("Invalid sensor atrributes");
1125 }
1126 if (warningInterface != sensorMap.end())
1127 {
1128 auto& warningMap = warningInterface->second;
1129
1130 auto warningHigh = warningMap.find("WarningHigh");
1131 auto warningLow = warningMap.find("WarningLow");
1132
1133 if (warningHigh != warningMap.end())
1134 {
1135
1136 double value =
1137 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001138 if (std::isfinite(value))
1139 {
1140 resp.warningHigh = scaleIPMIValueFromDouble(
1141 value, mValue, rExp, bValue, bExp, bSigned);
1142 }
Willy Tude54f482021-01-26 15:59:09 -08001143 }
1144 if (warningLow != warningMap.end())
1145 {
1146 double value =
1147 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001148 if (std::isfinite(value))
1149 {
1150 resp.warningLow = scaleIPMIValueFromDouble(
1151 value, mValue, rExp, bValue, bExp, bSigned);
1152 }
Willy Tude54f482021-01-26 15:59:09 -08001153 }
1154 }
1155 if (criticalInterface != sensorMap.end())
1156 {
1157 auto& criticalMap = criticalInterface->second;
1158
1159 auto criticalHigh = criticalMap.find("CriticalHigh");
1160 auto criticalLow = criticalMap.find("CriticalLow");
1161
1162 if (criticalHigh != criticalMap.end())
1163 {
1164 double value =
1165 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001166 if (std::isfinite(value))
1167 {
1168 resp.criticalHigh = scaleIPMIValueFromDouble(
1169 value, mValue, rExp, bValue, bExp, bSigned);
1170 }
Willy Tude54f482021-01-26 15:59:09 -08001171 }
1172 if (criticalLow != criticalMap.end())
1173 {
1174 double value =
1175 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001176 if (std::isfinite(value))
1177 {
1178 resp.criticalLow = scaleIPMIValueFromDouble(
1179 value, mValue, rExp, bValue, bExp, bSigned);
1180 }
Willy Tude54f482021-01-26 15:59:09 -08001181 }
1182 }
1183 }
1184 return resp;
1185}
1186
1187ipmi::RspType<uint8_t, // readable
1188 uint8_t, // lowerNCrit
1189 uint8_t, // lowerCrit
1190 uint8_t, // lowerNrecoverable
1191 uint8_t, // upperNC
1192 uint8_t, // upperCrit
1193 uint8_t> // upperNRecoverable
1194 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1195{
1196 std::string connection;
1197 std::string path;
1198
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001199 if (sensorNumber == reservedSensorNumber)
1200 {
1201 return ipmi::responseInvalidFieldRequest();
1202 }
1203
Willy Tude54f482021-01-26 15:59:09 -08001204 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1205 if (status)
1206 {
1207 return ipmi::response(status);
1208 }
1209
1210 DbusInterfaceMap sensorMap;
1211 if (!getSensorMap(ctx, connection, path, sensorMap))
1212 {
1213 return ipmi::responseResponseError();
1214 }
1215
1216 IPMIThresholds thresholdData;
1217 try
1218 {
1219 thresholdData = getIPMIThresholds(sensorMap);
1220 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001221 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001222 {
1223 return ipmi::responseResponseError();
1224 }
1225
1226 uint8_t readable = 0;
1227 uint8_t lowerNC = 0;
1228 uint8_t lowerCritical = 0;
1229 uint8_t lowerNonRecoverable = 0;
1230 uint8_t upperNC = 0;
1231 uint8_t upperCritical = 0;
1232 uint8_t upperNonRecoverable = 0;
1233
1234 if (thresholdData.warningHigh)
1235 {
1236 readable |=
1237 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1238 upperNC = *thresholdData.warningHigh;
1239 }
1240 if (thresholdData.warningLow)
1241 {
1242 readable |=
1243 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1244 lowerNC = *thresholdData.warningLow;
1245 }
1246
1247 if (thresholdData.criticalHigh)
1248 {
1249 readable |=
1250 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1251 upperCritical = *thresholdData.criticalHigh;
1252 }
1253 if (thresholdData.criticalLow)
1254 {
1255 readable |=
1256 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1257 lowerCritical = *thresholdData.criticalLow;
1258 }
1259
1260 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1261 lowerNonRecoverable, upperNC, upperCritical,
1262 upperNonRecoverable);
1263}
1264
1265/** @brief implements the get Sensor event enable command
1266 * @param sensorNumber - sensor number
1267 *
1268 * @returns IPMI completion code plus response data
1269 * - enabled - Sensor Event messages
1270 * - assertionEnabledLsb - Assertion event messages
1271 * - assertionEnabledMsb - Assertion event messages
1272 * - deassertionEnabledLsb - Deassertion event messages
1273 * - deassertionEnabledMsb - Deassertion event messages
1274 */
1275
1276ipmi::RspType<uint8_t, // enabled
1277 uint8_t, // assertionEnabledLsb
1278 uint8_t, // assertionEnabledMsb
1279 uint8_t, // deassertionEnabledLsb
1280 uint8_t> // deassertionEnabledMsb
1281 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1282{
1283 std::string connection;
1284 std::string path;
1285
1286 uint8_t enabled = 0;
1287 uint8_t assertionEnabledLsb = 0;
1288 uint8_t assertionEnabledMsb = 0;
1289 uint8_t deassertionEnabledLsb = 0;
1290 uint8_t deassertionEnabledMsb = 0;
1291
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001292 if (sensorNum == reservedSensorNumber)
1293 {
1294 return ipmi::responseInvalidFieldRequest();
1295 }
1296
Willy Tude54f482021-01-26 15:59:09 -08001297 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1298 if (status)
1299 {
1300 return ipmi::response(status);
1301 }
1302
Scron Chang2703b022021-07-06 15:47:45 +08001303#ifdef FEATURE_HYBRID_SENSORS
1304 if (auto sensor = findStaticSensor(path);
1305 sensor != ipmi::sensor::sensors.end() &&
1306 getSensorEventTypeFromPath(path) !=
1307 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1308 {
1309 enabled = static_cast<uint8_t>(
1310 IPMISensorEventEnableByte2::sensorScanningEnable);
1311 uint16_t assertionEnabled = 0;
1312 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1313 ->second.begin()
1314 ->second.second)
1315 {
1316 assertionEnabled |= (1 << offsetValMap.first);
1317 }
1318 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1319 assertionEnabledMsb =
1320 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1321
1322 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1323 assertionEnabledMsb, deassertionEnabledLsb,
1324 deassertionEnabledMsb);
1325 }
1326#endif
1327
Willy Tude54f482021-01-26 15:59:09 -08001328 DbusInterfaceMap sensorMap;
1329 if (!getSensorMap(ctx, connection, path, sensorMap))
1330 {
1331 return ipmi::responseResponseError();
1332 }
1333
1334 auto warningInterface =
1335 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1336 auto criticalInterface =
1337 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1338 if ((warningInterface != sensorMap.end()) ||
1339 (criticalInterface != sensorMap.end()))
1340 {
1341 enabled = static_cast<uint8_t>(
1342 IPMISensorEventEnableByte2::sensorScanningEnable);
1343 if (warningInterface != sensorMap.end())
1344 {
1345 auto& warningMap = warningInterface->second;
1346
1347 auto warningHigh = warningMap.find("WarningHigh");
1348 auto warningLow = warningMap.find("WarningLow");
1349 if (warningHigh != warningMap.end())
1350 {
1351 assertionEnabledLsb |= static_cast<uint8_t>(
1352 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1353 deassertionEnabledLsb |= static_cast<uint8_t>(
1354 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1355 }
1356 if (warningLow != warningMap.end())
1357 {
1358 assertionEnabledLsb |= static_cast<uint8_t>(
1359 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1360 deassertionEnabledLsb |= static_cast<uint8_t>(
1361 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1362 }
1363 }
1364 if (criticalInterface != sensorMap.end())
1365 {
1366 auto& criticalMap = criticalInterface->second;
1367
1368 auto criticalHigh = criticalMap.find("CriticalHigh");
1369 auto criticalLow = criticalMap.find("CriticalLow");
1370
1371 if (criticalHigh != criticalMap.end())
1372 {
1373 assertionEnabledMsb |= static_cast<uint8_t>(
1374 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1375 deassertionEnabledMsb |= static_cast<uint8_t>(
1376 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1377 }
1378 if (criticalLow != criticalMap.end())
1379 {
1380 assertionEnabledLsb |= static_cast<uint8_t>(
1381 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1382 deassertionEnabledLsb |= static_cast<uint8_t>(
1383 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1384 }
1385 }
1386 }
1387
1388 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1389 assertionEnabledMsb, deassertionEnabledLsb,
1390 deassertionEnabledMsb);
1391}
1392
1393/** @brief implements the get Sensor event status command
1394 * @param sensorNumber - sensor number, FFh = reserved
1395 *
1396 * @returns IPMI completion code plus response data
1397 * - sensorEventStatus - Sensor Event messages state
1398 * - assertions - Assertion event messages
1399 * - deassertions - Deassertion event messages
1400 */
1401ipmi::RspType<uint8_t, // sensorEventStatus
1402 std::bitset<16>, // assertions
1403 std::bitset<16> // deassertion
1404 >
1405 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1406{
1407 if (sensorNum == reservedSensorNumber)
1408 {
1409 return ipmi::responseInvalidFieldRequest();
1410 }
1411
1412 std::string connection;
1413 std::string path;
1414 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1415 if (status)
1416 {
1417 phosphor::logging::log<phosphor::logging::level::ERR>(
1418 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1419 phosphor::logging::entry("SENSOR=%d", sensorNum));
1420 return ipmi::response(status);
1421 }
1422
Scron Chang2703b022021-07-06 15:47:45 +08001423#ifdef FEATURE_HYBRID_SENSORS
1424 if (auto sensor = findStaticSensor(path);
1425 sensor != ipmi::sensor::sensors.end() &&
1426 getSensorEventTypeFromPath(path) !=
1427 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1428 {
1429 auto response = ipmi::sensor::get::mapDbusToAssertion(
1430 sensor->second, path, sensor->second.sensorInterface);
1431 std::bitset<16> assertions;
1432 // deassertions are not used.
1433 std::bitset<16> deassertions = 0;
1434 uint8_t sensorEventStatus;
1435 if (response.readingOrStateUnavailable)
1436 {
1437 sensorEventStatus |= static_cast<uint8_t>(
1438 IPMISensorReadingByte2::readingStateUnavailable);
1439 }
1440 if (response.scanningEnabled)
1441 {
1442 sensorEventStatus |= static_cast<uint8_t>(
1443 IPMISensorReadingByte2::sensorScanningEnable);
1444 }
1445 if (response.allEventMessagesEnabled)
1446 {
1447 sensorEventStatus |= static_cast<uint8_t>(
1448 IPMISensorReadingByte2::eventMessagesEnable);
1449 }
1450 assertions |= response.discreteReadingSensorStates << 8;
1451 assertions |= response.thresholdLevelsStates;
1452 return ipmi::responseSuccess(sensorEventStatus, assertions,
1453 deassertions);
1454 }
1455#endif
1456
Willy Tude54f482021-01-26 15:59:09 -08001457 DbusInterfaceMap sensorMap;
1458 if (!getSensorMap(ctx, connection, path, sensorMap))
1459 {
1460 phosphor::logging::log<phosphor::logging::level::ERR>(
1461 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1462 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1463 return ipmi::responseResponseError();
1464 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001465
1466 uint8_t sensorEventStatus =
1467 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1468 std::bitset<16> assertions = 0;
1469 std::bitset<16> deassertions = 0;
1470
1471 // handle VR typed sensor
1472 auto vrInterface = sensorMap.find(sensor::vrInterface);
1473 if (vrInterface != sensorMap.end())
1474 {
1475 if (!sensor::getVrEventStatus(ctx, connection, path,
1476 vrInterface->second, assertions))
1477 {
1478 return ipmi::responseResponseError();
1479 }
1480
1481 // both Event Message and Sensor Scanning are disable for VR.
1482 sensorEventStatus = 0;
1483 return ipmi::responseSuccess(sensorEventStatus, assertions,
1484 deassertions);
1485 }
1486
Willy Tude54f482021-01-26 15:59:09 -08001487 auto warningInterface =
1488 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1489 auto criticalInterface =
1490 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1491
Willy Tude54f482021-01-26 15:59:09 -08001492 std::optional<bool> criticalDeassertHigh =
1493 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1494 std::optional<bool> criticalDeassertLow =
1495 thresholdDeassertMap[path]["CriticalAlarmLow"];
1496 std::optional<bool> warningDeassertHigh =
1497 thresholdDeassertMap[path]["WarningAlarmHigh"];
1498 std::optional<bool> warningDeassertLow =
1499 thresholdDeassertMap[path]["WarningAlarmLow"];
1500
Willy Tude54f482021-01-26 15:59:09 -08001501 if (criticalDeassertHigh && !*criticalDeassertHigh)
1502 {
1503 deassertions.set(static_cast<size_t>(
1504 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1505 }
1506 if (criticalDeassertLow && !*criticalDeassertLow)
1507 {
1508 deassertions.set(static_cast<size_t>(
1509 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1510 }
1511 if (warningDeassertHigh && !*warningDeassertHigh)
1512 {
1513 deassertions.set(static_cast<size_t>(
1514 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1515 }
1516 if (warningDeassertLow && !*warningDeassertLow)
1517 {
1518 deassertions.set(static_cast<size_t>(
1519 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1520 }
1521 if ((warningInterface != sensorMap.end()) ||
1522 (criticalInterface != sensorMap.end()))
1523 {
1524 sensorEventStatus = static_cast<size_t>(
1525 IPMISensorEventEnableByte2::eventMessagesEnable);
1526 if (warningInterface != sensorMap.end())
1527 {
1528 auto& warningMap = warningInterface->second;
1529
1530 auto warningHigh = warningMap.find("WarningAlarmHigh");
1531 auto warningLow = warningMap.find("WarningAlarmLow");
1532 auto warningHighAlarm = false;
1533 auto warningLowAlarm = false;
1534
1535 if (warningHigh != warningMap.end())
1536 {
1537 warningHighAlarm = std::get<bool>(warningHigh->second);
1538 }
1539 if (warningLow != warningMap.end())
1540 {
1541 warningLowAlarm = std::get<bool>(warningLow->second);
1542 }
1543 if (warningHighAlarm)
1544 {
1545 assertions.set(
1546 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1547 upperNonCriticalGoingHigh));
1548 }
1549 if (warningLowAlarm)
1550 {
1551 assertions.set(
1552 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1553 lowerNonCriticalGoingLow));
1554 }
1555 }
1556 if (criticalInterface != sensorMap.end())
1557 {
1558 auto& criticalMap = criticalInterface->second;
1559
1560 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1561 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1562 auto criticalHighAlarm = false;
1563 auto criticalLowAlarm = false;
1564
1565 if (criticalHigh != criticalMap.end())
1566 {
1567 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1568 }
1569 if (criticalLow != criticalMap.end())
1570 {
1571 criticalLowAlarm = std::get<bool>(criticalLow->second);
1572 }
1573 if (criticalHighAlarm)
1574 {
1575 assertions.set(
1576 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1577 upperCriticalGoingHigh));
1578 }
1579 if (criticalLowAlarm)
1580 {
1581 assertions.set(static_cast<size_t>(
1582 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1583 }
1584 }
1585 }
1586
1587 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1588}
1589
Willy Tu38e7a2b2021-03-29 15:09:56 -07001590// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001591void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1592 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001593{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001594 get_sdr::header::set_record_id(
1595 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1596
Willy Tu38e7a2b2021-03-29 15:09:56 -07001597 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1598 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1599
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001600 record.header.sdr_version = ipmiSdrVersion;
1601 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1602 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1603 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001604 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001605 record.key.owner_lun = lun;
1606 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001607}
Willy Tu4eca2512022-06-20 21:14:51 -07001608bool constructSensorSdr(
1609 ipmi::Context::ptr ctx,
1610 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1611 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1612 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001613{
Hao Jiange39d4d82021-04-16 17:02:40 -07001614 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1615
1616 DbusInterfaceMap sensorMap;
1617 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1618 {
1619 phosphor::logging::log<phosphor::logging::level::ERR>(
1620 "Failed to update sensor map for threshold sensor",
1621 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1622 phosphor::logging::entry("PATH=%s", path.c_str()));
1623 return false;
1624 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001625
1626 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1627 record.body.sensor_type = getSensorTypeFromPath(path);
1628 std::string type = getSensorTypeStringFromPath(path);
1629 auto typeCstr = type.c_str();
1630 auto findUnits = sensorUnits.find(typeCstr);
1631 if (findUnits != sensorUnits.end())
1632 {
1633 record.body.sensor_units_2_base =
1634 static_cast<uint8_t>(findUnits->second);
1635 } // else default 0x0 unspecified
1636
1637 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1638
Hao Jiangd2afd052020-12-10 15:09:32 -08001639 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001640 if (sensorObject == sensorMap.end())
1641 {
1642 phosphor::logging::log<phosphor::logging::level::ERR>(
1643 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001644 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001645 }
1646
1647 uint8_t entityId = 0;
1648 uint8_t entityInstance = 0x01;
1649
1650 // follow the association chain to get the parent board's entityid and
1651 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001652 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1653 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001654
1655 record.body.entity_id = entityId;
1656 record.body.entity_instance = entityInstance;
1657
Shakeeb Pasha93889722021-10-14 10:20:13 +05301658 double max = 0;
1659 double min = 0;
1660 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001661
1662 int16_t mValue = 0;
1663 int8_t rExp = 0;
1664 int16_t bValue = 0;
1665 int8_t bExp = 0;
1666 bool bSigned = false;
1667
1668 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1669 {
1670 phosphor::logging::log<phosphor::logging::level::ERR>(
1671 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001672 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001673 }
1674
1675 // The record.body is a struct SensorDataFullRecordBody
1676 // from sensorhandler.hpp in phosphor-ipmi-host.
1677 // The meaning of these bits appears to come from
1678 // table 43.1 of the IPMI spec.
1679 // The above 5 sensor attributes are stuffed in as follows:
1680 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1681 // Byte 22-24 are for other purposes
1682 // Byte 25 = MMMMMMMM = LSB of M
1683 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1684 // Byte 27 = BBBBBBBB = LSB of B
1685 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1686 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1687 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1688
1689 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1690 record.body.m_lsb = mValue & 0xFF;
1691
1692 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1693 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1694
1695 // move the smallest bit of the MSB into place (bit 9)
1696 // the MSbs are bits 7:8 in m_msb_and_tolerance
1697 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1698
1699 record.body.b_lsb = bValue & 0xFF;
1700
1701 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1702 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1703
1704 // move the smallest bit of the MSB into place (bit 9)
1705 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1706 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1707
1708 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1709 uint8_t rExpBits = rExp & 0x07;
1710
1711 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1712 uint8_t bExpBits = bExp & 0x07;
1713
1714 // move rExp and bExp into place
1715 record.body.r_b_exponents =
1716 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1717
1718 // Set the analog reading byte interpretation accordingly
1719 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1720
1721 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1722 // These seem redundant, but derivable from the above 5 attributes
1723 // Original comment said "todo fill out rest of units"
1724
1725 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001726 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001727 record.body.id_string_info = name.size();
1728 std::strncpy(record.body.id_string, name.c_str(),
1729 sizeof(record.body.id_string));
1730
Josh Lehana55c9532020-10-28 21:59:06 -07001731 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001732 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001733
Jie Yangf0a89942021-07-29 15:30:25 -07001734 bool sensorSettable = false;
1735 auto mutability =
1736 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1737 if (mutability != sensorMap.end())
1738 {
1739 sensorSettable =
1740 mappedVariant<bool>(mutability->second, "Mutable", false);
1741 }
1742 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1743
1744 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001745 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001746
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001747 IPMIThresholds thresholdData;
1748 try
1749 {
1750 thresholdData = getIPMIThresholds(sensorMap);
1751 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001752 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001753 {
1754 phosphor::logging::log<phosphor::logging::level::ERR>(
1755 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001756 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001757 }
1758
1759 if (thresholdData.criticalHigh)
1760 {
1761 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1762 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::criticalThreshold);
1764 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1765 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1766 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1767 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1768 record.body.discrete_reading_setting_mask[0] |=
1769 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1770 }
1771 if (thresholdData.warningHigh)
1772 {
1773 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1774 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1775 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1776 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1777 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1778 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1779 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1780 record.body.discrete_reading_setting_mask[0] |=
1781 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1782 }
1783 if (thresholdData.criticalLow)
1784 {
1785 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1786 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::criticalThreshold);
1788 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1789 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1790 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1791 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1792 record.body.discrete_reading_setting_mask[0] |=
1793 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1794 }
1795 if (thresholdData.warningLow)
1796 {
1797 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1798 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1799 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1800 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1801 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1802 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1803 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1804 record.body.discrete_reading_setting_mask[0] |=
1805 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1806 }
1807
1808 // everything that is readable is setable
1809 record.body.discrete_reading_setting_mask[1] =
1810 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001811 return true;
1812}
1813
Scron Chang2703b022021-07-06 15:47:45 +08001814#ifdef FEATURE_HYBRID_SENSORS
1815// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001816void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001817 uint16_t recordID,
1818 ipmi::sensor::IdInfoMap::const_iterator sensor,
1819 get_sdr::SensorDataFullRecord& record)
1820{
1821 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1822
1823 record.body.entity_id = sensor->second.entityType;
1824 record.body.sensor_type = sensor->second.sensorType;
1825 record.body.event_reading_type = sensor->second.sensorReadingType;
1826 record.body.entity_instance = sensor->second.instance;
1827 if (ipmi::sensor::Mutability::Write ==
1828 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1829 {
1830 get_sdr::body::init_settable_state(true, &(record.body));
1831 }
1832
1833 auto id_string = sensor->second.sensorName;
1834
1835 if (id_string.empty())
1836 {
1837 id_string = sensor->second.sensorNameFunc(sensor->second);
1838 }
1839
1840 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1841 {
1842 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1843 &(record.body));
1844 }
1845 else
1846 {
1847 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1848 }
1849 std::strncpy(record.body.id_string, id_string.c_str(),
1850 get_sdr::body::get_id_strlen(&(record.body)));
1851}
1852#endif
1853
Hao Jiange39d4d82021-04-16 17:02:40 -07001854// Construct type 3 SDR header and key (for VR and other discrete sensors)
1855void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1856 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001857{
1858 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1859 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1860
1861 get_sdr::header::set_record_id(
1862 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1863
1864 record.header.sdr_version = ipmiSdrVersion;
1865 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1866 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1867 sizeof(get_sdr::SensorDataRecordHeader);
1868 record.key.owner_id = bmcI2CAddr;
1869 record.key.owner_lun = lun;
1870 record.key.sensor_number = sensornumber;
1871
1872 record.body.entity_id = 0x00;
1873 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001874}
Willy Tu61992ad2021-03-29 15:33:20 -07001875
Hao Jiange39d4d82021-04-16 17:02:40 -07001876// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001877bool constructVrSdr(ipmi::Context::ptr ctx,
1878 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1879 uint16_t sensorNum, uint16_t recordID,
1880 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001881 get_sdr::SensorDataEventRecord& record)
1882{
Hao Jiange39d4d82021-04-16 17:02:40 -07001883 constructEventSdrHeaderKey(sensorNum, recordID, record);
1884
1885 DbusInterfaceMap sensorMap;
1886 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1887 {
1888 phosphor::logging::log<phosphor::logging::level::ERR>(
1889 "Failed to update sensor map for VR sensor",
1890 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1891 phosphor::logging::entry("PATH=%s", path.c_str()));
1892 return false;
1893 }
Willy Tu61992ad2021-03-29 15:33:20 -07001894 // follow the association chain to get the parent board's entityid and
1895 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001896 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1897 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001898 record.body.entity_instance);
1899
1900 // Sensor type is hardcoded as a module/board type instead of parsing from
1901 // sensor path. This is because VR control is allocated in an independent
1902 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1903 // types.
1904 static constexpr const uint8_t module_board_type = 0x15;
1905 record.body.sensor_type = module_board_type;
1906 record.body.event_reading_type = 0x00;
1907
1908 record.body.sensor_record_sharing_1 = 0x00;
1909 record.body.sensor_record_sharing_2 = 0x00;
1910
1911 // populate sensor name from path
1912 auto name = sensor::parseSdrIdFromPath(path);
1913 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1914 record.body.id_string_info = nameSize;
1915 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1916 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1917
1918 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001919 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001920
1921 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001922}
1923
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001924static inline uint16_t getNumberOfSensors()
1925{
1926 return std::min(getSensorTree().size(), maxIPMISensors);
1927}
1928
Willy Tu4eca2512022-06-20 21:14:51 -07001929static int getSensorDataRecord(
1930 ipmi::Context::ptr ctx,
1931 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1932 std::vector<uint8_t>& recordData, uint16_t recordID,
1933 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001934{
1935 size_t fruCount = 0;
1936 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1937 if (ret != ipmi::ccSuccess)
1938 {
1939 phosphor::logging::log<phosphor::logging::level::ERR>(
1940 "getSensorDataRecord: getFruSdrCount error");
1941 return GENERAL_ERROR;
1942 }
1943
Harvey Wu05d17c02021-09-15 08:46:59 +08001944 const auto& entityRecords =
1945 ipmi::sensor::EntityInfoMapContainer::getContainer()
1946 ->getIpmiEntityRecords();
1947 size_t entityCount = entityRecords.size();
1948
1949 size_t lastRecord = getNumberOfSensors() + fruCount +
1950 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001951 if (recordID == lastRecordIndex)
1952 {
1953 recordID = lastRecord;
1954 }
1955 if (recordID > lastRecord)
1956 {
1957 phosphor::logging::log<phosphor::logging::level::ERR>(
1958 "getSensorDataRecord: recordID > lastRecord error");
1959 return GENERAL_ERROR;
1960 }
1961
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001962 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001963 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001964 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001965
Harvey Wu05d17c02021-09-15 08:46:59 +08001966 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1967 {
1968 // handle type 8 entity map records
1969 ipmi::sensor::EntityInfoMap::const_iterator entity =
1970 entityRecords.find(static_cast<uint8_t>(
1971 sdrIndex - fruCount - ipmi::storage::type12Count));
1972 if (entity == entityRecords.end())
1973 {
1974 return IPMI_CC_SENSOR_INVALID;
1975 }
1976 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1977 }
1978 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001979 {
1980 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001981 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001982 if (type12Index >= ipmi::storage::type12Count)
1983 {
1984 phosphor::logging::log<phosphor::logging::level::ERR>(
1985 "getSensorDataRecord: type12Index error");
1986 return GENERAL_ERROR;
1987 }
1988 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1989 }
1990 else
1991 {
1992 // handle fru records
1993 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001994 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001995 if (ret != IPMI_CC_OK)
1996 {
1997 return GENERAL_ERROR;
1998 }
1999 data.header.record_id_msb = recordID >> 8;
2000 data.header.record_id_lsb = recordID & 0xFF;
2001 recordData.insert(recordData.end(), (uint8_t*)&data,
2002 ((uint8_t*)&data) + sizeof(data));
2003 }
2004
2005 return 0;
2006 }
2007
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002008 // Perform a incremental scan of the SDR Record ID's and translate the
2009 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2010 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2011 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2012 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002013 std::string connection;
2014 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002015 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002016 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002017 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002018 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002019 // LUN 0 has one reserved sensor number. Compensate here by adding one
2020 // to the record ID
2021 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002022 ctx->lun = 1;
2023 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002024 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002025 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002026 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2027 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2028 // rules governing its use.
2029 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002030 ctx->lun = 3;
2031 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002032
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002033 auto status =
2034 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2035 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002036 if (status)
2037 {
2038 phosphor::logging::log<phosphor::logging::level::ERR>(
2039 "getSensorDataRecord: getSensorConnection error");
2040 return GENERAL_ERROR;
2041 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002042 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002043 // Return an error on LUN 2 assingments, and any sensor number beyond the
2044 // range of LUN 3
2045 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2046 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002047 {
2048 phosphor::logging::log<phosphor::logging::level::ERR>(
2049 "getSensorDataRecord: invalidSensorNumber");
2050 return GENERAL_ERROR;
2051 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002052 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2053 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2054
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002055 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2056 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002057 {
2058 phosphor::logging::log<phosphor::logging::level::ERR>(
2059 "getSensorDataRecord: sensor record mismatch");
2060 return GENERAL_ERROR;
2061 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002062
Willy Tu38e7a2b2021-03-29 15:09:56 -07002063 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002064 if (std::find(interfaces.begin(), interfaces.end(),
2065 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002066 {
Willy Tu11d68892022-01-20 10:37:34 -08002067 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002068
Hao Jiange39d4d82021-04-16 17:02:40 -07002069 // If the request doesn't read SDR body, construct only header and key
2070 // part to avoid additional DBus transaction.
2071 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2072 {
2073 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2074 }
Willy Tu4eca2512022-06-20 21:14:51 -07002075 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2076 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002077 {
2078 return GENERAL_ERROR;
2079 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002080
Willy Tu38e7a2b2021-03-29 15:09:56 -07002081 recordData.insert(recordData.end(), (uint8_t*)&record,
2082 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002083
2084 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002085 }
Willy Tu61992ad2021-03-29 15:33:20 -07002086
Scron Chang2703b022021-07-06 15:47:45 +08002087#ifdef FEATURE_HYBRID_SENSORS
2088 if (auto sensor = findStaticSensor(path);
2089 sensor != ipmi::sensor::sensors.end() &&
2090 getSensorEventTypeFromPath(path) !=
2091 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2092 {
Willy Tu11d68892022-01-20 10:37:34 -08002093 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002094
2095 // If the request doesn't read SDR body, construct only header and key
2096 // part to avoid additional DBus transaction.
2097 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2098 {
2099 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2100 }
2101 else
2102 {
2103 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2104 }
2105
2106 recordData.insert(recordData.end(), (uint8_t*)&record,
2107 ((uint8_t*)&record) + sizeof(record));
2108
2109 return 0;
2110 }
2111#endif
2112
Willy Tu61992ad2021-03-29 15:33:20 -07002113 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002114 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2115 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002116 {
Willy Tu11d68892022-01-20 10:37:34 -08002117 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002118
Hao Jiange39d4d82021-04-16 17:02:40 -07002119 // If the request doesn't read SDR body, construct only header and key
2120 // part to avoid additional DBus transaction.
2121 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2122 {
2123 constructEventSdrHeaderKey(sensorNum, recordID, record);
2124 }
Willy Tu4eca2512022-06-20 21:14:51 -07002125 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2126 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002127 {
2128 return GENERAL_ERROR;
2129 }
Willy Tu61992ad2021-03-29 15:33:20 -07002130 recordData.insert(recordData.end(), (uint8_t*)&record,
2131 ((uint8_t*)&record) + sizeof(record));
2132 }
2133
Willy Tude54f482021-01-26 15:59:09 -08002134 return 0;
2135}
2136
2137/** @brief implements the get SDR Info command
2138 * @param count - Operation
2139 *
2140 * @returns IPMI completion code plus response data
2141 * - sdrCount - sensor/SDR count
2142 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2143 */
2144static ipmi::RspType<uint8_t, // respcount
2145 uint8_t, // dynamic population flags
2146 uint32_t // last time a sensor was added
2147 >
2148 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2149 std::optional<uint8_t> count)
2150{
2151 auto& sensorTree = getSensorTree();
2152 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002153 uint16_t recordID = 0;
2154 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002155 // Sensors are dynamically allocated, and there is at least one LUN
2156 uint8_t lunsAndDynamicPopulation = 0x80;
2157 constexpr uint8_t getSdrCount = 0x01;
2158 constexpr uint8_t getSensorCount = 0x00;
2159
2160 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2161 {
2162 return ipmi::responseResponseError();
2163 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002164 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002165 if (count.value_or(0) == getSdrCount)
2166 {
Willy Tu4eca2512022-06-20 21:14:51 -07002167 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2168
Willy Tude54f482021-01-26 15:59:09 -08002169 // Count the number of Type 1 SDR entries assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002170 while (!getSensorDataRecord(
2171 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2172 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002173 {
2174 get_sdr::SensorDataRecordHeader* hdr =
2175 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002176 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002177 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2178 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002179 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002180 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002181 record.data());
2182 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002183 {
2184 sdrCount++;
2185 }
2186 }
2187 }
2188 }
2189 else if (count.value_or(0) == getSensorCount)
2190 {
2191 // Return the number of sensors attached to the LUN
2192 if ((ctx->lun == 0) && (numSensors > 0))
2193 {
2194 sdrCount =
2195 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2196 }
2197 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2198 {
2199 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2200 ? maxSensorsPerLUN
2201 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2202 }
2203 else if (ctx->lun == 3)
2204 {
2205 if (numSensors <= maxIPMISensors)
2206 {
2207 sdrCount =
2208 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2209 }
2210 else
2211 {
2212 // error
2213 throw std::out_of_range(
2214 "Maximum number of IPMI sensors exceeded.");
2215 }
2216 }
2217 }
2218 else
2219 {
2220 return ipmi::responseInvalidFieldRequest();
2221 }
2222
2223 // Get Sensor count. This returns the number of sensors
2224 if (numSensors > 0)
2225 {
2226 lunsAndDynamicPopulation |= 1;
2227 }
2228 if (numSensors > maxSensorsPerLUN)
2229 {
2230 lunsAndDynamicPopulation |= 2;
2231 }
2232 if (numSensors >= (maxSensorsPerLUN * 2))
2233 {
2234 lunsAndDynamicPopulation |= 8;
2235 }
2236 if (numSensors > maxIPMISensors)
2237 {
2238 // error
2239 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2240 }
2241
2242 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2243 sdrLastAdd);
2244}
2245
2246/* end sensor commands */
2247
2248/* storage commands */
2249
2250ipmi::RspType<uint8_t, // sdr version
2251 uint16_t, // record count
2252 uint16_t, // free space
2253 uint32_t, // most recent addition
2254 uint32_t, // most recent erase
2255 uint8_t // operationSupport
2256 >
2257 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2258{
2259 auto& sensorTree = getSensorTree();
2260 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002261 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002262 {
2263 return ipmi::responseResponseError();
2264 }
2265
2266 size_t fruCount = 0;
2267 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2268 if (ret != ipmi::ccSuccess)
2269 {
2270 return ipmi::response(ret);
2271 }
2272
2273 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002274 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002275
2276 uint8_t operationSupport = static_cast<uint8_t>(
2277 SdrRepositoryInfoOps::overflow); // write not supported
2278
2279 operationSupport |=
2280 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2281 operationSupport |= static_cast<uint8_t>(
2282 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2283 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2284 unspecifiedFreeSpace, sdrLastAdd,
2285 sdrLastRemove, operationSupport);
2286}
2287
2288/** @brief implements the get SDR allocation info command
2289 *
2290 * @returns IPMI completion code plus response data
2291 * - allocUnits - Number of possible allocation units
2292 * - allocUnitSize - Allocation unit size in bytes.
2293 * - allocUnitFree - Number of free allocation units
2294 * - allocUnitLargestFree - Largest free block in allocation units
2295 * - maxRecordSize - Maximum record size in allocation units.
2296 */
2297ipmi::RspType<uint16_t, // allocUnits
2298 uint16_t, // allocUnitSize
2299 uint16_t, // allocUnitFree
2300 uint16_t, // allocUnitLargestFree
2301 uint8_t // maxRecordSize
2302 >
2303 ipmiStorageGetSDRAllocationInfo()
2304{
2305 // 0000h unspecified number of alloc units
2306 constexpr uint16_t allocUnits = 0;
2307
2308 constexpr uint16_t allocUnitFree = 0;
2309 constexpr uint16_t allocUnitLargestFree = 0;
2310 // only allow one block at a time
2311 constexpr uint8_t maxRecordSize = 1;
2312
2313 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2314 allocUnitLargestFree, maxRecordSize);
2315}
2316
2317/** @brief implements the reserve SDR command
2318 * @returns IPMI completion code plus response data
2319 * - sdrReservationID
2320 */
2321ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2322{
2323 sdrReservationID++;
2324 if (sdrReservationID == 0)
2325 {
2326 sdrReservationID++;
2327 }
2328
2329 return ipmi::responseSuccess(sdrReservationID);
2330}
2331
2332ipmi::RspType<uint16_t, // next record ID
2333 std::vector<uint8_t> // payload
2334 >
2335 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2336 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2337{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002338 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002339 // reservation required for partial reads with non zero offset into
2340 // record
2341 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2342 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002343 phosphor::logging::log<phosphor::logging::level::ERR>(
2344 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002345 return ipmi::responseInvalidReservationId();
2346 }
Willy Tude54f482021-01-26 15:59:09 -08002347 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2348 if (ret != ipmi::ccSuccess)
2349 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002350 phosphor::logging::log<phosphor::logging::level::ERR>(
2351 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002352 return ipmi::response(ret);
2353 }
2354
Harvey Wu05d17c02021-09-15 08:46:59 +08002355 const auto& entityRecords =
2356 ipmi::sensor::EntityInfoMapContainer::getContainer()
2357 ->getIpmiEntityRecords();
2358 int entityCount = entityRecords.size();
2359
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002360 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002361 size_t lastRecord = getNumberOfSensors() + fruCount +
2362 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002363 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2364
2365 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002366 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002367 phosphor::logging::log<phosphor::logging::level::ERR>(
2368 "ipmiStorageGetSDR: getSensorSubtree error");
2369 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002370 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002371
Willy Tu4eca2512022-06-20 21:14:51 -07002372 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2373
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002374 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002375 if (getSensorDataRecord(
2376 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2377 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002378 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002379 phosphor::logging::log<phosphor::logging::level::ERR>(
2380 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002381 return ipmi::responseInvalidFieldRequest();
2382 }
Willy Tude54f482021-01-26 15:59:09 -08002383 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002384 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002385 if (!hdr)
2386 {
2387 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002388 "ipmiStorageGetSDR: record header is null");
2389 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002390 }
2391
2392 size_t sdrLength =
2393 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2394 if (sdrLength < (offset + bytesToRead))
2395 {
2396 bytesToRead = sdrLength - offset;
2397 }
2398
2399 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2400 if (!respStart)
2401 {
2402 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002403 "ipmiStorageGetSDR: record is null");
2404 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002405 }
2406
2407 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002408
Willy Tude54f482021-01-26 15:59:09 -08002409 return ipmi::responseSuccess(nextRecordId, recordData);
2410}
adarshgrami042e9db2022-09-15 10:34:34 +05302411namespace dcmi
2412{
2413
2414ipmi::RspType<uint8_t, // No of instances for requested id
2415 uint8_t, // No of record ids in the response
2416 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2417 // IDs
2418 >
2419 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
2420 uint8_t entityInstance, uint8_t instanceStart)
2421{
2422 auto match = ipmi::dcmi::validEntityId.find(entityId);
2423 if (match == ipmi::dcmi::validEntityId.end())
2424 {
2425 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2426
2427 return ipmi::responseInvalidFieldRequest();
2428 }
2429
2430 if (sensorType != ipmi::dcmi::temperatureSensorType)
2431 {
2432 log<level::ERR>("Invalid sensor type",
2433 entry("SENSOR_TYPE=%d", sensorType));
2434
2435 return ipmi::responseInvalidFieldRequest();
2436 }
2437 auto& sensorTree = getSensorTree();
2438 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2439 {
2440 return ipmi::responseUnspecifiedError();
2441 }
2442
2443 std::vector<uint16_t> sensorRec{};
2444 uint8_t numInstances = 0;
2445
2446 for (const auto& sensor : sensorTree)
2447 {
2448 auto sensorTypeValue = getSensorTypeFromPath(sensor.first);
2449 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2450 {
2451 continue;
2452 }
2453 const auto& connection = sensor.second.begin()->first;
2454
2455 DbusInterfaceMap sensorMap;
2456 if (!getSensorMap(ctx, connection, sensor.first, sensorMap,
2457 sensorMapSdrUpdatePeriod))
2458 {
2459 phosphor::logging::log<phosphor::logging::level::ERR>(
2460 "Failed to update sensor map for threshold sensor",
2461 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2462 phosphor::logging::entry("PATH=%s", sensor.first.c_str()));
2463 continue;
2464 }
2465 uint8_t entityIdValue = 0;
2466 uint8_t entityInstanceValue = 0;
2467
2468 updateIpmiFromAssociation(sensor.first, sensorMap, entityIdValue,
2469 entityInstanceValue);
2470 if (!entityInstance)
2471 {
2472 if (entityIdValue == match->first || entityIdValue == match->second)
2473 {
2474 auto recordId = getSensorNumberFromPath(sensor.first);
2475 if (recordId != invalidSensorNumber)
2476 {
2477 numInstances++;
2478 if (numInstances <= ipmi::dcmi::maxRecords)
2479 {
2480 sensorRec.push_back(recordId);
2481 }
2482 }
2483 }
2484 }
2485 else
2486 {
2487 if (entityIdValue == match->first || entityIdValue == match->second)
2488 {
2489 if (entityInstance == entityInstanceValue)
2490 {
2491 auto recordId = getSensorNumberFromPath(sensor.first);
2492 if ((recordId != invalidSensorNumber) && sensorRec.empty())
2493 {
2494 sensorRec.push_back(recordId);
2495 }
2496 }
2497 numInstances++;
2498 }
2499 }
2500 }
2501 if (sensorRec.empty())
2502 {
2503 return ipmi::responseSensorInvalid();
2504 }
2505 uint8_t numRecords = sensorRec.size();
2506 return ipmi::responseSuccess(numInstances, numRecords, sensorRec);
2507}
2508} // namespace dcmi
2509
Willy Tude54f482021-01-26 15:59:09 -08002510/* end storage commands */
2511
2512void registerSensorFunctions()
2513{
2514 // <Platform Event>
2515 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2516 ipmi::sensor_event::cmdPlatformEvent,
2517 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2518
Willy Tudbafbce2021-03-29 00:37:05 -07002519 // <Set Sensor Reading and Event Status>
2520 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2521 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2522 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002523
Willy Tude54f482021-01-26 15:59:09 -08002524 // <Get Sensor Reading>
2525 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2526 ipmi::sensor_event::cmdGetSensorReading,
2527 ipmi::Privilege::User, ipmiSenGetSensorReading);
2528
2529 // <Get Sensor Threshold>
2530 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2531 ipmi::sensor_event::cmdGetSensorThreshold,
2532 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2533
2534 // <Set Sensor Threshold>
2535 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2536 ipmi::sensor_event::cmdSetSensorThreshold,
2537 ipmi::Privilege::Operator,
2538 ipmiSenSetSensorThresholds);
2539
2540 // <Get Sensor Event Enable>
2541 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2542 ipmi::sensor_event::cmdGetSensorEventEnable,
2543 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2544
2545 // <Get Sensor Event Status>
2546 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2547 ipmi::sensor_event::cmdGetSensorEventStatus,
2548 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2549
2550 // register all storage commands for both Sensor and Storage command
2551 // versions
2552
2553 // <Get SDR Repository Info>
2554 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2555 ipmi::storage::cmdGetSdrRepositoryInfo,
2556 ipmi::Privilege::User,
2557 ipmiStorageGetSDRRepositoryInfo);
2558
2559 // <Get Device SDR Info>
2560 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2561 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2562 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2563
2564 // <Get SDR Allocation Info>
2565 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2566 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2567 ipmi::Privilege::User,
2568 ipmiStorageGetSDRAllocationInfo);
2569
2570 // <Reserve SDR Repo>
2571 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2572 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2573 ipmi::Privilege::User, ipmiStorageReserveSDR);
2574
2575 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2576 ipmi::storage::cmdReserveSdrRepository,
2577 ipmi::Privilege::User, ipmiStorageReserveSDR);
2578
2579 // <Get Sdr>
2580 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2581 ipmi::sensor_event::cmdGetDeviceSdr,
2582 ipmi::Privilege::User, ipmiStorageGetSDR);
2583
2584 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2585 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2586 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302587 // <Get DCMI Sensor Info>
2588 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2589 ipmi::dcmi::cmdGetDcmiSensorInfo,
2590 ipmi::Privilege::User,
2591 ipmi::dcmi::getSensorInfo);
Willy Tude54f482021-01-26 15:59:09 -08002592}
2593} // namespace ipmi