blob: 4753a7bc52bc4cf2fb5090c9293464d6353f1376 [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;
312 boost::system::error_code ec = getManagedObjects(
313 ctx, sensorConnection.c_str(), "/", managedObjects);
314 if (ec)
315 {
316 phosphor::logging::log<phosphor::logging::level::ERR>(
317 "GetMangagedObjects for getSensorMap failed",
318 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
319
320 return false;
321 }
322
323 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700324 // Update time after finish building the map which allow the
325 // data to be cached for updatePeriod plus the build time.
326 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800327 }
328 auto connection = SensorCache.find(sensorConnection);
329 if (connection == SensorCache.end())
330 {
331 return false;
332 }
333 auto path = connection->second.find(sensorPath);
334 if (path == connection->second.end())
335 {
336 return false;
337 }
338 sensorMap = path->second;
339
340 return true;
341}
342
Hao Jiangd2afd052020-12-10 15:09:32 -0800343namespace sensor
344{
Hao Jiangd48c9212021-02-03 15:45:06 -0800345// Read VR profiles from sensor(daemon) interface
346static std::optional<std::vector<std::string>>
347 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800348{
349 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800350 auto supportedProperty = object.find("Supported");
351 if (supportedProperty == object.end() ||
352 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800353 {
354 phosphor::logging::log<phosphor::logging::level::ERR>(
355 "Missing the required Supported and Selected properties");
356 return std::nullopt;
357 }
358
359 const auto profilesPtr =
360 std::get_if<std::vector<std::string>>(&supportedProperty->second);
361
362 if (profilesPtr == nullptr)
363 {
364 phosphor::logging::log<phosphor::logging::level::ERR>(
365 "property is not array of string");
366 return std::nullopt;
367 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800368 return *profilesPtr;
369}
370
371// Calculate VR Mode from input IPMI discrete event bytes
372static std::optional<std::string>
373 calculateVRMode(uint15_t assertOffset,
374 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
375{
376 // get VR mode profiles from Supported Interface
377 auto profiles = getSupportedVrProfiles(VRObject);
378 if (!profiles)
379 {
380 return std::nullopt;
381 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800382
383 // interpret IPMI cmd bits into profiles' index
384 long unsigned int index = 0;
385 // only one bit should be set and the highest bit should not be used.
386 if (assertOffset == 0 || assertOffset == (1u << 15) ||
387 (assertOffset & (assertOffset - 1)))
388 {
389 phosphor::logging::log<phosphor::logging::level::ERR>(
390 "IPMI cmd format incorrect",
391
392 phosphor::logging::entry("BYTES=%#02x",
393 static_cast<uint16_t>(assertOffset)));
394 return std::nullopt;
395 }
396
397 while (assertOffset != 1)
398 {
399 assertOffset >>= 1;
400 index++;
401 }
402
Hao Jiangd48c9212021-02-03 15:45:06 -0800403 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800404 {
405 phosphor::logging::log<phosphor::logging::level::ERR>(
406 "profile index out of boundary");
407 return std::nullopt;
408 }
409
Hao Jiangd48c9212021-02-03 15:45:06 -0800410 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800411}
412
413// Calculate sensor value from IPMI reading byte
414static std::optional<double>
415 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
416 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
417{
418 if (valueObject.find("Value") == valueObject.end())
419 {
420 phosphor::logging::log<phosphor::logging::level::ERR>(
421 "Missing the required Value property");
422 return std::nullopt;
423 }
424
425 double max = 0;
426 double min = 0;
427 getSensorMaxMin(sensorMap, max, min);
428
429 int16_t mValue = 0;
430 int16_t bValue = 0;
431 int8_t rExp = 0;
432 int8_t bExp = 0;
433 bool bSigned = false;
434
435 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
436 {
437 return std::nullopt;
438 }
439
440 double value = bSigned ? ((int8_t)reading) : reading;
441
442 value *= ((double)mValue);
443 value += ((double)bValue) * std::pow(10.0, bExp);
444 value *= std::pow(10.0, rExp);
445
446 return value;
447}
448
Willy Tu38e7a2b2021-03-29 15:09:56 -0700449// Extract file name from sensor path as the sensors SDR ID. Simplify the name
450// if it is too long.
451std::string parseSdrIdFromPath(const std::string& path)
452{
453 std::string name;
454 size_t nameStart = path.rfind("/");
455 if (nameStart != std::string::npos)
456 {
457 name = path.substr(nameStart + 1, std::string::npos - nameStart);
458 }
459
Willy Tu38e7a2b2021-03-29 15:09:56 -0700460 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
461 {
462 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800463 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700464 {
JeffLind950f412021-10-20 18:49:34 +0800465 if (boost::ends_with(name, suffix))
466 {
467 boost::replace_all(name, suffix, "");
468 break;
469 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700470 }
Duke Du97014f52021-12-16 17:21:01 +0800471 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
472 {
473 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
474 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700475 }
JeffLind950f412021-10-20 18:49:34 +0800476 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700477 return name;
478}
479
Hao Jiangd48c9212021-02-03 15:45:06 -0800480bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
481 const std::string& path,
482 const ipmi::DbusInterfaceMap::mapped_type& object,
483 std::bitset<16>& assertions)
484{
485 auto profiles = sensor::getSupportedVrProfiles(object);
486 if (!profiles)
487 {
488 return false;
489 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700490 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800491
492 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700493 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800494 if (ec)
495 {
496 log<level::ERR>("Failed to get property",
497 entry("PROPERTY=%s", "Selected"),
498 entry("PATH=%s", path.c_str()),
499 entry("INTERFACE=%s", sensor::sensorInterface),
500 entry("WHAT=%s", ec.message().c_str()));
501 return false;
502 }
503
Willy Tu8366f0b2022-04-29 05:00:17 -0700504 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800505 if (itr == profiles->end())
506 {
507 using namespace phosphor::logging;
508 log<level::ERR>("VR mode doesn't match any of its profiles",
509 entry("PATH=%s", path.c_str()));
510 return false;
511 }
512 std::size_t index =
513 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
514
Willy Tubef102a2022-06-09 15:36:09 -0700515 // map index to response event assertion bit.
516 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800517 {
Willy Tubef102a2022-06-09 15:36:09 -0700518 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800519 }
520 else
521 {
522 log<level::ERR>("VR profile index reaches max assertion bit",
523 entry("PATH=%s", path.c_str()),
524 entry("INDEX=%uz", index));
525 return false;
526 }
527 if constexpr (debug)
528 {
529 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700530 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800531 }
532 return true;
533}
Hao Jiangd2afd052020-12-10 15:09:32 -0800534} // namespace sensor
535
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000536ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
537 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800538{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000539 constexpr const uint8_t validEnvmRev = 0x04;
540 constexpr const uint8_t lastSensorType = 0x2C;
541 constexpr const uint8_t oemReserved = 0xC0;
542
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700543 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000544 uint8_t evmRev = 0;
545 uint8_t sensorType = 0;
546 uint8_t sensorNum = 0;
547 uint8_t eventType = 0;
548 uint8_t eventData1 = 0;
549 std::optional<uint8_t> eventData2 = 0;
550 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700551 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000552 ipmi::ChannelInfo chInfo;
553
554 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
555 {
556 phosphor::logging::log<phosphor::logging::level::ERR>(
557 "Failed to get Channel Info",
558 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
559 return ipmi::responseUnspecifiedError();
560 }
561
562 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
563 ipmi::EChannelMediumType::systemInterface)
564 {
565
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700566 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000567 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700568 // Refer to IPMI Spec Table 32: SEL Event Records
569 generatorID = (ctx->channel << 12) // Channel
570 | (0x0 << 10) // Reserved
571 | (0x0 << 8) // 0x0 for sys-soft ID
572 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000573 }
574 else
575 {
576
577 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
578 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700579 // Refer to IPMI Spec Table 32: SEL Event Records
580 generatorID = (ctx->channel << 12) // Channel
581 | (0x0 << 10) // Reserved
582 | ((ctx->lun & 0x3) << 8) // Lun
583 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000584 }
585
586 if (!p.fullyUnpacked())
587 {
588 return ipmi::responseReqDataLenInvalid();
589 }
590
591 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
592 if (evmRev != validEnvmRev)
593 {
594 return ipmi::responseInvalidFieldRequest();
595 }
596 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
597 {
598 return ipmi::responseInvalidFieldRequest();
599 }
600
Willy Tude54f482021-01-26 15:59:09 -0800601 return ipmi::responseSuccess();
602}
603
Willy Tudbafbce2021-03-29 00:37:05 -0700604ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800605 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700606 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800607 bool, uint15_t, bool, uint8_t, uint8_t,
608 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700609{
610 std::string connection;
611 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700612 std::vector<std::string> interfaces;
613
614 ipmi::Cc status =
615 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700616 if (status)
617 {
618 return ipmi::response(status);
619 }
620
Hao Jiangd2afd052020-12-10 15:09:32 -0800621 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700622 if (std::find(interfaces.begin(), interfaces.end(),
623 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700624 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700625 DbusInterfaceMap sensorMap;
626 if (!getSensorMap(ctx, connection, path, sensorMap))
627 {
628 return ipmi::responseResponseError();
629 }
630 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800631 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700632 {
633 return ipmi::responseResponseError();
634 }
635
Jie Yangf0a89942021-07-29 15:30:25 -0700636 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800637 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
638 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700639 {
640 return ipmi::responseResponseError();
641 }
642
Hao Jiangd2afd052020-12-10 15:09:32 -0800643 auto value =
644 sensor::calculateValue(reading, sensorMap, sensorObject->second);
645 if (!value)
646 {
647 return ipmi::responseResponseError();
648 }
649
650 if constexpr (debug)
651 {
652 phosphor::logging::log<phosphor::logging::level::INFO>(
653 "IPMI SET_SENSOR",
654 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
655 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
656 phosphor::logging::entry("VALUE=%f", *value));
657 }
658
659 boost::system::error_code ec =
660 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
661 "Value", ipmi::Value(*value));
662
663 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500664 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800665 // callback functions for now (e.g. ipmiSetSensorReading).
666 if (ec)
667 {
668 using namespace phosphor::logging;
669 log<level::ERR>("Failed to set property",
670 entry("PROPERTY=%s", "Value"),
671 entry("PATH=%s", path.c_str()),
672 entry("INTERFACE=%s", sensor::sensorInterface),
673 entry("WHAT=%s", ec.message().c_str()));
674 return ipmi::responseResponseError();
675 }
676 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700677 }
678
Hao Jiange39d4d82021-04-16 17:02:40 -0700679 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
680 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700681 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700682 DbusInterfaceMap sensorMap;
683 if (!getSensorMap(ctx, connection, path, sensorMap))
684 {
685 return ipmi::responseResponseError();
686 }
687 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800688 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700689 {
690 return ipmi::responseResponseError();
691 }
692
Hao Jiangd2afd052020-12-10 15:09:32 -0800693 // VR sensors are treated as a special case and we will not check the
694 // write permission for VR sensors, since they always deemed writable
695 // and permission table are not applied to VR sensors.
696 auto vrMode =
697 sensor::calculateVRMode(assertOffset, sensorObject->second);
698 if (!vrMode)
699 {
700 return ipmi::responseResponseError();
701 }
702 boost::system::error_code ec = setDbusProperty(
703 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
704 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500705 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800706 // callback functions for now (e.g. ipmiSetSensorReading).
707 if (ec)
708 {
709 using namespace phosphor::logging;
710 log<level::ERR>("Failed to set property",
711 entry("PROPERTY=%s", "Selected"),
712 entry("PATH=%s", path.c_str()),
713 entry("INTERFACE=%s", sensor::sensorInterface),
714 entry("WHAT=%s", ec.message().c_str()));
715 return ipmi::responseResponseError();
716 }
717 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700718 }
719
Hao Jiangd2afd052020-12-10 15:09:32 -0800720 phosphor::logging::log<phosphor::logging::level::ERR>(
721 "unknown sensor type",
722 phosphor::logging::entry("PATH=%s", path.c_str()));
723 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700724}
725
Willy Tude54f482021-01-26 15:59:09 -0800726ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
727 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
728{
729 std::string connection;
730 std::string path;
731
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000732 if (sensnum == reservedSensorNumber)
733 {
734 return ipmi::responseInvalidFieldRequest();
735 }
736
Willy Tude54f482021-01-26 15:59:09 -0800737 auto status = getSensorConnection(ctx, sensnum, connection, path);
738 if (status)
739 {
740 return ipmi::response(status);
741 }
742
Scron Chang2703b022021-07-06 15:47:45 +0800743#ifdef FEATURE_HYBRID_SENSORS
744 if (auto sensor = findStaticSensor(path);
745 sensor != ipmi::sensor::sensors.end() &&
746 getSensorEventTypeFromPath(path) !=
747 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
748 {
749 if (ipmi::sensor::Mutability::Read !=
750 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
751 {
752 return ipmi::responseIllegalCommand();
753 }
754
755 uint8_t operation;
756 try
757 {
758 ipmi::sensor::GetSensorResponse getResponse =
759 sensor->second.getFunc(sensor->second);
760
761 if (getResponse.readingOrStateUnavailable)
762 {
763 operation |= static_cast<uint8_t>(
764 IPMISensorReadingByte2::readingStateUnavailable);
765 }
766 if (getResponse.scanningEnabled)
767 {
768 operation |= static_cast<uint8_t>(
769 IPMISensorReadingByte2::sensorScanningEnable);
770 }
771 if (getResponse.allEventMessagesEnabled)
772 {
773 operation |= static_cast<uint8_t>(
774 IPMISensorReadingByte2::eventMessagesEnable);
775 }
776 return ipmi::responseSuccess(
777 getResponse.reading, operation,
778 getResponse.thresholdLevelsStates,
779 getResponse.discreteReadingSensorStates);
780 }
781 catch (const std::exception& e)
782 {
783 operation |= static_cast<uint8_t>(
784 IPMISensorReadingByte2::readingStateUnavailable);
785 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
786 }
787 }
788#endif
789
Willy Tude54f482021-01-26 15:59:09 -0800790 DbusInterfaceMap sensorMap;
791 if (!getSensorMap(ctx, connection, path, sensorMap))
792 {
793 return ipmi::responseResponseError();
794 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800795 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800796
797 if (sensorObject == sensorMap.end() ||
798 sensorObject->second.find("Value") == sensorObject->second.end())
799 {
800 return ipmi::responseResponseError();
801 }
802 auto& valueVariant = sensorObject->second["Value"];
803 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
804
805 double max = 0;
806 double min = 0;
807 getSensorMaxMin(sensorMap, max, min);
808
809 int16_t mValue = 0;
810 int16_t bValue = 0;
811 int8_t rExp = 0;
812 int8_t bExp = 0;
813 bool bSigned = false;
814
815 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
816 {
817 return ipmi::responseResponseError();
818 }
819
820 uint8_t value =
821 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
822 uint8_t operation =
823 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
824 operation |=
825 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
826 bool notReading = std::isnan(reading);
827
828 if (!notReading)
829 {
830 auto availableObject =
831 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
832 if (availableObject != sensorMap.end())
833 {
834 auto findAvailable = availableObject->second.find("Available");
835 if (findAvailable != availableObject->second.end())
836 {
837 bool* available = std::get_if<bool>(&(findAvailable->second));
838 if (available && !(*available))
839 {
840 notReading = true;
841 }
842 }
843 }
844 }
845
846 if (notReading)
847 {
848 operation |= static_cast<uint8_t>(
849 IPMISensorReadingByte2::readingStateUnavailable);
850 }
851
Josh Lehana55c9532020-10-28 21:59:06 -0700852 if constexpr (details::enableInstrumentation)
853 {
854 int byteValue;
855 if (bSigned)
856 {
857 byteValue = static_cast<int>(static_cast<int8_t>(value));
858 }
859 else
860 {
861 byteValue = static_cast<int>(static_cast<uint8_t>(value));
862 }
863
864 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800865 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
866 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700867 {
868 // This is the first reading, show the coefficients
869 double step = (max - min) / 255.0;
870 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800871 << details::sdrStatsTable.getName((ctx->lun << 8) |
872 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700873 << ": Range min=" << min << " max=" << max
874 << ", step=" << step
875 << ", Coefficients mValue=" << static_cast<int>(mValue)
876 << " rExp=" << static_cast<int>(rExp)
877 << " bValue=" << static_cast<int>(bValue)
878 << " bExp=" << static_cast<int>(bExp)
879 << " bSigned=" << static_cast<int>(bSigned) << "\n";
880 }
881 }
882
Willy Tude54f482021-01-26 15:59:09 -0800883 uint8_t thresholds = 0;
884
885 auto warningObject =
886 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
887 if (warningObject != sensorMap.end())
888 {
889 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
890 auto alarmLow = warningObject->second.find("WarningAlarmLow");
891 if (alarmHigh != warningObject->second.end())
892 {
893 if (std::get<bool>(alarmHigh->second))
894 {
895 thresholds |= static_cast<uint8_t>(
896 IPMISensorReadingByte3::upperNonCritical);
897 }
898 }
899 if (alarmLow != warningObject->second.end())
900 {
901 if (std::get<bool>(alarmLow->second))
902 {
903 thresholds |= static_cast<uint8_t>(
904 IPMISensorReadingByte3::lowerNonCritical);
905 }
906 }
907 }
908
909 auto criticalObject =
910 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
911 if (criticalObject != sensorMap.end())
912 {
913 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
914 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
915 if (alarmHigh != criticalObject->second.end())
916 {
917 if (std::get<bool>(alarmHigh->second))
918 {
919 thresholds |=
920 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
921 }
922 }
923 if (alarmLow != criticalObject->second.end())
924 {
925 if (std::get<bool>(alarmLow->second))
926 {
927 thresholds |=
928 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
929 }
930 }
931 }
932
933 // no discrete as of today so optional byte is never returned
934 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
935}
936
937/** @brief implements the Set Sensor threshold command
938 * @param sensorNumber - sensor number
939 * @param lowerNonCriticalThreshMask
940 * @param lowerCriticalThreshMask
941 * @param lowerNonRecovThreshMask
942 * @param upperNonCriticalThreshMask
943 * @param upperCriticalThreshMask
944 * @param upperNonRecovThreshMask
945 * @param reserved
946 * @param lowerNonCritical - lower non-critical threshold
947 * @param lowerCritical - Lower critical threshold
948 * @param lowerNonRecoverable - Lower non recovarable threshold
949 * @param upperNonCritical - Upper non-critical threshold
950 * @param upperCritical - Upper critical
951 * @param upperNonRecoverable - Upper Non-recoverable
952 *
953 * @returns IPMI completion code
954 */
955ipmi::RspType<> ipmiSenSetSensorThresholds(
956 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
957 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
958 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
959 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800960 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800961 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800962 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800963{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000964 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800965 {
966 return ipmi::responseInvalidFieldRequest();
967 }
968
969 // lower nc and upper nc not suppported on any sensor
970 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
971 {
972 return ipmi::responseInvalidFieldRequest();
973 }
974
975 // if none of the threshold mask are set, nothing to do
976 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
977 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
978 upperCriticalThreshMask | upperNonRecovThreshMask))
979 {
980 return ipmi::responseSuccess();
981 }
982
983 std::string connection;
984 std::string path;
985
986 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
987 if (status)
988 {
989 return ipmi::response(status);
990 }
991 DbusInterfaceMap sensorMap;
992 if (!getSensorMap(ctx, connection, path, sensorMap))
993 {
994 return ipmi::responseResponseError();
995 }
996
997 double max = 0;
998 double min = 0;
999 getSensorMaxMin(sensorMap, max, min);
1000
1001 int16_t mValue = 0;
1002 int16_t bValue = 0;
1003 int8_t rExp = 0;
1004 int8_t bExp = 0;
1005 bool bSigned = false;
1006
1007 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1008 {
1009 return ipmi::responseResponseError();
1010 }
1011
1012 // store a vector of property name, value to set, and interface
1013 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1014
1015 // define the indexes of the tuple
1016 constexpr uint8_t propertyName = 0;
1017 constexpr uint8_t thresholdValue = 1;
1018 constexpr uint8_t interface = 2;
1019 // verifiy all needed fields are present
1020 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1021 {
1022 auto findThreshold =
1023 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1024 if (findThreshold == sensorMap.end())
1025 {
1026 return ipmi::responseInvalidFieldRequest();
1027 }
1028 if (lowerCriticalThreshMask)
1029 {
1030 auto findLower = findThreshold->second.find("CriticalLow");
1031 if (findLower == findThreshold->second.end())
1032 {
1033 return ipmi::responseInvalidFieldRequest();
1034 }
1035 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1036 findThreshold->first);
1037 }
1038 if (upperCriticalThreshMask)
1039 {
1040 auto findUpper = findThreshold->second.find("CriticalHigh");
1041 if (findUpper == findThreshold->second.end())
1042 {
1043 return ipmi::responseInvalidFieldRequest();
1044 }
1045 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1046 findThreshold->first);
1047 }
1048 }
1049 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1050 {
1051 auto findThreshold =
1052 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1053 if (findThreshold == sensorMap.end())
1054 {
1055 return ipmi::responseInvalidFieldRequest();
1056 }
1057 if (lowerNonCriticalThreshMask)
1058 {
1059 auto findLower = findThreshold->second.find("WarningLow");
1060 if (findLower == findThreshold->second.end())
1061 {
1062 return ipmi::responseInvalidFieldRequest();
1063 }
1064 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1065 findThreshold->first);
1066 }
1067 if (upperNonCriticalThreshMask)
1068 {
1069 auto findUpper = findThreshold->second.find("WarningHigh");
1070 if (findUpper == findThreshold->second.end())
1071 {
1072 return ipmi::responseInvalidFieldRequest();
1073 }
1074 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1075 findThreshold->first);
1076 }
1077 }
1078 for (const auto& property : thresholdsToSet)
1079 {
1080 // from section 36.3 in the IPMI Spec, assume all linear
1081 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1082 (bValue * std::pow(10.0, bExp))) *
1083 std::pow(10.0, rExp);
1084 setDbusProperty(
1085 *getSdBus(), connection, path, std::get<interface>(property),
1086 std::get<propertyName>(property), ipmi::Value(valueToSet));
1087 }
1088 return ipmi::responseSuccess();
1089}
1090
1091IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1092{
1093 IPMIThresholds resp;
1094 auto warningInterface =
1095 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1096 auto criticalInterface =
1097 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1098
1099 if ((warningInterface != sensorMap.end()) ||
1100 (criticalInterface != sensorMap.end()))
1101 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001102 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001103
1104 if (sensorPair == sensorMap.end())
1105 {
1106 // should not have been able to find a sensor not implementing
1107 // the sensor object
1108 throw std::runtime_error("Invalid sensor map");
1109 }
1110
1111 double max = 0;
1112 double min = 0;
1113 getSensorMaxMin(sensorMap, max, min);
1114
1115 int16_t mValue = 0;
1116 int16_t bValue = 0;
1117 int8_t rExp = 0;
1118 int8_t bExp = 0;
1119 bool bSigned = false;
1120
1121 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1122 {
1123 throw std::runtime_error("Invalid sensor atrributes");
1124 }
1125 if (warningInterface != sensorMap.end())
1126 {
1127 auto& warningMap = warningInterface->second;
1128
1129 auto warningHigh = warningMap.find("WarningHigh");
1130 auto warningLow = warningMap.find("WarningLow");
1131
1132 if (warningHigh != warningMap.end())
1133 {
1134
1135 double value =
1136 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001137 if (std::isfinite(value))
1138 {
1139 resp.warningHigh = scaleIPMIValueFromDouble(
1140 value, mValue, rExp, bValue, bExp, bSigned);
1141 }
Willy Tude54f482021-01-26 15:59:09 -08001142 }
1143 if (warningLow != warningMap.end())
1144 {
1145 double value =
1146 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001147 if (std::isfinite(value))
1148 {
1149 resp.warningLow = scaleIPMIValueFromDouble(
1150 value, mValue, rExp, bValue, bExp, bSigned);
1151 }
Willy Tude54f482021-01-26 15:59:09 -08001152 }
1153 }
1154 if (criticalInterface != sensorMap.end())
1155 {
1156 auto& criticalMap = criticalInterface->second;
1157
1158 auto criticalHigh = criticalMap.find("CriticalHigh");
1159 auto criticalLow = criticalMap.find("CriticalLow");
1160
1161 if (criticalHigh != criticalMap.end())
1162 {
1163 double value =
1164 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001165 if (std::isfinite(value))
1166 {
1167 resp.criticalHigh = scaleIPMIValueFromDouble(
1168 value, mValue, rExp, bValue, bExp, bSigned);
1169 }
Willy Tude54f482021-01-26 15:59:09 -08001170 }
1171 if (criticalLow != criticalMap.end())
1172 {
1173 double value =
1174 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001175 if (std::isfinite(value))
1176 {
1177 resp.criticalLow = scaleIPMIValueFromDouble(
1178 value, mValue, rExp, bValue, bExp, bSigned);
1179 }
Willy Tude54f482021-01-26 15:59:09 -08001180 }
1181 }
1182 }
1183 return resp;
1184}
1185
1186ipmi::RspType<uint8_t, // readable
1187 uint8_t, // lowerNCrit
1188 uint8_t, // lowerCrit
1189 uint8_t, // lowerNrecoverable
1190 uint8_t, // upperNC
1191 uint8_t, // upperCrit
1192 uint8_t> // upperNRecoverable
1193 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1194{
1195 std::string connection;
1196 std::string path;
1197
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001198 if (sensorNumber == reservedSensorNumber)
1199 {
1200 return ipmi::responseInvalidFieldRequest();
1201 }
1202
Willy Tude54f482021-01-26 15:59:09 -08001203 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1204 if (status)
1205 {
1206 return ipmi::response(status);
1207 }
1208
1209 DbusInterfaceMap sensorMap;
1210 if (!getSensorMap(ctx, connection, path, sensorMap))
1211 {
1212 return ipmi::responseResponseError();
1213 }
1214
1215 IPMIThresholds thresholdData;
1216 try
1217 {
1218 thresholdData = getIPMIThresholds(sensorMap);
1219 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001220 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001221 {
1222 return ipmi::responseResponseError();
1223 }
1224
1225 uint8_t readable = 0;
1226 uint8_t lowerNC = 0;
1227 uint8_t lowerCritical = 0;
1228 uint8_t lowerNonRecoverable = 0;
1229 uint8_t upperNC = 0;
1230 uint8_t upperCritical = 0;
1231 uint8_t upperNonRecoverable = 0;
1232
1233 if (thresholdData.warningHigh)
1234 {
1235 readable |=
1236 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1237 upperNC = *thresholdData.warningHigh;
1238 }
1239 if (thresholdData.warningLow)
1240 {
1241 readable |=
1242 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1243 lowerNC = *thresholdData.warningLow;
1244 }
1245
1246 if (thresholdData.criticalHigh)
1247 {
1248 readable |=
1249 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1250 upperCritical = *thresholdData.criticalHigh;
1251 }
1252 if (thresholdData.criticalLow)
1253 {
1254 readable |=
1255 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1256 lowerCritical = *thresholdData.criticalLow;
1257 }
1258
1259 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1260 lowerNonRecoverable, upperNC, upperCritical,
1261 upperNonRecoverable);
1262}
1263
1264/** @brief implements the get Sensor event enable command
1265 * @param sensorNumber - sensor number
1266 *
1267 * @returns IPMI completion code plus response data
1268 * - enabled - Sensor Event messages
1269 * - assertionEnabledLsb - Assertion event messages
1270 * - assertionEnabledMsb - Assertion event messages
1271 * - deassertionEnabledLsb - Deassertion event messages
1272 * - deassertionEnabledMsb - Deassertion event messages
1273 */
1274
1275ipmi::RspType<uint8_t, // enabled
1276 uint8_t, // assertionEnabledLsb
1277 uint8_t, // assertionEnabledMsb
1278 uint8_t, // deassertionEnabledLsb
1279 uint8_t> // deassertionEnabledMsb
1280 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1281{
1282 std::string connection;
1283 std::string path;
1284
1285 uint8_t enabled = 0;
1286 uint8_t assertionEnabledLsb = 0;
1287 uint8_t assertionEnabledMsb = 0;
1288 uint8_t deassertionEnabledLsb = 0;
1289 uint8_t deassertionEnabledMsb = 0;
1290
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001291 if (sensorNum == reservedSensorNumber)
1292 {
1293 return ipmi::responseInvalidFieldRequest();
1294 }
1295
Willy Tude54f482021-01-26 15:59:09 -08001296 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1297 if (status)
1298 {
1299 return ipmi::response(status);
1300 }
1301
Scron Chang2703b022021-07-06 15:47:45 +08001302#ifdef FEATURE_HYBRID_SENSORS
1303 if (auto sensor = findStaticSensor(path);
1304 sensor != ipmi::sensor::sensors.end() &&
1305 getSensorEventTypeFromPath(path) !=
1306 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1307 {
1308 enabled = static_cast<uint8_t>(
1309 IPMISensorEventEnableByte2::sensorScanningEnable);
1310 uint16_t assertionEnabled = 0;
1311 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1312 ->second.begin()
1313 ->second.second)
1314 {
1315 assertionEnabled |= (1 << offsetValMap.first);
1316 }
1317 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1318 assertionEnabledMsb =
1319 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1320
1321 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1322 assertionEnabledMsb, deassertionEnabledLsb,
1323 deassertionEnabledMsb);
1324 }
1325#endif
1326
Willy Tude54f482021-01-26 15:59:09 -08001327 DbusInterfaceMap sensorMap;
1328 if (!getSensorMap(ctx, connection, path, sensorMap))
1329 {
1330 return ipmi::responseResponseError();
1331 }
1332
1333 auto warningInterface =
1334 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1335 auto criticalInterface =
1336 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1337 if ((warningInterface != sensorMap.end()) ||
1338 (criticalInterface != sensorMap.end()))
1339 {
1340 enabled = static_cast<uint8_t>(
1341 IPMISensorEventEnableByte2::sensorScanningEnable);
1342 if (warningInterface != sensorMap.end())
1343 {
1344 auto& warningMap = warningInterface->second;
1345
1346 auto warningHigh = warningMap.find("WarningHigh");
1347 auto warningLow = warningMap.find("WarningLow");
1348 if (warningHigh != warningMap.end())
1349 {
1350 assertionEnabledLsb |= static_cast<uint8_t>(
1351 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1352 deassertionEnabledLsb |= static_cast<uint8_t>(
1353 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1354 }
1355 if (warningLow != warningMap.end())
1356 {
1357 assertionEnabledLsb |= static_cast<uint8_t>(
1358 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1359 deassertionEnabledLsb |= static_cast<uint8_t>(
1360 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1361 }
1362 }
1363 if (criticalInterface != sensorMap.end())
1364 {
1365 auto& criticalMap = criticalInterface->second;
1366
1367 auto criticalHigh = criticalMap.find("CriticalHigh");
1368 auto criticalLow = criticalMap.find("CriticalLow");
1369
1370 if (criticalHigh != criticalMap.end())
1371 {
1372 assertionEnabledMsb |= static_cast<uint8_t>(
1373 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1374 deassertionEnabledMsb |= static_cast<uint8_t>(
1375 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1376 }
1377 if (criticalLow != criticalMap.end())
1378 {
1379 assertionEnabledLsb |= static_cast<uint8_t>(
1380 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1381 deassertionEnabledLsb |= static_cast<uint8_t>(
1382 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1383 }
1384 }
1385 }
1386
1387 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1388 assertionEnabledMsb, deassertionEnabledLsb,
1389 deassertionEnabledMsb);
1390}
1391
1392/** @brief implements the get Sensor event status command
1393 * @param sensorNumber - sensor number, FFh = reserved
1394 *
1395 * @returns IPMI completion code plus response data
1396 * - sensorEventStatus - Sensor Event messages state
1397 * - assertions - Assertion event messages
1398 * - deassertions - Deassertion event messages
1399 */
1400ipmi::RspType<uint8_t, // sensorEventStatus
1401 std::bitset<16>, // assertions
1402 std::bitset<16> // deassertion
1403 >
1404 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1405{
1406 if (sensorNum == reservedSensorNumber)
1407 {
1408 return ipmi::responseInvalidFieldRequest();
1409 }
1410
1411 std::string connection;
1412 std::string path;
1413 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1414 if (status)
1415 {
1416 phosphor::logging::log<phosphor::logging::level::ERR>(
1417 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1418 phosphor::logging::entry("SENSOR=%d", sensorNum));
1419 return ipmi::response(status);
1420 }
1421
Scron Chang2703b022021-07-06 15:47:45 +08001422#ifdef FEATURE_HYBRID_SENSORS
1423 if (auto sensor = findStaticSensor(path);
1424 sensor != ipmi::sensor::sensors.end() &&
1425 getSensorEventTypeFromPath(path) !=
1426 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1427 {
1428 auto response = ipmi::sensor::get::mapDbusToAssertion(
1429 sensor->second, path, sensor->second.sensorInterface);
1430 std::bitset<16> assertions;
1431 // deassertions are not used.
1432 std::bitset<16> deassertions = 0;
1433 uint8_t sensorEventStatus;
1434 if (response.readingOrStateUnavailable)
1435 {
1436 sensorEventStatus |= static_cast<uint8_t>(
1437 IPMISensorReadingByte2::readingStateUnavailable);
1438 }
1439 if (response.scanningEnabled)
1440 {
1441 sensorEventStatus |= static_cast<uint8_t>(
1442 IPMISensorReadingByte2::sensorScanningEnable);
1443 }
1444 if (response.allEventMessagesEnabled)
1445 {
1446 sensorEventStatus |= static_cast<uint8_t>(
1447 IPMISensorReadingByte2::eventMessagesEnable);
1448 }
1449 assertions |= response.discreteReadingSensorStates << 8;
1450 assertions |= response.thresholdLevelsStates;
1451 return ipmi::responseSuccess(sensorEventStatus, assertions,
1452 deassertions);
1453 }
1454#endif
1455
Willy Tude54f482021-01-26 15:59:09 -08001456 DbusInterfaceMap sensorMap;
1457 if (!getSensorMap(ctx, connection, path, sensorMap))
1458 {
1459 phosphor::logging::log<phosphor::logging::level::ERR>(
1460 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1461 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1462 return ipmi::responseResponseError();
1463 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001464
1465 uint8_t sensorEventStatus =
1466 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1467 std::bitset<16> assertions = 0;
1468 std::bitset<16> deassertions = 0;
1469
1470 // handle VR typed sensor
1471 auto vrInterface = sensorMap.find(sensor::vrInterface);
1472 if (vrInterface != sensorMap.end())
1473 {
1474 if (!sensor::getVrEventStatus(ctx, connection, path,
1475 vrInterface->second, assertions))
1476 {
1477 return ipmi::responseResponseError();
1478 }
1479
1480 // both Event Message and Sensor Scanning are disable for VR.
1481 sensorEventStatus = 0;
1482 return ipmi::responseSuccess(sensorEventStatus, assertions,
1483 deassertions);
1484 }
1485
Willy Tude54f482021-01-26 15:59:09 -08001486 auto warningInterface =
1487 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1488 auto criticalInterface =
1489 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1490
Willy Tude54f482021-01-26 15:59:09 -08001491 std::optional<bool> criticalDeassertHigh =
1492 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1493 std::optional<bool> criticalDeassertLow =
1494 thresholdDeassertMap[path]["CriticalAlarmLow"];
1495 std::optional<bool> warningDeassertHigh =
1496 thresholdDeassertMap[path]["WarningAlarmHigh"];
1497 std::optional<bool> warningDeassertLow =
1498 thresholdDeassertMap[path]["WarningAlarmLow"];
1499
Willy Tude54f482021-01-26 15:59:09 -08001500 if (criticalDeassertHigh && !*criticalDeassertHigh)
1501 {
1502 deassertions.set(static_cast<size_t>(
1503 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1504 }
1505 if (criticalDeassertLow && !*criticalDeassertLow)
1506 {
1507 deassertions.set(static_cast<size_t>(
1508 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1509 }
1510 if (warningDeassertHigh && !*warningDeassertHigh)
1511 {
1512 deassertions.set(static_cast<size_t>(
1513 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1514 }
1515 if (warningDeassertLow && !*warningDeassertLow)
1516 {
1517 deassertions.set(static_cast<size_t>(
1518 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1519 }
1520 if ((warningInterface != sensorMap.end()) ||
1521 (criticalInterface != sensorMap.end()))
1522 {
1523 sensorEventStatus = static_cast<size_t>(
1524 IPMISensorEventEnableByte2::eventMessagesEnable);
1525 if (warningInterface != sensorMap.end())
1526 {
1527 auto& warningMap = warningInterface->second;
1528
1529 auto warningHigh = warningMap.find("WarningAlarmHigh");
1530 auto warningLow = warningMap.find("WarningAlarmLow");
1531 auto warningHighAlarm = false;
1532 auto warningLowAlarm = false;
1533
1534 if (warningHigh != warningMap.end())
1535 {
1536 warningHighAlarm = std::get<bool>(warningHigh->second);
1537 }
1538 if (warningLow != warningMap.end())
1539 {
1540 warningLowAlarm = std::get<bool>(warningLow->second);
1541 }
1542 if (warningHighAlarm)
1543 {
1544 assertions.set(
1545 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1546 upperNonCriticalGoingHigh));
1547 }
1548 if (warningLowAlarm)
1549 {
1550 assertions.set(
1551 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1552 lowerNonCriticalGoingLow));
1553 }
1554 }
1555 if (criticalInterface != sensorMap.end())
1556 {
1557 auto& criticalMap = criticalInterface->second;
1558
1559 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1560 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1561 auto criticalHighAlarm = false;
1562 auto criticalLowAlarm = false;
1563
1564 if (criticalHigh != criticalMap.end())
1565 {
1566 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1567 }
1568 if (criticalLow != criticalMap.end())
1569 {
1570 criticalLowAlarm = std::get<bool>(criticalLow->second);
1571 }
1572 if (criticalHighAlarm)
1573 {
1574 assertions.set(
1575 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1576 upperCriticalGoingHigh));
1577 }
1578 if (criticalLowAlarm)
1579 {
1580 assertions.set(static_cast<size_t>(
1581 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1582 }
1583 }
1584 }
1585
1586 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1587}
1588
Willy Tu38e7a2b2021-03-29 15:09:56 -07001589// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001590void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1591 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001592{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001593 get_sdr::header::set_record_id(
1594 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1595
Willy Tu38e7a2b2021-03-29 15:09:56 -07001596 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1597 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1598
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001599 record.header.sdr_version = ipmiSdrVersion;
1600 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1601 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1602 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001603 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001604 record.key.owner_lun = lun;
1605 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001606}
Willy Tu4eca2512022-06-20 21:14:51 -07001607bool constructSensorSdr(
1608 ipmi::Context::ptr ctx,
1609 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1610 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1611 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001612{
Hao Jiange39d4d82021-04-16 17:02:40 -07001613 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1614
1615 DbusInterfaceMap sensorMap;
1616 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1617 {
1618 phosphor::logging::log<phosphor::logging::level::ERR>(
1619 "Failed to update sensor map for threshold sensor",
1620 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1621 phosphor::logging::entry("PATH=%s", path.c_str()));
1622 return false;
1623 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001624
1625 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1626 record.body.sensor_type = getSensorTypeFromPath(path);
1627 std::string type = getSensorTypeStringFromPath(path);
1628 auto typeCstr = type.c_str();
1629 auto findUnits = sensorUnits.find(typeCstr);
1630 if (findUnits != sensorUnits.end())
1631 {
1632 record.body.sensor_units_2_base =
1633 static_cast<uint8_t>(findUnits->second);
1634 } // else default 0x0 unspecified
1635
1636 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1637
Hao Jiangd2afd052020-12-10 15:09:32 -08001638 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001639 if (sensorObject == sensorMap.end())
1640 {
1641 phosphor::logging::log<phosphor::logging::level::ERR>(
1642 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001643 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001644 }
1645
1646 uint8_t entityId = 0;
1647 uint8_t entityInstance = 0x01;
1648
1649 // follow the association chain to get the parent board's entityid and
1650 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001651 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1652 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001653
1654 record.body.entity_id = entityId;
1655 record.body.entity_instance = entityInstance;
1656
Shakeeb Pasha93889722021-10-14 10:20:13 +05301657 double max = 0;
1658 double min = 0;
1659 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001660
1661 int16_t mValue = 0;
1662 int8_t rExp = 0;
1663 int16_t bValue = 0;
1664 int8_t bExp = 0;
1665 bool bSigned = false;
1666
1667 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1668 {
1669 phosphor::logging::log<phosphor::logging::level::ERR>(
1670 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001671 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001672 }
1673
1674 // The record.body is a struct SensorDataFullRecordBody
1675 // from sensorhandler.hpp in phosphor-ipmi-host.
1676 // The meaning of these bits appears to come from
1677 // table 43.1 of the IPMI spec.
1678 // The above 5 sensor attributes are stuffed in as follows:
1679 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1680 // Byte 22-24 are for other purposes
1681 // Byte 25 = MMMMMMMM = LSB of M
1682 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1683 // Byte 27 = BBBBBBBB = LSB of B
1684 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1685 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1686 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1687
1688 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1689 record.body.m_lsb = mValue & 0xFF;
1690
1691 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1692 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1693
1694 // move the smallest bit of the MSB into place (bit 9)
1695 // the MSbs are bits 7:8 in m_msb_and_tolerance
1696 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1697
1698 record.body.b_lsb = bValue & 0xFF;
1699
1700 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1701 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1702
1703 // move the smallest bit of the MSB into place (bit 9)
1704 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1705 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1706
1707 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1708 uint8_t rExpBits = rExp & 0x07;
1709
1710 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1711 uint8_t bExpBits = bExp & 0x07;
1712
1713 // move rExp and bExp into place
1714 record.body.r_b_exponents =
1715 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1716
1717 // Set the analog reading byte interpretation accordingly
1718 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1719
1720 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1721 // These seem redundant, but derivable from the above 5 attributes
1722 // Original comment said "todo fill out rest of units"
1723
1724 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001725 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001726 record.body.id_string_info = name.size();
1727 std::strncpy(record.body.id_string, name.c_str(),
1728 sizeof(record.body.id_string));
1729
Josh Lehana55c9532020-10-28 21:59:06 -07001730 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001731 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001732
Jie Yangf0a89942021-07-29 15:30:25 -07001733 bool sensorSettable = false;
1734 auto mutability =
1735 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1736 if (mutability != sensorMap.end())
1737 {
1738 sensorSettable =
1739 mappedVariant<bool>(mutability->second, "Mutable", false);
1740 }
1741 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1742
1743 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001744 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001745
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001746 IPMIThresholds thresholdData;
1747 try
1748 {
1749 thresholdData = getIPMIThresholds(sensorMap);
1750 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001751 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001752 {
1753 phosphor::logging::log<phosphor::logging::level::ERR>(
1754 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001755 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001756 }
1757
1758 if (thresholdData.criticalHigh)
1759 {
1760 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1761 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1762 IPMISensorEventEnableThresholds::criticalThreshold);
1763 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1764 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1765 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1766 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1767 record.body.discrete_reading_setting_mask[0] |=
1768 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1769 }
1770 if (thresholdData.warningHigh)
1771 {
1772 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1773 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1774 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1775 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1776 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1777 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1778 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1779 record.body.discrete_reading_setting_mask[0] |=
1780 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1781 }
1782 if (thresholdData.criticalLow)
1783 {
1784 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1785 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1786 IPMISensorEventEnableThresholds::criticalThreshold);
1787 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1788 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1789 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1790 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1791 record.body.discrete_reading_setting_mask[0] |=
1792 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1793 }
1794 if (thresholdData.warningLow)
1795 {
1796 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1797 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1798 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1799 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1800 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1801 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1802 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1803 record.body.discrete_reading_setting_mask[0] |=
1804 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1805 }
1806
1807 // everything that is readable is setable
1808 record.body.discrete_reading_setting_mask[1] =
1809 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001810 return true;
1811}
1812
Scron Chang2703b022021-07-06 15:47:45 +08001813#ifdef FEATURE_HYBRID_SENSORS
1814// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001815void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001816 uint16_t recordID,
1817 ipmi::sensor::IdInfoMap::const_iterator sensor,
1818 get_sdr::SensorDataFullRecord& record)
1819{
1820 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1821
1822 record.body.entity_id = sensor->second.entityType;
1823 record.body.sensor_type = sensor->second.sensorType;
1824 record.body.event_reading_type = sensor->second.sensorReadingType;
1825 record.body.entity_instance = sensor->second.instance;
1826 if (ipmi::sensor::Mutability::Write ==
1827 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1828 {
1829 get_sdr::body::init_settable_state(true, &(record.body));
1830 }
1831
1832 auto id_string = sensor->second.sensorName;
1833
1834 if (id_string.empty())
1835 {
1836 id_string = sensor->second.sensorNameFunc(sensor->second);
1837 }
1838
1839 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1840 {
1841 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1842 &(record.body));
1843 }
1844 else
1845 {
1846 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1847 }
1848 std::strncpy(record.body.id_string, id_string.c_str(),
1849 get_sdr::body::get_id_strlen(&(record.body)));
1850}
1851#endif
1852
Hao Jiange39d4d82021-04-16 17:02:40 -07001853// Construct type 3 SDR header and key (for VR and other discrete sensors)
1854void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1855 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001856{
1857 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1858 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1859
1860 get_sdr::header::set_record_id(
1861 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1862
1863 record.header.sdr_version = ipmiSdrVersion;
1864 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1865 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1866 sizeof(get_sdr::SensorDataRecordHeader);
1867 record.key.owner_id = bmcI2CAddr;
1868 record.key.owner_lun = lun;
1869 record.key.sensor_number = sensornumber;
1870
1871 record.body.entity_id = 0x00;
1872 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001873}
Willy Tu61992ad2021-03-29 15:33:20 -07001874
Hao Jiange39d4d82021-04-16 17:02:40 -07001875// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001876bool constructVrSdr(ipmi::Context::ptr ctx,
1877 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1878 uint16_t sensorNum, uint16_t recordID,
1879 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001880 get_sdr::SensorDataEventRecord& record)
1881{
Hao Jiange39d4d82021-04-16 17:02:40 -07001882 constructEventSdrHeaderKey(sensorNum, recordID, record);
1883
1884 DbusInterfaceMap sensorMap;
1885 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1886 {
1887 phosphor::logging::log<phosphor::logging::level::ERR>(
1888 "Failed to update sensor map for VR sensor",
1889 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1890 phosphor::logging::entry("PATH=%s", path.c_str()));
1891 return false;
1892 }
Willy Tu61992ad2021-03-29 15:33:20 -07001893 // follow the association chain to get the parent board's entityid and
1894 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001895 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1896 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001897 record.body.entity_instance);
1898
1899 // Sensor type is hardcoded as a module/board type instead of parsing from
1900 // sensor path. This is because VR control is allocated in an independent
1901 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1902 // types.
1903 static constexpr const uint8_t module_board_type = 0x15;
1904 record.body.sensor_type = module_board_type;
1905 record.body.event_reading_type = 0x00;
1906
1907 record.body.sensor_record_sharing_1 = 0x00;
1908 record.body.sensor_record_sharing_2 = 0x00;
1909
1910 // populate sensor name from path
1911 auto name = sensor::parseSdrIdFromPath(path);
1912 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1913 record.body.id_string_info = nameSize;
1914 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1915 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1916
1917 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001918 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001919
1920 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001921}
1922
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001923static inline uint16_t getNumberOfSensors()
1924{
1925 return std::min(getSensorTree().size(), maxIPMISensors);
1926}
1927
Willy Tu4eca2512022-06-20 21:14:51 -07001928static int getSensorDataRecord(
1929 ipmi::Context::ptr ctx,
1930 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1931 std::vector<uint8_t>& recordData, uint16_t recordID,
1932 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001933{
1934 size_t fruCount = 0;
1935 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1936 if (ret != ipmi::ccSuccess)
1937 {
1938 phosphor::logging::log<phosphor::logging::level::ERR>(
1939 "getSensorDataRecord: getFruSdrCount error");
1940 return GENERAL_ERROR;
1941 }
1942
Harvey Wu05d17c02021-09-15 08:46:59 +08001943 const auto& entityRecords =
1944 ipmi::sensor::EntityInfoMapContainer::getContainer()
1945 ->getIpmiEntityRecords();
1946 size_t entityCount = entityRecords.size();
1947
1948 size_t lastRecord = getNumberOfSensors() + fruCount +
1949 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001950 if (recordID == lastRecordIndex)
1951 {
1952 recordID = lastRecord;
1953 }
1954 if (recordID > lastRecord)
1955 {
1956 phosphor::logging::log<phosphor::logging::level::ERR>(
1957 "getSensorDataRecord: recordID > lastRecord error");
1958 return GENERAL_ERROR;
1959 }
1960
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001961 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001962 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001963 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001964
Harvey Wu05d17c02021-09-15 08:46:59 +08001965 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1966 {
1967 // handle type 8 entity map records
1968 ipmi::sensor::EntityInfoMap::const_iterator entity =
1969 entityRecords.find(static_cast<uint8_t>(
1970 sdrIndex - fruCount - ipmi::storage::type12Count));
1971 if (entity == entityRecords.end())
1972 {
1973 return IPMI_CC_SENSOR_INVALID;
1974 }
1975 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1976 }
1977 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001978 {
1979 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001980 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001981 if (type12Index >= ipmi::storage::type12Count)
1982 {
1983 phosphor::logging::log<phosphor::logging::level::ERR>(
1984 "getSensorDataRecord: type12Index error");
1985 return GENERAL_ERROR;
1986 }
1987 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1988 }
1989 else
1990 {
1991 // handle fru records
1992 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001993 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001994 if (ret != IPMI_CC_OK)
1995 {
1996 return GENERAL_ERROR;
1997 }
1998 data.header.record_id_msb = recordID >> 8;
1999 data.header.record_id_lsb = recordID & 0xFF;
2000 recordData.insert(recordData.end(), (uint8_t*)&data,
2001 ((uint8_t*)&data) + sizeof(data));
2002 }
2003
2004 return 0;
2005 }
2006
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002007 // Perform a incremental scan of the SDR Record ID's and translate the
2008 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2009 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2010 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2011 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002012 std::string connection;
2013 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002014 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002015 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002016 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002017 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002018 // LUN 0 has one reserved sensor number. Compensate here by adding one
2019 // to the record ID
2020 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002021 ctx->lun = 1;
2022 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002023 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002024 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002025 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2026 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2027 // rules governing its use.
2028 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002029 ctx->lun = 3;
2030 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002031
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002032 auto status =
2033 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2034 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002035 if (status)
2036 {
2037 phosphor::logging::log<phosphor::logging::level::ERR>(
2038 "getSensorDataRecord: getSensorConnection error");
2039 return GENERAL_ERROR;
2040 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002041 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002042 // Return an error on LUN 2 assingments, and any sensor number beyond the
2043 // range of LUN 3
2044 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2045 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002046 {
2047 phosphor::logging::log<phosphor::logging::level::ERR>(
2048 "getSensorDataRecord: invalidSensorNumber");
2049 return GENERAL_ERROR;
2050 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002051 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2052 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2053
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002054 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2055 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002056 {
2057 phosphor::logging::log<phosphor::logging::level::ERR>(
2058 "getSensorDataRecord: sensor record mismatch");
2059 return GENERAL_ERROR;
2060 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002061
Willy Tu38e7a2b2021-03-29 15:09:56 -07002062 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002063 if (std::find(interfaces.begin(), interfaces.end(),
2064 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002065 {
Willy Tu11d68892022-01-20 10:37:34 -08002066 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002067
Hao Jiange39d4d82021-04-16 17:02:40 -07002068 // If the request doesn't read SDR body, construct only header and key
2069 // part to avoid additional DBus transaction.
2070 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2071 {
2072 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2073 }
Willy Tu4eca2512022-06-20 21:14:51 -07002074 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2075 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002076 {
2077 return GENERAL_ERROR;
2078 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002079
Willy Tu38e7a2b2021-03-29 15:09:56 -07002080 recordData.insert(recordData.end(), (uint8_t*)&record,
2081 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002082
2083 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002084 }
Willy Tu61992ad2021-03-29 15:33:20 -07002085
Scron Chang2703b022021-07-06 15:47:45 +08002086#ifdef FEATURE_HYBRID_SENSORS
2087 if (auto sensor = findStaticSensor(path);
2088 sensor != ipmi::sensor::sensors.end() &&
2089 getSensorEventTypeFromPath(path) !=
2090 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2091 {
Willy Tu11d68892022-01-20 10:37:34 -08002092 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002093
2094 // If the request doesn't read SDR body, construct only header and key
2095 // part to avoid additional DBus transaction.
2096 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2097 {
2098 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2099 }
2100 else
2101 {
2102 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2103 }
2104
2105 recordData.insert(recordData.end(), (uint8_t*)&record,
2106 ((uint8_t*)&record) + sizeof(record));
2107
2108 return 0;
2109 }
2110#endif
2111
Willy Tu61992ad2021-03-29 15:33:20 -07002112 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002113 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2114 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002115 {
Willy Tu11d68892022-01-20 10:37:34 -08002116 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002117
Hao Jiange39d4d82021-04-16 17:02:40 -07002118 // If the request doesn't read SDR body, construct only header and key
2119 // part to avoid additional DBus transaction.
2120 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2121 {
2122 constructEventSdrHeaderKey(sensorNum, recordID, record);
2123 }
Willy Tu4eca2512022-06-20 21:14:51 -07002124 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2125 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002126 {
2127 return GENERAL_ERROR;
2128 }
Willy Tu61992ad2021-03-29 15:33:20 -07002129 recordData.insert(recordData.end(), (uint8_t*)&record,
2130 ((uint8_t*)&record) + sizeof(record));
2131 }
2132
Willy Tude54f482021-01-26 15:59:09 -08002133 return 0;
2134}
2135
2136/** @brief implements the get SDR Info command
2137 * @param count - Operation
2138 *
2139 * @returns IPMI completion code plus response data
2140 * - sdrCount - sensor/SDR count
2141 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2142 */
2143static ipmi::RspType<uint8_t, // respcount
2144 uint8_t, // dynamic population flags
2145 uint32_t // last time a sensor was added
2146 >
2147 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2148 std::optional<uint8_t> count)
2149{
2150 auto& sensorTree = getSensorTree();
2151 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002152 uint16_t recordID = 0;
2153 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002154 // Sensors are dynamically allocated, and there is at least one LUN
2155 uint8_t lunsAndDynamicPopulation = 0x80;
2156 constexpr uint8_t getSdrCount = 0x01;
2157 constexpr uint8_t getSensorCount = 0x00;
2158
2159 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2160 {
2161 return ipmi::responseResponseError();
2162 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002163 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002164 if (count.value_or(0) == getSdrCount)
2165 {
Willy Tu4eca2512022-06-20 21:14:51 -07002166 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2167
Willy Tude54f482021-01-26 15:59:09 -08002168 // Count the number of Type 1 SDR entries assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002169 while (!getSensorDataRecord(
2170 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2171 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002172 {
2173 get_sdr::SensorDataRecordHeader* hdr =
2174 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002175 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002176 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2177 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002178 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002179 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002180 record.data());
2181 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002182 {
2183 sdrCount++;
2184 }
2185 }
2186 }
2187 }
2188 else if (count.value_or(0) == getSensorCount)
2189 {
2190 // Return the number of sensors attached to the LUN
2191 if ((ctx->lun == 0) && (numSensors > 0))
2192 {
2193 sdrCount =
2194 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2195 }
2196 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2197 {
2198 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2199 ? maxSensorsPerLUN
2200 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2201 }
2202 else if (ctx->lun == 3)
2203 {
2204 if (numSensors <= maxIPMISensors)
2205 {
2206 sdrCount =
2207 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2208 }
2209 else
2210 {
2211 // error
2212 throw std::out_of_range(
2213 "Maximum number of IPMI sensors exceeded.");
2214 }
2215 }
2216 }
2217 else
2218 {
2219 return ipmi::responseInvalidFieldRequest();
2220 }
2221
2222 // Get Sensor count. This returns the number of sensors
2223 if (numSensors > 0)
2224 {
2225 lunsAndDynamicPopulation |= 1;
2226 }
2227 if (numSensors > maxSensorsPerLUN)
2228 {
2229 lunsAndDynamicPopulation |= 2;
2230 }
2231 if (numSensors >= (maxSensorsPerLUN * 2))
2232 {
2233 lunsAndDynamicPopulation |= 8;
2234 }
2235 if (numSensors > maxIPMISensors)
2236 {
2237 // error
2238 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2239 }
2240
2241 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2242 sdrLastAdd);
2243}
2244
2245/* end sensor commands */
2246
2247/* storage commands */
2248
2249ipmi::RspType<uint8_t, // sdr version
2250 uint16_t, // record count
2251 uint16_t, // free space
2252 uint32_t, // most recent addition
2253 uint32_t, // most recent erase
2254 uint8_t // operationSupport
2255 >
2256 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2257{
2258 auto& sensorTree = getSensorTree();
2259 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002260 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002261 {
2262 return ipmi::responseResponseError();
2263 }
2264
2265 size_t fruCount = 0;
2266 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2267 if (ret != ipmi::ccSuccess)
2268 {
2269 return ipmi::response(ret);
2270 }
2271
2272 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002273 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002274
2275 uint8_t operationSupport = static_cast<uint8_t>(
2276 SdrRepositoryInfoOps::overflow); // write not supported
2277
2278 operationSupport |=
2279 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2280 operationSupport |= static_cast<uint8_t>(
2281 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2282 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2283 unspecifiedFreeSpace, sdrLastAdd,
2284 sdrLastRemove, operationSupport);
2285}
2286
2287/** @brief implements the get SDR allocation info command
2288 *
2289 * @returns IPMI completion code plus response data
2290 * - allocUnits - Number of possible allocation units
2291 * - allocUnitSize - Allocation unit size in bytes.
2292 * - allocUnitFree - Number of free allocation units
2293 * - allocUnitLargestFree - Largest free block in allocation units
2294 * - maxRecordSize - Maximum record size in allocation units.
2295 */
2296ipmi::RspType<uint16_t, // allocUnits
2297 uint16_t, // allocUnitSize
2298 uint16_t, // allocUnitFree
2299 uint16_t, // allocUnitLargestFree
2300 uint8_t // maxRecordSize
2301 >
2302 ipmiStorageGetSDRAllocationInfo()
2303{
2304 // 0000h unspecified number of alloc units
2305 constexpr uint16_t allocUnits = 0;
2306
2307 constexpr uint16_t allocUnitFree = 0;
2308 constexpr uint16_t allocUnitLargestFree = 0;
2309 // only allow one block at a time
2310 constexpr uint8_t maxRecordSize = 1;
2311
2312 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2313 allocUnitLargestFree, maxRecordSize);
2314}
2315
2316/** @brief implements the reserve SDR command
2317 * @returns IPMI completion code plus response data
2318 * - sdrReservationID
2319 */
2320ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2321{
2322 sdrReservationID++;
2323 if (sdrReservationID == 0)
2324 {
2325 sdrReservationID++;
2326 }
2327
2328 return ipmi::responseSuccess(sdrReservationID);
2329}
2330
2331ipmi::RspType<uint16_t, // next record ID
2332 std::vector<uint8_t> // payload
2333 >
2334 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2335 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2336{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002337 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002338 // reservation required for partial reads with non zero offset into
2339 // record
2340 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2341 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002342 phosphor::logging::log<phosphor::logging::level::ERR>(
2343 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002344 return ipmi::responseInvalidReservationId();
2345 }
Willy Tude54f482021-01-26 15:59:09 -08002346 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2347 if (ret != ipmi::ccSuccess)
2348 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002349 phosphor::logging::log<phosphor::logging::level::ERR>(
2350 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002351 return ipmi::response(ret);
2352 }
2353
Harvey Wu05d17c02021-09-15 08:46:59 +08002354 const auto& entityRecords =
2355 ipmi::sensor::EntityInfoMapContainer::getContainer()
2356 ->getIpmiEntityRecords();
2357 int entityCount = entityRecords.size();
2358
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002359 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002360 size_t lastRecord = getNumberOfSensors() + fruCount +
2361 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002362 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2363
2364 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002365 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002366 phosphor::logging::log<phosphor::logging::level::ERR>(
2367 "ipmiStorageGetSDR: getSensorSubtree error");
2368 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002369 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002370
Willy Tu4eca2512022-06-20 21:14:51 -07002371 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2372
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002373 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002374 if (getSensorDataRecord(
2375 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2376 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002377 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002378 phosphor::logging::log<phosphor::logging::level::ERR>(
2379 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002380 return ipmi::responseInvalidFieldRequest();
2381 }
Willy Tude54f482021-01-26 15:59:09 -08002382 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002383 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002384 if (!hdr)
2385 {
2386 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002387 "ipmiStorageGetSDR: record header is null");
2388 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002389 }
2390
2391 size_t sdrLength =
2392 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2393 if (sdrLength < (offset + bytesToRead))
2394 {
2395 bytesToRead = sdrLength - offset;
2396 }
2397
2398 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2399 if (!respStart)
2400 {
2401 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002402 "ipmiStorageGetSDR: record is null");
2403 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002404 }
2405
2406 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002407
Willy Tude54f482021-01-26 15:59:09 -08002408 return ipmi::responseSuccess(nextRecordId, recordData);
2409}
adarshgrami042e9db2022-09-15 10:34:34 +05302410namespace dcmi
2411{
2412
2413ipmi::RspType<uint8_t, // No of instances for requested id
2414 uint8_t, // No of record ids in the response
2415 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2416 // IDs
2417 >
2418 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
2419 uint8_t entityInstance, uint8_t instanceStart)
2420{
2421 auto match = ipmi::dcmi::validEntityId.find(entityId);
2422 if (match == ipmi::dcmi::validEntityId.end())
2423 {
2424 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2425
2426 return ipmi::responseInvalidFieldRequest();
2427 }
2428
2429 if (sensorType != ipmi::dcmi::temperatureSensorType)
2430 {
2431 log<level::ERR>("Invalid sensor type",
2432 entry("SENSOR_TYPE=%d", sensorType));
2433
2434 return ipmi::responseInvalidFieldRequest();
2435 }
2436 auto& sensorTree = getSensorTree();
2437 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2438 {
2439 return ipmi::responseUnspecifiedError();
2440 }
2441
2442 std::vector<uint16_t> sensorRec{};
2443 uint8_t numInstances = 0;
2444
2445 for (const auto& sensor : sensorTree)
2446 {
2447 auto sensorTypeValue = getSensorTypeFromPath(sensor.first);
2448 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2449 {
2450 continue;
2451 }
2452 const auto& connection = sensor.second.begin()->first;
2453
2454 DbusInterfaceMap sensorMap;
2455 if (!getSensorMap(ctx, connection, sensor.first, sensorMap,
2456 sensorMapSdrUpdatePeriod))
2457 {
2458 phosphor::logging::log<phosphor::logging::level::ERR>(
2459 "Failed to update sensor map for threshold sensor",
2460 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2461 phosphor::logging::entry("PATH=%s", sensor.first.c_str()));
2462 continue;
2463 }
2464 uint8_t entityIdValue = 0;
2465 uint8_t entityInstanceValue = 0;
2466
2467 updateIpmiFromAssociation(sensor.first, sensorMap, entityIdValue,
2468 entityInstanceValue);
2469 if (!entityInstance)
2470 {
2471 if (entityIdValue == match->first || entityIdValue == match->second)
2472 {
2473 auto recordId = getSensorNumberFromPath(sensor.first);
2474 if (recordId != invalidSensorNumber)
2475 {
2476 numInstances++;
2477 if (numInstances <= ipmi::dcmi::maxRecords)
2478 {
2479 sensorRec.push_back(recordId);
2480 }
2481 }
2482 }
2483 }
2484 else
2485 {
2486 if (entityIdValue == match->first || entityIdValue == match->second)
2487 {
2488 if (entityInstance == entityInstanceValue)
2489 {
2490 auto recordId = getSensorNumberFromPath(sensor.first);
2491 if ((recordId != invalidSensorNumber) && sensorRec.empty())
2492 {
2493 sensorRec.push_back(recordId);
2494 }
2495 }
2496 numInstances++;
2497 }
2498 }
2499 }
2500 if (sensorRec.empty())
2501 {
2502 return ipmi::responseSensorInvalid();
2503 }
2504 uint8_t numRecords = sensorRec.size();
2505 return ipmi::responseSuccess(numInstances, numRecords, sensorRec);
2506}
2507} // namespace dcmi
2508
Willy Tude54f482021-01-26 15:59:09 -08002509/* end storage commands */
2510
2511void registerSensorFunctions()
2512{
2513 // <Platform Event>
2514 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2515 ipmi::sensor_event::cmdPlatformEvent,
2516 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2517
Willy Tudbafbce2021-03-29 00:37:05 -07002518 // <Set Sensor Reading and Event Status>
2519 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2520 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2521 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002522
Willy Tude54f482021-01-26 15:59:09 -08002523 // <Get Sensor Reading>
2524 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2525 ipmi::sensor_event::cmdGetSensorReading,
2526 ipmi::Privilege::User, ipmiSenGetSensorReading);
2527
2528 // <Get Sensor Threshold>
2529 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2530 ipmi::sensor_event::cmdGetSensorThreshold,
2531 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2532
2533 // <Set Sensor Threshold>
2534 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2535 ipmi::sensor_event::cmdSetSensorThreshold,
2536 ipmi::Privilege::Operator,
2537 ipmiSenSetSensorThresholds);
2538
2539 // <Get Sensor Event Enable>
2540 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2541 ipmi::sensor_event::cmdGetSensorEventEnable,
2542 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2543
2544 // <Get Sensor Event Status>
2545 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2546 ipmi::sensor_event::cmdGetSensorEventStatus,
2547 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2548
2549 // register all storage commands for both Sensor and Storage command
2550 // versions
2551
2552 // <Get SDR Repository Info>
2553 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2554 ipmi::storage::cmdGetSdrRepositoryInfo,
2555 ipmi::Privilege::User,
2556 ipmiStorageGetSDRRepositoryInfo);
2557
2558 // <Get Device SDR Info>
2559 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2560 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2561 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2562
2563 // <Get SDR Allocation Info>
2564 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2565 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2566 ipmi::Privilege::User,
2567 ipmiStorageGetSDRAllocationInfo);
2568
2569 // <Reserve SDR Repo>
2570 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2571 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2572 ipmi::Privilege::User, ipmiStorageReserveSDR);
2573
2574 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2575 ipmi::storage::cmdReserveSdrRepository,
2576 ipmi::Privilege::User, ipmiStorageReserveSDR);
2577
2578 // <Get Sdr>
2579 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2580 ipmi::sensor_event::cmdGetDeviceSdr,
2581 ipmi::Privilege::User, ipmiStorageGetSDR);
2582
2583 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2584 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2585 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302586 // <Get DCMI Sensor Info>
2587 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2588 ipmi::dcmi::cmdGetDcmiSensorInfo,
2589 ipmi::Privilege::User,
2590 ipmi::dcmi::getSensorInfo);
Willy Tude54f482021-01-26 15:59:09 -08002591}
2592} // namespace ipmi