blob: c6a167febadddb84d3c5384646c790dba0c30a37 [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
Alexander Hansenc2c26f92023-07-17 09:38:43 +020017#include "config.h"
18
Willy Tude54f482021-01-26 15:59:09 -080019#include "dbus-sdr/sensorcommands.hpp"
20
21#include "dbus-sdr/sdrutils.hpp"
22#include "dbus-sdr/sensorutils.hpp"
23#include "dbus-sdr/storagecommands.hpp"
24
Willy Tude54f482021-01-26 15:59:09 -080025#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050027#include <ipmid/api.hpp>
Vernon Mauery9cf08382023-04-28 14:00:11 -070028#include <ipmid/entity_map_json.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050029#include <ipmid/types.hpp>
30#include <ipmid/utils.hpp>
George Liude6694e2024-07-17 15:22:25 +080031#include <phosphor-logging/lg2.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050032#include <sdbusplus/bus.hpp>
33#include <user_channel/channel_layer.hpp>
34
35#include <algorithm>
36#include <array>
Willy Tude54f482021-01-26 15:59:09 -080037#include <chrono>
38#include <cmath>
39#include <cstring>
Willy Tu62e3ca82024-01-31 17:28:34 +000040#include <format>
Willy Tude54f482021-01-26 15:59:09 -080041#include <map>
Willy Tude54f482021-01-26 15:59:09 -080042#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080043#include <stdexcept>
44#include <string>
45#include <utility>
46#include <variant>
47
Scron Chang2703b022021-07-06 15:47:45 +080048#ifdef FEATURE_HYBRID_SENSORS
49
50#include "sensordatahandler.hpp"
51namespace ipmi
52{
53namespace sensor
54{
55extern const IdInfoMap sensors;
56} // namespace sensor
57} // namespace ipmi
58#endif
adarshgrami042e9db2022-09-15 10:34:34 +053059namespace ipmi
60{
61namespace dcmi
62{
63// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
64static const std::map<uint8_t, uint8_t> validEntityId{
65 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
66 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
67constexpr uint8_t temperatureSensorType = 0x01;
68constexpr uint8_t maxRecords = 8;
69} // namespace dcmi
70} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080071constexpr std::array<const char*, 7> suffixes = {
72 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
73 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080074namespace ipmi
75{
Hao Jiangd48c9212021-02-03 15:45:06 -080076
77using phosphor::logging::entry;
78using phosphor::logging::level;
79using phosphor::logging::log;
80
Willy Tude54f482021-01-26 15:59:09 -080081static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070082static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080083
Willy Tu38e7a2b2021-03-29 15:09:56 -070084// BMC I2C address is generally at 0x20
85static constexpr uint8_t bmcI2CAddr = 0x20;
86
Willy Tude54f482021-01-26 15:59:09 -080087constexpr size_t maxSDRTotalSize =
88 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
89constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
90
91static uint16_t sdrReservationID;
92static uint32_t sdrLastAdd = noTimestamp;
93static uint32_t sdrLastRemove = noTimestamp;
94static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070095
96// The IPMI spec defines four Logical Units (LUN), each capable of supporting
97// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
98// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +080099// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700100// location.
101static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
102
Harvey Wu75893062023-03-22 17:17:31 +0800103static constexpr uint8_t lun0 = 0x0;
104static constexpr uint8_t lun1 = 0x1;
105static constexpr uint8_t lun3 = 0x3;
106
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700107static constexpr size_t lun0MaxSensorNum = 0xfe;
108static constexpr size_t lun1MaxSensorNum = 0x1fe;
109static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800110static constexpr int GENERAL_ERROR = -1;
111
Willy Tude54f482021-01-26 15:59:09 -0800112static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
113
114// Specify the comparison required to sort and find char* map objects
115struct CmpStr
116{
117 bool operator()(const char* a, const char* b) const
118 {
119 return std::strcmp(a, b) < 0;
120 }
121};
122const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
123 sensorUnits{{{"temperature", SensorUnits::degreesC},
124 {"voltage", SensorUnits::volts},
125 {"current", SensorUnits::amps},
126 {"fan_tach", SensorUnits::rpm},
Johnathan Mantey7b037272024-06-17 12:12:40 -0700127 {"power", SensorUnits::watts},
128 {"energy", SensorUnits::joules}}};
Willy Tude54f482021-01-26 15:59:09 -0800129
130void registerSensorFunctions() __attribute__((constructor));
131
Patrick Williams5d82f472022-07-22 19:26:53 -0500132static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800133 *getSdBus(),
134 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
135 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500136 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400137 getSensorTree().clear();
138 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
139 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
140 std::chrono::system_clock::now().time_since_epoch())
141 .count();
142 });
Willy Tude54f482021-01-26 15:59:09 -0800143
Patrick Williams5d82f472022-07-22 19:26:53 -0500144static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800145 *getSdBus(),
146 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
147 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500148 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400149 getSensorTree().clear();
150 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
151 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
152 std::chrono::system_clock::now().time_since_epoch())
153 .count();
154 });
Willy Tude54f482021-01-26 15:59:09 -0800155
George Liu23c868c2025-07-04 09:31:35 +0800156ipmi::Cc getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
157 std::string& connection, std::string& path,
158 std::vector<std::string>* interfaces)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700159{
160 auto& sensorTree = getSensorTree();
161 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
162 {
George Liu879c1d82025-07-03 09:36:57 +0800163 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700164 }
165
166 if (ctx == nullptr)
167 {
George Liu879c1d82025-07-03 09:36:57 +0800168 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700169 }
170
171 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
172 if (path.empty())
173 {
George Liu879c1d82025-07-03 09:36:57 +0800174 return ipmi::ccInvalidFieldRequest;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700175 }
176
177 for (const auto& sensor : sensorTree)
178 {
179 if (path == sensor.first)
180 {
181 connection = sensor.second.begin()->first;
182 if (interfaces)
183 *interfaces = sensor.second.begin()->second;
184 break;
185 }
186 }
187
188 return 0;
189}
190
191SensorSubTree& getSensorTree()
192{
193 static SensorSubTree sensorTree;
194 return sensorTree;
195}
196
Willy Tude54f482021-01-26 15:59:09 -0800197// this keeps track of deassertions for sensor event status command. A
198// deasertion can only happen if an assertion was seen first.
199static boost::container::flat_map<
200 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
201 thresholdDeassertMap;
202
Patrick Williams5d82f472022-07-22 19:26:53 -0500203static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800204 *getSdBus(),
205 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
206 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500207 [](sdbusplus::message_t& m) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400208 boost::container::flat_map<std::string, std::variant<bool, double>>
209 values;
210 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800211
Patrick Williams1318a5e2024-08-16 15:19:54 -0400212 auto findAssert =
213 std::find_if(values.begin(), values.end(), [](const auto& pair) {
214 return pair.first.find("Alarm") != std::string::npos;
215 });
216 if (findAssert != values.end())
Willy Tude54f482021-01-26 15:59:09 -0800217 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400218 auto ptr = std::get_if<bool>(&(findAssert->second));
219 if (ptr == nullptr)
220 {
221 lg2::error("thresholdChanged: Assert non bool");
222 return;
223 }
224 if (*ptr)
Willy Tude54f482021-01-26 15:59:09 -0800225 {
George Liude6694e2024-07-17 15:22:25 +0800226 lg2::info(
Patrick Williams1318a5e2024-08-16 15:19:54 -0400227 "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
George Liude6694e2024-07-17 15:22:25 +0800228 "SENSOR_PATH", m.get_path());
Patrick Williams1318a5e2024-08-16 15:19:54 -0400229 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
230 }
231 else
232 {
233 auto& value =
234 thresholdDeassertMap[m.get_path()][findAssert->first];
235 if (value)
236 {
237 lg2::info(
238 "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
239 "SENSOR_PATH", m.get_path());
240 value = *ptr;
241 }
Willy Tude54f482021-01-26 15:59:09 -0800242 }
243 }
Patrick Williams1318a5e2024-08-16 15:19:54 -0400244 });
Willy Tude54f482021-01-26 15:59:09 -0800245
Hao Jiangd2afd052020-12-10 15:09:32 -0800246namespace sensor
247{
248static constexpr const char* vrInterface =
249 "xyz.openbmc_project.Control.VoltageRegulatorMode";
250static constexpr const char* sensorInterface =
251 "xyz.openbmc_project.Sensor.Value";
252} // namespace sensor
253
Willy Tude54f482021-01-26 15:59:09 -0800254static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
255 double& min)
256{
257 max = 127;
258 min = -128;
259
Hao Jiangd2afd052020-12-10 15:09:32 -0800260 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800261 auto critical =
262 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
263 auto warning =
264 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
265
266 if (sensorObject != sensorMap.end())
267 {
268 auto maxMap = sensorObject->second.find("MaxValue");
269 auto minMap = sensorObject->second.find("MinValue");
270
271 if (maxMap != sensorObject->second.end())
272 {
273 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
274 }
275 if (minMap != sensorObject->second.end())
276 {
277 min = std::visit(VariantToDoubleVisitor(), minMap->second);
278 }
279 }
280 if (critical != sensorMap.end())
281 {
282 auto lower = critical->second.find("CriticalLow");
283 auto upper = critical->second.find("CriticalHigh");
284 if (lower != critical->second.end())
285 {
286 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300287 if (std::isfinite(value))
288 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700289 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300290 }
Willy Tude54f482021-01-26 15:59:09 -0800291 }
292 if (upper != critical->second.end())
293 {
294 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300295 if (std::isfinite(value))
296 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700297 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300298 }
Willy Tude54f482021-01-26 15:59:09 -0800299 }
300 }
301 if (warning != sensorMap.end())
302 {
Willy Tude54f482021-01-26 15:59:09 -0800303 auto lower = warning->second.find("WarningLow");
304 auto upper = warning->second.find("WarningHigh");
305 if (lower != warning->second.end())
306 {
307 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300308 if (std::isfinite(value))
309 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700310 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300311 }
Willy Tude54f482021-01-26 15:59:09 -0800312 }
313 if (upper != warning->second.end())
314 {
315 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300316 if (std::isfinite(value))
317 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700318 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300319 }
Willy Tude54f482021-01-26 15:59:09 -0800320 }
321 }
322}
323
324static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700325 std::string sensorPath, DbusInterfaceMap& sensorMap,
326 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800327{
Scron Chang2703b022021-07-06 15:47:45 +0800328#ifdef FEATURE_HYBRID_SENSORS
329 if (auto sensor = findStaticSensor(sensorPath);
330 sensor != ipmi::sensor::sensors.end() &&
331 getSensorEventTypeFromPath(sensorPath) !=
332 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
333 {
334 // If the incoming sensor is a discrete sensor, it might fail in
335 // getManagedObjects(), return true, and use its own getFunc to get
336 // value.
337 return true;
338 }
339#endif
340
Willy Tude54f482021-01-26 15:59:09 -0800341 static boost::container::flat_map<
342 std::string, std::chrono::time_point<std::chrono::steady_clock>>
343 updateTimeMap;
344
345 auto updateFind = updateTimeMap.find(sensorConnection);
346 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
347 if (updateFind != updateTimeMap.end())
348 {
349 lastUpdate = updateFind->second;
350 }
351
352 auto now = std::chrono::steady_clock::now();
353
354 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700355 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800356 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800357 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800358
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800359 // Object managers for different kinds of OpenBMC DBus interfaces.
360 // Documented in the phosphor-dbus-interfaces repository.
361 const char* paths[] = {
362 "/xyz/openbmc_project/sensors",
363 "/xyz/openbmc_project/vr",
364 };
George Liuf2807ed2025-04-03 15:52:57 +0800365 constexpr size_t numPaths = sizeof(paths) / sizeof(paths[0]);
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800366 ObjectValueTree allManagedObjects;
367
George Liuf2807ed2025-04-03 15:52:57 +0800368 for (size_t i = 0; i < numPaths; i++)
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800369 {
370 ObjectValueTree managedObjects;
371 boost::system::error_code ec = getManagedObjects(
372 ctx, sensorConnection.c_str(), paths[i], managedObjects);
373 if (ec)
374 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800375 continue;
376 }
377 allManagedObjects.merge(managedObjects);
378 found = true;
379 }
380
381 if (!found)
382 {
George Liude6694e2024-07-17 15:22:25 +0800383 lg2::error("GetMangagedObjects for getSensorMap failed, "
384 "service: {SERVICE}",
385 "SERVICE", sensorConnection);
Tom Tung6615d472023-05-31 18:48:12 +0800386
Willy Tude54f482021-01-26 15:59:09 -0800387 return false;
388 }
389
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800390 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700391 // Update time after finish building the map which allow the
392 // data to be cached for updatePeriod plus the build time.
393 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800394 }
395 auto connection = SensorCache.find(sensorConnection);
396 if (connection == SensorCache.end())
397 {
398 return false;
399 }
400 auto path = connection->second.find(sensorPath);
401 if (path == connection->second.end())
402 {
403 return false;
404 }
405 sensorMap = path->second;
406
407 return true;
408}
409
Hao Jiangd2afd052020-12-10 15:09:32 -0800410namespace sensor
411{
Hao Jiangd48c9212021-02-03 15:45:06 -0800412// Read VR profiles from sensor(daemon) interface
Patrick Williams69b4c282025-03-03 11:19:13 -0500413static std::optional<std::vector<std::string>> getSupportedVrProfiles(
414 const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800415{
416 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800417 auto supportedProperty = object.find("Supported");
418 if (supportedProperty == object.end() ||
419 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800420 {
George Liude6694e2024-07-17 15:22:25 +0800421 lg2::error("Missing the required Supported and Selected properties");
Hao Jiangd2afd052020-12-10 15:09:32 -0800422 return std::nullopt;
423 }
424
425 const auto profilesPtr =
426 std::get_if<std::vector<std::string>>(&supportedProperty->second);
427
428 if (profilesPtr == nullptr)
429 {
George Liude6694e2024-07-17 15:22:25 +0800430 lg2::error("property is not array of string");
Hao Jiangd2afd052020-12-10 15:09:32 -0800431 return std::nullopt;
432 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800433 return *profilesPtr;
434}
435
436// Calculate VR Mode from input IPMI discrete event bytes
Patrick Williams1318a5e2024-08-16 15:19:54 -0400437static std::optional<std::string> calculateVRMode(
438 uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
Hao Jiangd48c9212021-02-03 15:45:06 -0800439{
440 // get VR mode profiles from Supported Interface
441 auto profiles = getSupportedVrProfiles(VRObject);
442 if (!profiles)
443 {
444 return std::nullopt;
445 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800446
447 // interpret IPMI cmd bits into profiles' index
448 long unsigned int index = 0;
449 // only one bit should be set and the highest bit should not be used.
450 if (assertOffset == 0 || assertOffset == (1u << 15) ||
451 (assertOffset & (assertOffset - 1)))
452 {
George Liude6694e2024-07-17 15:22:25 +0800453 lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
454 lg2::hex, static_cast<uint16_t>(assertOffset));
Hao Jiangd2afd052020-12-10 15:09:32 -0800455 return std::nullopt;
456 }
457
458 while (assertOffset != 1)
459 {
460 assertOffset >>= 1;
461 index++;
462 }
463
Hao Jiangd48c9212021-02-03 15:45:06 -0800464 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800465 {
George Liude6694e2024-07-17 15:22:25 +0800466 lg2::error("profile index out of boundary");
Hao Jiangd2afd052020-12-10 15:09:32 -0800467 return std::nullopt;
468 }
469
Hao Jiangd48c9212021-02-03 15:45:06 -0800470 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800471}
472
473// Calculate sensor value from IPMI reading byte
Patrick Williams69b4c282025-03-03 11:19:13 -0500474static std::optional<double> calculateValue(
475 uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
476 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
Hao Jiangd2afd052020-12-10 15:09:32 -0800477{
478 if (valueObject.find("Value") == valueObject.end())
479 {
George Liude6694e2024-07-17 15:22:25 +0800480 lg2::error("Missing the required Value property");
Hao Jiangd2afd052020-12-10 15:09:32 -0800481 return std::nullopt;
482 }
483
484 double max = 0;
485 double min = 0;
486 getSensorMaxMin(sensorMap, max, min);
487
488 int16_t mValue = 0;
489 int16_t bValue = 0;
490 int8_t rExp = 0;
491 int8_t bExp = 0;
492 bool bSigned = false;
493
494 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
495 {
496 return std::nullopt;
497 }
498
499 double value = bSigned ? ((int8_t)reading) : reading;
500
501 value *= ((double)mValue);
502 value += ((double)bValue) * std::pow(10.0, bExp);
503 value *= std::pow(10.0, rExp);
504
505 return value;
506}
507
Willy Tu38e7a2b2021-03-29 15:09:56 -0700508// Extract file name from sensor path as the sensors SDR ID. Simplify the name
509// if it is too long.
510std::string parseSdrIdFromPath(const std::string& path)
511{
512 std::string name;
513 size_t nameStart = path.rfind("/");
514 if (nameStart != std::string::npos)
515 {
516 name = path.substr(nameStart + 1, std::string::npos - nameStart);
517 }
518
Willy Tu38e7a2b2021-03-29 15:09:56 -0700519 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
520 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200521#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800522 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700523 {
George Liuc024b392025-08-21 16:38:58 +0800524 if (name.ends_with(suffix))
JeffLind950f412021-10-20 18:49:34 +0800525 {
526 boost::replace_all(name, suffix, "");
527 break;
528 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700529 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200530#endif
531#ifdef SHORTNAME_REPLACE_WORDS
532 constexpr std::array<std::pair<const char*, const char*>, 2>
533 replaceWords = {std::make_pair("Output", "Out"),
534 std::make_pair("Input", "In")};
535 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800536 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200537 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800538 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200539#endif
540
541 // as a backup and if nothing else is configured
542 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700543 }
544 return name;
545}
546
Hao Jiangd48c9212021-02-03 15:45:06 -0800547bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
548 const std::string& path,
549 const ipmi::DbusInterfaceMap::mapped_type& object,
550 std::bitset<16>& assertions)
551{
552 auto profiles = sensor::getSupportedVrProfiles(object);
553 if (!profiles)
554 {
555 return false;
556 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700557 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800558
559 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700560 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800561 if (ec)
562 {
George Liude6694e2024-07-17 15:22:25 +0800563 lg2::error("Failed to get Selected, path: {PATH}, "
564 "interface: {INTERFACE}, error: {ERROR}",
565 "PATH", path, "INTERFACE", sensor::sensorInterface, "ERROR",
566 ec.message());
Hao Jiangd48c9212021-02-03 15:45:06 -0800567 return false;
568 }
569
Willy Tu8366f0b2022-04-29 05:00:17 -0700570 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800571 if (itr == profiles->end())
572 {
George Liude6694e2024-07-17 15:22:25 +0800573 lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
574 "PATH", path);
Hao Jiangd48c9212021-02-03 15:45:06 -0800575 return false;
576 }
577 std::size_t index =
578 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
579
Willy Tubef102a2022-06-09 15:36:09 -0700580 // map index to response event assertion bit.
581 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800582 {
Willy Tubef102a2022-06-09 15:36:09 -0700583 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800584 }
585 else
586 {
George Liude6694e2024-07-17 15:22:25 +0800587 lg2::error("VR profile index reaches max assertion bit, "
588 "path: {PATH}, index: {INDEX}",
589 "PATH", path, "INDEX", index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800590 return false;
591 }
592 if constexpr (debug)
593 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800594 lg2::error("VR sensor {PATH} mode is: [{INDEX}] {MODE}", "PATH",
595 sensor::parseSdrIdFromPath(path), "INDEX", index, "MODE",
596 mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800597 }
598 return true;
599}
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700600
601/*
602 * Handle every Sensor Data Record besides Type 01
603 *
604 * The D-Bus sensors work well for generating Type 01 SDRs.
605 * After the Type 01 sensors are processed the remaining sensor types require
606 * special handling. Each BMC vendor is going to have their own requirements for
607 * insertion of non-Type 01 records.
608 * Manage non-Type 01 records:
609 *
610 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
611 * Populate it with the two weakly linked functions below, without adding the
612 * 'weak' attribute definition prior to the function definition.
613 * getOtherSensorsCount(...)
614 * getOtherSensorsDataRecord(...)
615 * Example contents are provided in the weak definitions below
616 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
617 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
618 * The contents of the sensorcommands_oem.cpp file will then override the code
619 * provided below.
620 */
621
622size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
623size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
624{
625 size_t fruCount = 0;
626
627 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
628 if (ret != ipmi::ccSuccess)
629 {
George Liude6694e2024-07-17 15:22:25 +0800630 lg2::error("getOtherSensorsCount: getFruSdrCount error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700631 return std::numeric_limits<size_t>::max();
632 }
633
634 const auto& entityRecords =
635 ipmi::sensor::EntityInfoMapContainer::getContainer()
636 ->getIpmiEntityRecords();
637 size_t entityCount = entityRecords.size();
638
639 return fruCount + ipmi::storage::type12Count + entityCount;
640}
641
642int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
643 std::vector<uint8_t>& recordData)
644 __attribute__((weak));
645int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
646 std::vector<uint8_t>& recordData)
647{
648 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
649 if (otherCount == std::numeric_limits<size_t>::max())
650 {
651 return GENERAL_ERROR;
652 }
653 const auto& entityRecords =
654 ipmi::sensor::EntityInfoMapContainer::getContainer()
655 ->getIpmiEntityRecords();
656
657 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
658 size_t entityCount{entityRecords.size()};
659 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
660
661 if (sdrIndex > otherCount)
662 {
663 return std::numeric_limits<int>::min();
664 }
665 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
666 {
667 // handle type 8 entity map records
668 ipmi::sensor::EntityInfoMap::const_iterator entity =
669 entityRecords.find(static_cast<uint8_t>(
670 sdrIndex - fruCount - ipmi::storage::type12Count));
671
672 if (entity == entityRecords.end())
673 {
674 return GENERAL_ERROR;
675 }
676 recordData = ipmi::storage::getType8SDRs(entity, recordID);
677 }
678 else if (sdrIndex >= fruCount)
679 {
680 // handle type 12 hardcoded records
681 size_t type12Index = sdrIndex - fruCount;
682 if (type12Index >= ipmi::storage::type12Count)
683 {
George Liude6694e2024-07-17 15:22:25 +0800684 lg2::error("getSensorDataRecord: type12Index error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700685 return GENERAL_ERROR;
686 }
687 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
688 }
689 else
690 {
691 // handle fru records
692 get_sdr::SensorDataFruRecord data;
693 if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
George Liue8a97bd2024-12-05 17:26:46 +0800694 ret != ipmi::ccSuccess)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700695 {
696 return GENERAL_ERROR;
697 }
George Liuc7a4da52025-08-19 16:20:31 +0800698 data.header.recordId = recordID;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700699 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
700 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
701 }
702
703 return 0;
704}
705
Hao Jiangd2afd052020-12-10 15:09:32 -0800706} // namespace sensor
707
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000708ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
709 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800710{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000711 constexpr const uint8_t validEnvmRev = 0x04;
712 constexpr const uint8_t lastSensorType = 0x2C;
713 constexpr const uint8_t oemReserved = 0xC0;
714
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700715 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000716 uint8_t evmRev = 0;
717 uint8_t sensorType = 0;
718 uint8_t sensorNum = 0;
719 uint8_t eventType = 0;
720 uint8_t eventData1 = 0;
721 std::optional<uint8_t> eventData2 = 0;
722 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700723 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000724 ipmi::ChannelInfo chInfo;
725
726 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
727 {
George Liude6694e2024-07-17 15:22:25 +0800728 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
729 ctx->channel);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000730 return ipmi::responseUnspecifiedError();
731 }
732
733 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
734 ipmi::EChannelMediumType::systemInterface)
735 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700736 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000737 eventData1, eventData2, eventData3);
Johnathan Manteya440cd42024-06-20 13:21:48 -0700738 constexpr const uint8_t isSoftwareID = 0x01;
739 if (!(sysgeneratorID & isSoftwareID))
740 {
741 return ipmi::responseInvalidFieldRequest();
742 }
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700743 // Refer to IPMI Spec Table 32: SEL Event Records
744 generatorID = (ctx->channel << 12) // Channel
745 | (0x0 << 10) // Reserved
746 | (0x0 << 8) // 0x0 for sys-soft ID
Johnathan Manteya440cd42024-06-20 13:21:48 -0700747 | sysgeneratorID;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000748 }
749 else
750 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000751 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
752 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700753 // Refer to IPMI Spec Table 32: SEL Event Records
754 generatorID = (ctx->channel << 12) // Channel
755 | (0x0 << 10) // Reserved
756 | ((ctx->lun & 0x3) << 8) // Lun
757 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000758 }
759
760 if (!p.fullyUnpacked())
761 {
762 return ipmi::responseReqDataLenInvalid();
763 }
764
765 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
766 if (evmRev != validEnvmRev)
767 {
768 return ipmi::responseInvalidFieldRequest();
769 }
770 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
771 {
772 return ipmi::responseInvalidFieldRequest();
773 }
774
Willy Tude54f482021-01-26 15:59:09 -0800775 return ipmi::responseSuccess();
776}
777
Patrick Williams1318a5e2024-08-16 15:19:54 -0400778ipmi::RspType<> ipmiSetSensorReading(
779 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
780 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700781{
782 std::string connection;
783 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700784 std::vector<std::string> interfaces;
785
Patrick Williams1318a5e2024-08-16 15:19:54 -0400786 ipmi::Cc status =
787 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700788 if (status)
789 {
790 return ipmi::response(status);
791 }
792
Hao Jiangd2afd052020-12-10 15:09:32 -0800793 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700794 if (std::find(interfaces.begin(), interfaces.end(),
795 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700796 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700797 DbusInterfaceMap sensorMap;
798 if (!getSensorMap(ctx, connection, path, sensorMap))
799 {
800 return ipmi::responseResponseError();
801 }
802 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800803 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700804 {
805 return ipmi::responseResponseError();
806 }
807
Jie Yangf0a89942021-07-29 15:30:25 -0700808 // Only allow external SetSensor if write permission granted
Patrick Williams1318a5e2024-08-16 15:19:54 -0400809 if (!details::sdrWriteTable.getWritePermission(
810 (ctx->lun << 8) | sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700811 {
812 return ipmi::responseResponseError();
813 }
814
Patrick Williams1318a5e2024-08-16 15:19:54 -0400815 auto value =
816 sensor::calculateValue(reading, sensorMap, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800817 if (!value)
818 {
819 return ipmi::responseResponseError();
820 }
821
822 if constexpr (debug)
823 {
George Liude6694e2024-07-17 15:22:25 +0800824 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
825 "byte: {BYTE}, value: {VALUE}",
826 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
827 "VALUE", *value);
Hao Jiangd2afd052020-12-10 15:09:32 -0800828 }
829
830 boost::system::error_code ec =
831 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
832 "Value", ipmi::Value(*value));
833
834 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500835 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800836 // callback functions for now (e.g. ipmiSetSensorReading).
837 if (ec)
838 {
George Liude6694e2024-07-17 15:22:25 +0800839 lg2::error("Failed to set Value, path: {PATH}, "
840 "interface: {INTERFACE}, ERROR: {ERROR}",
841 "PATH", path, "INTERFACE", sensor::sensorInterface,
842 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800843 return ipmi::responseResponseError();
844 }
845 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700846 }
847
Hao Jiange39d4d82021-04-16 17:02:40 -0700848 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
849 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700850 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700851 DbusInterfaceMap sensorMap;
852 if (!getSensorMap(ctx, connection, path, sensorMap))
853 {
854 return ipmi::responseResponseError();
855 }
856 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800857 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700858 {
859 return ipmi::responseResponseError();
860 }
861
Hao Jiangd2afd052020-12-10 15:09:32 -0800862 // VR sensors are treated as a special case and we will not check the
863 // write permission for VR sensors, since they always deemed writable
864 // and permission table are not applied to VR sensors.
Patrick Williams1318a5e2024-08-16 15:19:54 -0400865 auto vrMode =
866 sensor::calculateVRMode(assertOffset, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800867 if (!vrMode)
868 {
869 return ipmi::responseResponseError();
870 }
871 boost::system::error_code ec = setDbusProperty(
872 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
873 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500874 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800875 // callback functions for now (e.g. ipmiSetSensorReading).
876 if (ec)
877 {
George Liude6694e2024-07-17 15:22:25 +0800878 lg2::error("Failed to set Selected, path: {PATH}, "
879 "interface: {INTERFACE}, ERROR: {ERROR}",
880 "PATH", path, "INTERFACE", sensor::sensorInterface,
881 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800882 }
883 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700884 }
885
George Liude6694e2024-07-17 15:22:25 +0800886 lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
Hao Jiangd2afd052020-12-10 15:09:32 -0800887 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700888}
889
Willy Tude54f482021-01-26 15:59:09 -0800890ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
891 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
892{
893 std::string connection;
894 std::string path;
895
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000896 if (sensnum == reservedSensorNumber)
897 {
898 return ipmi::responseInvalidFieldRequest();
899 }
900
Willy Tude54f482021-01-26 15:59:09 -0800901 auto status = getSensorConnection(ctx, sensnum, connection, path);
902 if (status)
903 {
904 return ipmi::response(status);
905 }
906
Scron Chang2703b022021-07-06 15:47:45 +0800907#ifdef FEATURE_HYBRID_SENSORS
908 if (auto sensor = findStaticSensor(path);
909 sensor != ipmi::sensor::sensors.end() &&
910 getSensorEventTypeFromPath(path) !=
911 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
912 {
913 if (ipmi::sensor::Mutability::Read !=
914 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
915 {
916 return ipmi::responseIllegalCommand();
917 }
918
919 uint8_t operation;
920 try
921 {
922 ipmi::sensor::GetSensorResponse getResponse =
923 sensor->second.getFunc(sensor->second);
924
925 if (getResponse.readingOrStateUnavailable)
926 {
927 operation |= static_cast<uint8_t>(
928 IPMISensorReadingByte2::readingStateUnavailable);
929 }
930 if (getResponse.scanningEnabled)
931 {
932 operation |= static_cast<uint8_t>(
933 IPMISensorReadingByte2::sensorScanningEnable);
934 }
935 if (getResponse.allEventMessagesEnabled)
936 {
937 operation |= static_cast<uint8_t>(
938 IPMISensorReadingByte2::eventMessagesEnable);
939 }
940 return ipmi::responseSuccess(
941 getResponse.reading, operation,
942 getResponse.thresholdLevelsStates,
943 getResponse.discreteReadingSensorStates);
944 }
945 catch (const std::exception& e)
946 {
947 operation |= static_cast<uint8_t>(
948 IPMISensorReadingByte2::readingStateUnavailable);
949 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
950 }
951 }
952#endif
953
Willy Tude54f482021-01-26 15:59:09 -0800954 DbusInterfaceMap sensorMap;
955 if (!getSensorMap(ctx, connection, path, sensorMap))
956 {
957 return ipmi::responseResponseError();
958 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800959 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800960
961 if (sensorObject == sensorMap.end() ||
962 sensorObject->second.find("Value") == sensorObject->second.end())
963 {
964 return ipmi::responseResponseError();
965 }
966 auto& valueVariant = sensorObject->second["Value"];
967 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
968
969 double max = 0;
970 double min = 0;
971 getSensorMaxMin(sensorMap, max, min);
972
973 int16_t mValue = 0;
974 int16_t bValue = 0;
975 int8_t rExp = 0;
976 int8_t bExp = 0;
977 bool bSigned = false;
978
979 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
980 {
981 return ipmi::responseResponseError();
982 }
983
Patrick Williams1318a5e2024-08-16 15:19:54 -0400984 uint8_t value =
985 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800986 uint8_t operation =
987 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
988 operation |=
989 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
990 bool notReading = std::isnan(reading);
991
992 if (!notReading)
993 {
994 auto availableObject =
995 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
996 if (availableObject != sensorMap.end())
997 {
998 auto findAvailable = availableObject->second.find("Available");
999 if (findAvailable != availableObject->second.end())
1000 {
1001 bool* available = std::get_if<bool>(&(findAvailable->second));
1002 if (available && !(*available))
1003 {
1004 notReading = true;
1005 }
1006 }
1007 }
1008 }
1009
1010 if (notReading)
1011 {
1012 operation |= static_cast<uint8_t>(
1013 IPMISensorReadingByte2::readingStateUnavailable);
1014 }
1015
Josh Lehana55c9532020-10-28 21:59:06 -07001016 if constexpr (details::enableInstrumentation)
1017 {
1018 int byteValue;
1019 if (bSigned)
1020 {
1021 byteValue = static_cast<int>(static_cast<int8_t>(value));
1022 }
1023 else
1024 {
1025 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1026 }
1027
1028 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001029 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1030 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001031 {
1032 // This is the first reading, show the coefficients
1033 double step = (max - min) / 255.0;
Haicheng Zhang041b3752025-07-14 17:13:45 +08001034 lg2::error(
1035 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, "
1036 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} "
1037 "bExp={BEXP} bSigned={BSIGNED}",
1038 "NAME",
1039 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum),
1040 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP",
1041 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned);
Josh Lehana55c9532020-10-28 21:59:06 -07001042 }
1043 }
1044
Willy Tude54f482021-01-26 15:59:09 -08001045 uint8_t thresholds = 0;
1046
1047 auto warningObject =
1048 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1049 if (warningObject != sensorMap.end())
1050 {
1051 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1052 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1053 if (alarmHigh != warningObject->second.end())
1054 {
1055 if (std::get<bool>(alarmHigh->second))
1056 {
1057 thresholds |= static_cast<uint8_t>(
1058 IPMISensorReadingByte3::upperNonCritical);
1059 }
1060 }
1061 if (alarmLow != warningObject->second.end())
1062 {
1063 if (std::get<bool>(alarmLow->second))
1064 {
1065 thresholds |= static_cast<uint8_t>(
1066 IPMISensorReadingByte3::lowerNonCritical);
1067 }
1068 }
1069 }
1070
1071 auto criticalObject =
1072 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1073 if (criticalObject != sensorMap.end())
1074 {
1075 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1076 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1077 if (alarmHigh != criticalObject->second.end())
1078 {
1079 if (std::get<bool>(alarmHigh->second))
1080 {
1081 thresholds |=
1082 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1083 }
1084 }
1085 if (alarmLow != criticalObject->second.end())
1086 {
1087 if (std::get<bool>(alarmLow->second))
1088 {
1089 thresholds |=
1090 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1091 }
1092 }
1093 }
1094
1095 // no discrete as of today so optional byte is never returned
1096 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1097}
1098
1099/** @brief implements the Set Sensor threshold command
1100 * @param sensorNumber - sensor number
1101 * @param lowerNonCriticalThreshMask
1102 * @param lowerCriticalThreshMask
1103 * @param lowerNonRecovThreshMask
1104 * @param upperNonCriticalThreshMask
1105 * @param upperCriticalThreshMask
1106 * @param upperNonRecovThreshMask
1107 * @param reserved
1108 * @param lowerNonCritical - lower non-critical threshold
1109 * @param lowerCritical - Lower critical threshold
1110 * @param lowerNonRecoverable - Lower non recovarable threshold
1111 * @param upperNonCritical - Upper non-critical threshold
1112 * @param upperCritical - Upper critical
1113 * @param upperNonRecoverable - Upper Non-recoverable
1114 *
1115 * @returns IPMI completion code
1116 */
1117ipmi::RspType<> ipmiSenSetSensorThresholds(
1118 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1119 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1120 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1121 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001122 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001123 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001124 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001125{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001126 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001127 {
1128 return ipmi::responseInvalidFieldRequest();
1129 }
1130
1131 // lower nc and upper nc not suppported on any sensor
1132 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1133 {
1134 return ipmi::responseInvalidFieldRequest();
1135 }
1136
1137 // if none of the threshold mask are set, nothing to do
1138 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1139 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1140 upperCriticalThreshMask | upperNonRecovThreshMask))
1141 {
1142 return ipmi::responseSuccess();
1143 }
1144
1145 std::string connection;
1146 std::string path;
1147
1148 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1149 if (status)
1150 {
1151 return ipmi::response(status);
1152 }
1153 DbusInterfaceMap sensorMap;
1154 if (!getSensorMap(ctx, connection, path, sensorMap))
1155 {
1156 return ipmi::responseResponseError();
1157 }
1158
1159 double max = 0;
1160 double min = 0;
1161 getSensorMaxMin(sensorMap, max, min);
1162
1163 int16_t mValue = 0;
1164 int16_t bValue = 0;
1165 int8_t rExp = 0;
1166 int8_t bExp = 0;
1167 bool bSigned = false;
1168
1169 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1170 {
1171 return ipmi::responseResponseError();
1172 }
1173
1174 // store a vector of property name, value to set, and interface
1175 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1176
1177 // define the indexes of the tuple
1178 constexpr uint8_t propertyName = 0;
1179 constexpr uint8_t thresholdValue = 1;
1180 constexpr uint8_t interface = 2;
1181 // verifiy all needed fields are present
1182 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1183 {
1184 auto findThreshold =
1185 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1186 if (findThreshold == sensorMap.end())
1187 {
1188 return ipmi::responseInvalidFieldRequest();
1189 }
1190 if (lowerCriticalThreshMask)
1191 {
1192 auto findLower = findThreshold->second.find("CriticalLow");
1193 if (findLower == findThreshold->second.end())
1194 {
1195 return ipmi::responseInvalidFieldRequest();
1196 }
1197 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1198 findThreshold->first);
1199 }
1200 if (upperCriticalThreshMask)
1201 {
1202 auto findUpper = findThreshold->second.find("CriticalHigh");
1203 if (findUpper == findThreshold->second.end())
1204 {
1205 return ipmi::responseInvalidFieldRequest();
1206 }
1207 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1208 findThreshold->first);
1209 }
1210 }
1211 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1212 {
1213 auto findThreshold =
1214 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1215 if (findThreshold == sensorMap.end())
1216 {
1217 return ipmi::responseInvalidFieldRequest();
1218 }
1219 if (lowerNonCriticalThreshMask)
1220 {
1221 auto findLower = findThreshold->second.find("WarningLow");
1222 if (findLower == findThreshold->second.end())
1223 {
1224 return ipmi::responseInvalidFieldRequest();
1225 }
1226 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1227 findThreshold->first);
1228 }
1229 if (upperNonCriticalThreshMask)
1230 {
1231 auto findUpper = findThreshold->second.find("WarningHigh");
1232 if (findUpper == findThreshold->second.end())
1233 {
1234 return ipmi::responseInvalidFieldRequest();
1235 }
1236 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1237 findThreshold->first);
1238 }
1239 }
1240 for (const auto& property : thresholdsToSet)
1241 {
1242 // from section 36.3 in the IPMI Spec, assume all linear
1243 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1244 (bValue * std::pow(10.0, bExp))) *
1245 std::pow(10.0, rExp);
1246 setDbusProperty(
1247 *getSdBus(), connection, path, std::get<interface>(property),
1248 std::get<propertyName>(property), ipmi::Value(valueToSet));
1249 }
1250 return ipmi::responseSuccess();
1251}
1252
1253IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1254{
1255 IPMIThresholds resp;
1256 auto warningInterface =
1257 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1258 auto criticalInterface =
1259 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1260
1261 if ((warningInterface != sensorMap.end()) ||
1262 (criticalInterface != sensorMap.end()))
1263 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001264 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001265
1266 if (sensorPair == sensorMap.end())
1267 {
1268 // should not have been able to find a sensor not implementing
1269 // the sensor object
1270 throw std::runtime_error("Invalid sensor map");
1271 }
1272
1273 double max = 0;
1274 double min = 0;
1275 getSensorMaxMin(sensorMap, max, min);
1276
1277 int16_t mValue = 0;
1278 int16_t bValue = 0;
1279 int8_t rExp = 0;
1280 int8_t bExp = 0;
1281 bool bSigned = false;
1282
1283 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1284 {
1285 throw std::runtime_error("Invalid sensor atrributes");
1286 }
1287 if (warningInterface != sensorMap.end())
1288 {
1289 auto& warningMap = warningInterface->second;
1290
1291 auto warningHigh = warningMap.find("WarningHigh");
1292 auto warningLow = warningMap.find("WarningLow");
1293
1294 if (warningHigh != warningMap.end())
1295 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001296 double value =
1297 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001298 if (std::isfinite(value))
1299 {
1300 resp.warningHigh = scaleIPMIValueFromDouble(
1301 value, mValue, rExp, bValue, bExp, bSigned);
1302 }
Willy Tude54f482021-01-26 15:59:09 -08001303 }
1304 if (warningLow != warningMap.end())
1305 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001306 double value =
1307 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001308 if (std::isfinite(value))
1309 {
1310 resp.warningLow = scaleIPMIValueFromDouble(
1311 value, mValue, rExp, bValue, bExp, bSigned);
1312 }
Willy Tude54f482021-01-26 15:59:09 -08001313 }
1314 }
1315 if (criticalInterface != sensorMap.end())
1316 {
1317 auto& criticalMap = criticalInterface->second;
1318
1319 auto criticalHigh = criticalMap.find("CriticalHigh");
1320 auto criticalLow = criticalMap.find("CriticalLow");
1321
1322 if (criticalHigh != criticalMap.end())
1323 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001324 double value =
1325 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001326 if (std::isfinite(value))
1327 {
1328 resp.criticalHigh = scaleIPMIValueFromDouble(
1329 value, mValue, rExp, bValue, bExp, bSigned);
1330 }
Willy Tude54f482021-01-26 15:59:09 -08001331 }
1332 if (criticalLow != criticalMap.end())
1333 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001334 double value =
1335 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001336 if (std::isfinite(value))
1337 {
1338 resp.criticalLow = scaleIPMIValueFromDouble(
1339 value, mValue, rExp, bValue, bExp, bSigned);
1340 }
Willy Tude54f482021-01-26 15:59:09 -08001341 }
1342 }
1343 }
1344 return resp;
1345}
1346
1347ipmi::RspType<uint8_t, // readable
1348 uint8_t, // lowerNCrit
1349 uint8_t, // lowerCrit
1350 uint8_t, // lowerNrecoverable
1351 uint8_t, // upperNC
1352 uint8_t, // upperCrit
1353 uint8_t> // upperNRecoverable
1354 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1355{
1356 std::string connection;
1357 std::string path;
1358
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001359 if (sensorNumber == reservedSensorNumber)
1360 {
1361 return ipmi::responseInvalidFieldRequest();
1362 }
1363
Willy Tude54f482021-01-26 15:59:09 -08001364 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1365 if (status)
1366 {
1367 return ipmi::response(status);
1368 }
1369
1370 DbusInterfaceMap sensorMap;
1371 if (!getSensorMap(ctx, connection, path, sensorMap))
1372 {
1373 return ipmi::responseResponseError();
1374 }
1375
1376 IPMIThresholds thresholdData;
1377 try
1378 {
1379 thresholdData = getIPMIThresholds(sensorMap);
1380 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001381 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001382 {
1383 return ipmi::responseResponseError();
1384 }
1385
1386 uint8_t readable = 0;
1387 uint8_t lowerNC = 0;
1388 uint8_t lowerCritical = 0;
1389 uint8_t lowerNonRecoverable = 0;
1390 uint8_t upperNC = 0;
1391 uint8_t upperCritical = 0;
1392 uint8_t upperNonRecoverable = 0;
1393
1394 if (thresholdData.warningHigh)
1395 {
1396 readable |=
1397 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1398 upperNC = *thresholdData.warningHigh;
1399 }
1400 if (thresholdData.warningLow)
1401 {
1402 readable |=
1403 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1404 lowerNC = *thresholdData.warningLow;
1405 }
1406
1407 if (thresholdData.criticalHigh)
1408 {
1409 readable |=
1410 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1411 upperCritical = *thresholdData.criticalHigh;
1412 }
1413 if (thresholdData.criticalLow)
1414 {
1415 readable |=
1416 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1417 lowerCritical = *thresholdData.criticalLow;
1418 }
1419
1420 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1421 lowerNonRecoverable, upperNC, upperCritical,
1422 upperNonRecoverable);
1423}
1424
1425/** @brief implements the get Sensor event enable command
1426 * @param sensorNumber - sensor number
1427 *
1428 * @returns IPMI completion code plus response data
1429 * - enabled - Sensor Event messages
1430 * - assertionEnabledLsb - Assertion event messages
1431 * - assertionEnabledMsb - Assertion event messages
1432 * - deassertionEnabledLsb - Deassertion event messages
1433 * - deassertionEnabledMsb - Deassertion event messages
1434 */
1435
1436ipmi::RspType<uint8_t, // enabled
1437 uint8_t, // assertionEnabledLsb
1438 uint8_t, // assertionEnabledMsb
1439 uint8_t, // deassertionEnabledLsb
1440 uint8_t> // deassertionEnabledMsb
1441 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1442{
1443 std::string connection;
1444 std::string path;
1445
1446 uint8_t enabled = 0;
1447 uint8_t assertionEnabledLsb = 0;
1448 uint8_t assertionEnabledMsb = 0;
1449 uint8_t deassertionEnabledLsb = 0;
1450 uint8_t deassertionEnabledMsb = 0;
1451
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001452 if (sensorNum == reservedSensorNumber)
1453 {
1454 return ipmi::responseInvalidFieldRequest();
1455 }
1456
Willy Tude54f482021-01-26 15:59:09 -08001457 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1458 if (status)
1459 {
1460 return ipmi::response(status);
1461 }
1462
Scron Chang2703b022021-07-06 15:47:45 +08001463#ifdef FEATURE_HYBRID_SENSORS
1464 if (auto sensor = findStaticSensor(path);
1465 sensor != ipmi::sensor::sensors.end() &&
1466 getSensorEventTypeFromPath(path) !=
1467 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1468 {
1469 enabled = static_cast<uint8_t>(
1470 IPMISensorEventEnableByte2::sensorScanningEnable);
1471 uint16_t assertionEnabled = 0;
1472 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1473 ->second.begin()
1474 ->second.second)
1475 {
1476 assertionEnabled |= (1 << offsetValMap.first);
1477 }
1478 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1479 assertionEnabledMsb =
1480 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1481
1482 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1483 assertionEnabledMsb, deassertionEnabledLsb,
1484 deassertionEnabledMsb);
1485 }
1486#endif
1487
Willy Tude54f482021-01-26 15:59:09 -08001488 DbusInterfaceMap sensorMap;
1489 if (!getSensorMap(ctx, connection, path, sensorMap))
1490 {
1491 return ipmi::responseResponseError();
1492 }
1493
1494 auto warningInterface =
1495 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1496 auto criticalInterface =
1497 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1498 if ((warningInterface != sensorMap.end()) ||
1499 (criticalInterface != sensorMap.end()))
1500 {
1501 enabled = static_cast<uint8_t>(
1502 IPMISensorEventEnableByte2::sensorScanningEnable);
1503 if (warningInterface != sensorMap.end())
1504 {
1505 auto& warningMap = warningInterface->second;
1506
1507 auto warningHigh = warningMap.find("WarningHigh");
1508 auto warningLow = warningMap.find("WarningLow");
1509 if (warningHigh != warningMap.end())
1510 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001511 double value =
1512 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001513 if (std::isfinite(value))
1514 {
1515 assertionEnabledLsb |= static_cast<uint8_t>(
1516 IPMISensorEventEnableThresholds::
1517 upperNonCriticalGoingHigh);
1518 deassertionEnabledLsb |= static_cast<uint8_t>(
1519 IPMISensorEventEnableThresholds::
1520 upperNonCriticalGoingLow);
1521 }
Willy Tude54f482021-01-26 15:59:09 -08001522 }
1523 if (warningLow != warningMap.end())
1524 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001525 double value =
1526 std::visit(VariantToDoubleVisitor(), warningLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001527 if (std::isfinite(value))
1528 {
1529 assertionEnabledLsb |= static_cast<uint8_t>(
1530 IPMISensorEventEnableThresholds::
1531 lowerNonCriticalGoingLow);
1532 deassertionEnabledLsb |= static_cast<uint8_t>(
1533 IPMISensorEventEnableThresholds::
1534 lowerNonCriticalGoingHigh);
1535 }
Willy Tude54f482021-01-26 15:59:09 -08001536 }
1537 }
1538 if (criticalInterface != sensorMap.end())
1539 {
1540 auto& criticalMap = criticalInterface->second;
1541
1542 auto criticalHigh = criticalMap.find("CriticalHigh");
1543 auto criticalLow = criticalMap.find("CriticalLow");
1544
1545 if (criticalHigh != criticalMap.end())
1546 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001547 double value =
1548 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001549 if (std::isfinite(value))
1550 {
1551 assertionEnabledMsb |= static_cast<uint8_t>(
1552 IPMISensorEventEnableThresholds::
1553 upperCriticalGoingHigh);
1554 deassertionEnabledMsb |= static_cast<uint8_t>(
1555 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1556 }
Willy Tude54f482021-01-26 15:59:09 -08001557 }
1558 if (criticalLow != criticalMap.end())
1559 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001560 double value =
1561 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001562 if (std::isfinite(value))
1563 {
1564 assertionEnabledLsb |= static_cast<uint8_t>(
1565 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1566 deassertionEnabledLsb |= static_cast<uint8_t>(
1567 IPMISensorEventEnableThresholds::
1568 lowerCriticalGoingHigh);
1569 }
Willy Tude54f482021-01-26 15:59:09 -08001570 }
1571 }
1572 }
1573
1574 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1575 assertionEnabledMsb, deassertionEnabledLsb,
1576 deassertionEnabledMsb);
1577}
1578
1579/** @brief implements the get Sensor event status command
1580 * @param sensorNumber - sensor number, FFh = reserved
1581 *
1582 * @returns IPMI completion code plus response data
1583 * - sensorEventStatus - Sensor Event messages state
1584 * - assertions - Assertion event messages
1585 * - deassertions - Deassertion event messages
1586 */
1587ipmi::RspType<uint8_t, // sensorEventStatus
1588 std::bitset<16>, // assertions
1589 std::bitset<16> // deassertion
1590 >
1591 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1592{
1593 if (sensorNum == reservedSensorNumber)
1594 {
1595 return ipmi::responseInvalidFieldRequest();
1596 }
1597
1598 std::string connection;
1599 std::string path;
1600 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1601 if (status)
1602 {
George Liude6694e2024-07-17 15:22:25 +08001603 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1604 "sensor number: {SENSOR_NUM}",
1605 "SENSOR_NUM", sensorNum);
Willy Tude54f482021-01-26 15:59:09 -08001606 return ipmi::response(status);
1607 }
1608
Scron Chang2703b022021-07-06 15:47:45 +08001609#ifdef FEATURE_HYBRID_SENSORS
1610 if (auto sensor = findStaticSensor(path);
1611 sensor != ipmi::sensor::sensors.end() &&
1612 getSensorEventTypeFromPath(path) !=
1613 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1614 {
1615 auto response = ipmi::sensor::get::mapDbusToAssertion(
1616 sensor->second, path, sensor->second.sensorInterface);
1617 std::bitset<16> assertions;
1618 // deassertions are not used.
1619 std::bitset<16> deassertions = 0;
1620 uint8_t sensorEventStatus;
1621 if (response.readingOrStateUnavailable)
1622 {
1623 sensorEventStatus |= static_cast<uint8_t>(
1624 IPMISensorReadingByte2::readingStateUnavailable);
1625 }
1626 if (response.scanningEnabled)
1627 {
1628 sensorEventStatus |= static_cast<uint8_t>(
1629 IPMISensorReadingByte2::sensorScanningEnable);
1630 }
1631 if (response.allEventMessagesEnabled)
1632 {
1633 sensorEventStatus |= static_cast<uint8_t>(
1634 IPMISensorReadingByte2::eventMessagesEnable);
1635 }
1636 assertions |= response.discreteReadingSensorStates << 8;
1637 assertions |= response.thresholdLevelsStates;
1638 return ipmi::responseSuccess(sensorEventStatus, assertions,
1639 deassertions);
1640 }
1641#endif
1642
Willy Tude54f482021-01-26 15:59:09 -08001643 DbusInterfaceMap sensorMap;
1644 if (!getSensorMap(ctx, connection, path, sensorMap))
1645 {
George Liude6694e2024-07-17 15:22:25 +08001646 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1647 "sensor path: {SENSOR_PATH}",
1648 "SENSOR_PATH", path);
Willy Tude54f482021-01-26 15:59:09 -08001649 return ipmi::responseResponseError();
1650 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001651
1652 uint8_t sensorEventStatus =
1653 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1654 std::bitset<16> assertions = 0;
1655 std::bitset<16> deassertions = 0;
1656
1657 // handle VR typed sensor
1658 auto vrInterface = sensorMap.find(sensor::vrInterface);
1659 if (vrInterface != sensorMap.end())
1660 {
1661 if (!sensor::getVrEventStatus(ctx, connection, path,
1662 vrInterface->second, assertions))
1663 {
1664 return ipmi::responseResponseError();
1665 }
1666
1667 // both Event Message and Sensor Scanning are disable for VR.
1668 sensorEventStatus = 0;
1669 return ipmi::responseSuccess(sensorEventStatus, assertions,
1670 deassertions);
1671 }
1672
Willy Tude54f482021-01-26 15:59:09 -08001673 auto warningInterface =
1674 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1675 auto criticalInterface =
1676 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1677
Willy Tude54f482021-01-26 15:59:09 -08001678 std::optional<bool> criticalDeassertHigh =
1679 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1680 std::optional<bool> criticalDeassertLow =
1681 thresholdDeassertMap[path]["CriticalAlarmLow"];
1682 std::optional<bool> warningDeassertHigh =
1683 thresholdDeassertMap[path]["WarningAlarmHigh"];
1684 std::optional<bool> warningDeassertLow =
1685 thresholdDeassertMap[path]["WarningAlarmLow"];
1686
Willy Tude54f482021-01-26 15:59:09 -08001687 if (criticalDeassertHigh && !*criticalDeassertHigh)
1688 {
1689 deassertions.set(static_cast<size_t>(
1690 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1691 }
1692 if (criticalDeassertLow && !*criticalDeassertLow)
1693 {
1694 deassertions.set(static_cast<size_t>(
1695 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1696 }
1697 if (warningDeassertHigh && !*warningDeassertHigh)
1698 {
1699 deassertions.set(static_cast<size_t>(
1700 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1701 }
1702 if (warningDeassertLow && !*warningDeassertLow)
1703 {
1704 deassertions.set(static_cast<size_t>(
1705 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1706 }
1707 if ((warningInterface != sensorMap.end()) ||
1708 (criticalInterface != sensorMap.end()))
1709 {
1710 sensorEventStatus = static_cast<size_t>(
1711 IPMISensorEventEnableByte2::eventMessagesEnable);
1712 if (warningInterface != sensorMap.end())
1713 {
1714 auto& warningMap = warningInterface->second;
1715
1716 auto warningHigh = warningMap.find("WarningAlarmHigh");
1717 auto warningLow = warningMap.find("WarningAlarmLow");
1718 auto warningHighAlarm = false;
1719 auto warningLowAlarm = false;
1720
1721 if (warningHigh != warningMap.end())
1722 {
1723 warningHighAlarm = std::get<bool>(warningHigh->second);
1724 }
1725 if (warningLow != warningMap.end())
1726 {
1727 warningLowAlarm = std::get<bool>(warningLow->second);
1728 }
1729 if (warningHighAlarm)
1730 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001731 assertions.set(static_cast<size_t>(
1732 IPMIGetSensorEventEnableThresholds::
1733 upperNonCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001734 }
1735 if (warningLowAlarm)
1736 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001737 assertions.set(static_cast<size_t>(
1738 IPMIGetSensorEventEnableThresholds::
1739 lowerNonCriticalGoingLow));
Willy Tude54f482021-01-26 15:59:09 -08001740 }
1741 }
1742 if (criticalInterface != sensorMap.end())
1743 {
1744 auto& criticalMap = criticalInterface->second;
1745
1746 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1747 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1748 auto criticalHighAlarm = false;
1749 auto criticalLowAlarm = false;
1750
1751 if (criticalHigh != criticalMap.end())
1752 {
1753 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1754 }
1755 if (criticalLow != criticalMap.end())
1756 {
1757 criticalLowAlarm = std::get<bool>(criticalLow->second);
1758 }
1759 if (criticalHighAlarm)
1760 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001761 assertions.set(static_cast<size_t>(
1762 IPMIGetSensorEventEnableThresholds::
1763 upperCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001764 }
1765 if (criticalLowAlarm)
1766 {
1767 assertions.set(static_cast<size_t>(
1768 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1769 }
1770 }
1771 }
1772
1773 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1774}
1775
Willy Tu38e7a2b2021-03-29 15:09:56 -07001776// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001777void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1778 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001779{
Willy Tu38e7a2b2021-03-29 15:09:56 -07001780 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1781 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1782
George Liuc7a4da52025-08-19 16:20:31 +08001783 record.header.recordId = recordID;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001784 record.header.sdr_version = ipmiSdrVersion;
1785 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1786 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1787 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001788 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001789 record.key.owner_lun = lun;
1790 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001791}
Willy Tu4eca2512022-06-20 21:14:51 -07001792bool constructSensorSdr(
1793 ipmi::Context::ptr ctx,
1794 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1795 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1796 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001797{
Hao Jiange39d4d82021-04-16 17:02:40 -07001798 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1799
1800 DbusInterfaceMap sensorMap;
1801 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1802 {
George Liude6694e2024-07-17 15:22:25 +08001803 lg2::error("Failed to update sensor map for threshold sensor, "
1804 "service: {SERVICE}, path: {PATH}",
1805 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07001806 return false;
1807 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001808
1809 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1810 record.body.sensor_type = getSensorTypeFromPath(path);
1811 std::string type = getSensorTypeStringFromPath(path);
1812 auto typeCstr = type.c_str();
1813 auto findUnits = sensorUnits.find(typeCstr);
1814 if (findUnits != sensorUnits.end())
1815 {
1816 record.body.sensor_units_2_base =
1817 static_cast<uint8_t>(findUnits->second);
1818 } // else default 0x0 unspecified
1819
1820 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1821
Hao Jiangd2afd052020-12-10 15:09:32 -08001822 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001823 if (sensorObject == sensorMap.end())
1824 {
George Liude6694e2024-07-17 15:22:25 +08001825 lg2::error("constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001826 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001827 }
1828
1829 uint8_t entityId = 0;
1830 uint8_t entityInstance = 0x01;
1831
1832 // follow the association chain to get the parent board's entityid and
1833 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001834 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1835 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001836
1837 record.body.entity_id = entityId;
1838 record.body.entity_instance = entityInstance;
1839
Shakeeb Pasha93889722021-10-14 10:20:13 +05301840 double max = 0;
1841 double min = 0;
1842 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001843
1844 int16_t mValue = 0;
1845 int8_t rExp = 0;
1846 int16_t bValue = 0;
1847 int8_t bExp = 0;
1848 bool bSigned = false;
1849
1850 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1851 {
George Liude6694e2024-07-17 15:22:25 +08001852 lg2::error("constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001853 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001854 }
1855
1856 // The record.body is a struct SensorDataFullRecordBody
1857 // from sensorhandler.hpp in phosphor-ipmi-host.
1858 // The meaning of these bits appears to come from
1859 // table 43.1 of the IPMI spec.
1860 // The above 5 sensor attributes are stuffed in as follows:
1861 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1862 // Byte 22-24 are for other purposes
1863 // Byte 25 = MMMMMMMM = LSB of M
1864 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1865 // Byte 27 = BBBBBBBB = LSB of B
1866 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1867 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1868 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1869
1870 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1871 record.body.m_lsb = mValue & 0xFF;
1872
1873 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1874 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1875
1876 // move the smallest bit of the MSB into place (bit 9)
1877 // the MSbs are bits 7:8 in m_msb_and_tolerance
1878 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1879
1880 record.body.b_lsb = bValue & 0xFF;
1881
1882 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1883 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1884
1885 // move the smallest bit of the MSB into place (bit 9)
1886 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1887 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1888
1889 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1890 uint8_t rExpBits = rExp & 0x07;
1891
1892 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1893 uint8_t bExpBits = bExp & 0x07;
1894
1895 // move rExp and bExp into place
Patrick Williams1318a5e2024-08-16 15:19:54 -04001896 record.body.r_b_exponents =
1897 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001898
1899 // Set the analog reading byte interpretation accordingly
1900 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1901
1902 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1903 // These seem redundant, but derivable from the above 5 attributes
1904 // Original comment said "todo fill out rest of units"
1905
1906 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001907 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001908 get_sdr::body::set_id_strlen(name.size(), &record.body);
1909 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001910 std::memcpy(record.body.id_string, name.c_str(),
1911 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001912
Josh Lehana55c9532020-10-28 21:59:06 -07001913 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001914 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001915
Jie Yangf0a89942021-07-29 15:30:25 -07001916 bool sensorSettable = false;
1917 auto mutability =
1918 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1919 if (mutability != sensorMap.end())
1920 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001921 sensorSettable =
1922 mappedVariant<bool>(mutability->second, "Mutable", false);
Jie Yangf0a89942021-07-29 15:30:25 -07001923 }
1924 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1925
1926 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001927 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001928
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001929 IPMIThresholds thresholdData;
1930 try
1931 {
1932 thresholdData = getIPMIThresholds(sensorMap);
1933 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001934 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001935 {
George Liude6694e2024-07-17 15:22:25 +08001936 lg2::error("constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001937 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001938 }
1939
1940 if (thresholdData.criticalHigh)
1941 {
1942 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1943 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1944 IPMISensorEventEnableThresholds::criticalThreshold);
1945 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1946 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1947 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1948 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1949 record.body.discrete_reading_setting_mask[0] |=
1950 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1951 }
1952 if (thresholdData.warningHigh)
1953 {
1954 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1955 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1956 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1957 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1958 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1959 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1960 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1961 record.body.discrete_reading_setting_mask[0] |=
1962 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1963 }
1964 if (thresholdData.criticalLow)
1965 {
1966 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1967 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1968 IPMISensorEventEnableThresholds::criticalThreshold);
1969 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1970 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1971 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1972 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1973 record.body.discrete_reading_setting_mask[0] |=
1974 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1975 }
1976 if (thresholdData.warningLow)
1977 {
1978 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1979 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1980 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1981 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1982 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1983 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1984 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1985 record.body.discrete_reading_setting_mask[0] |=
1986 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1987 }
1988
1989 // everything that is readable is setable
1990 record.body.discrete_reading_setting_mask[1] =
1991 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001992 return true;
1993}
1994
Scron Chang2703b022021-07-06 15:47:45 +08001995#ifdef FEATURE_HYBRID_SENSORS
1996// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001997void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001998 uint16_t recordID,
1999 ipmi::sensor::IdInfoMap::const_iterator sensor,
2000 get_sdr::SensorDataFullRecord& record)
2001{
2002 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2003
2004 record.body.entity_id = sensor->second.entityType;
2005 record.body.sensor_type = sensor->second.sensorType;
2006 record.body.event_reading_type = sensor->second.sensorReadingType;
2007 record.body.entity_instance = sensor->second.instance;
2008 if (ipmi::sensor::Mutability::Write ==
2009 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2010 {
2011 get_sdr::body::init_settable_state(true, &(record.body));
2012 }
2013
2014 auto id_string = sensor->second.sensorName;
2015
2016 if (id_string.empty())
2017 {
2018 id_string = sensor->second.sensorNameFunc(sensor->second);
2019 }
2020
2021 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2022 {
2023 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2024 &(record.body));
2025 }
2026 else
2027 {
2028 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2029 }
Paul Fertser51136982022-08-18 12:36:41 +00002030 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002031 std::strncpy(record.body.id_string, id_string.c_str(),
2032 get_sdr::body::get_id_strlen(&(record.body)));
2033}
2034#endif
2035
Hao Jiange39d4d82021-04-16 17:02:40 -07002036// Construct type 3 SDR header and key (for VR and other discrete sensors)
2037void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2038 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002039{
2040 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2041 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2042
George Liuc7a4da52025-08-19 16:20:31 +08002043 record.header.recordId = recordID;
Willy Tu61992ad2021-03-29 15:33:20 -07002044 record.header.sdr_version = ipmiSdrVersion;
2045 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2046 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2047 sizeof(get_sdr::SensorDataRecordHeader);
2048 record.key.owner_id = bmcI2CAddr;
2049 record.key.owner_lun = lun;
2050 record.key.sensor_number = sensornumber;
2051
2052 record.body.entity_id = 0x00;
2053 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002054}
Willy Tu61992ad2021-03-29 15:33:20 -07002055
Hao Jiange39d4d82021-04-16 17:02:40 -07002056// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002057bool constructVrSdr(ipmi::Context::ptr ctx,
2058 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2059 uint16_t sensorNum, uint16_t recordID,
2060 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002061 get_sdr::SensorDataEventRecord& record)
2062{
Hao Jiange39d4d82021-04-16 17:02:40 -07002063 constructEventSdrHeaderKey(sensorNum, recordID, record);
2064
2065 DbusInterfaceMap sensorMap;
2066 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2067 {
George Liude6694e2024-07-17 15:22:25 +08002068 lg2::error("Failed to update sensor map for VR sensor, "
2069 "service: {SERVICE}, path: {PATH}",
2070 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07002071 return false;
2072 }
Willy Tu61992ad2021-03-29 15:33:20 -07002073 // follow the association chain to get the parent board's entityid and
2074 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002075 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2076 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002077 record.body.entity_instance);
2078
2079 // Sensor type is hardcoded as a module/board type instead of parsing from
2080 // sensor path. This is because VR control is allocated in an independent
2081 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2082 // types.
George Liuf2807ed2025-04-03 15:52:57 +08002083 static constexpr const uint8_t moduleBoardType = 0x15;
2084 record.body.sensor_type = moduleBoardType;
Willy Tu61992ad2021-03-29 15:33:20 -07002085 record.body.event_reading_type = 0x00;
2086
2087 record.body.sensor_record_sharing_1 = 0x00;
2088 record.body.sensor_record_sharing_2 = 0x00;
2089
2090 // populate sensor name from path
2091 auto name = sensor::parseSdrIdFromPath(path);
2092 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002093 get_sdr::body::set_id_strlen(nameSize, &record.body);
2094 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002095 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2096 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2097
2098 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002099 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002100
2101 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002102}
2103
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002104uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002105{
2106 return std::min(getSensorTree().size(), maxIPMISensors);
2107}
2108
Willy Tu4eca2512022-06-20 21:14:51 -07002109static int getSensorDataRecord(
2110 ipmi::Context::ptr ctx,
2111 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2112 std::vector<uint8_t>& recordData, uint16_t recordID,
2113 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002114{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302115 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002116 size_t lastRecord = ipmi::getNumberOfSensors() +
2117 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2118 uint16_t nextRecord(recordID + 1);
2119
Willy Tu38e7a2b2021-03-29 15:09:56 -07002120 if (recordID == lastRecordIndex)
2121 {
2122 recordID = lastRecord;
2123 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002124 if (recordID == lastRecord)
2125 {
2126 nextRecord = lastRecordIndex;
2127 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002128 if (recordID > lastRecord)
2129 {
George Liude6694e2024-07-17 15:22:25 +08002130 lg2::error("getSensorDataRecord: recordID > lastRecord error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002131 return GENERAL_ERROR;
2132 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002133 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002134 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002135 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2136 recordData);
2137 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002138 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002139 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002140 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002141 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002142 }
2143
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002144 // Perform a incremental scan of the SDR Record ID's and translate the
2145 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2146 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2147 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2148 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002149 std::string connection;
2150 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002151 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002152 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002153 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002154 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002155 // LUN 0 has one reserved sensor number. Compensate here by adding one
2156 // to the record ID
2157 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002158 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002159 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002160 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002161 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002162 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2163 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2164 // rules governing its use.
2165 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002166 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002167 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002168
Patrick Williams1318a5e2024-08-16 15:19:54 -04002169 auto status =
2170 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2171 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002172 if (status)
2173 {
George Liude6694e2024-07-17 15:22:25 +08002174 lg2::error("getSensorDataRecord: getSensorConnection error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002175 return GENERAL_ERROR;
2176 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002177 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002178 // Return an error on LUN 2 assingments, and any sensor number beyond the
2179 // range of LUN 3
2180 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2181 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002182 {
George Liude6694e2024-07-17 15:22:25 +08002183 lg2::error("getSensorDataRecord: invalidSensorNumber");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002184 return GENERAL_ERROR;
2185 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002186 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2187 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2188
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002189 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2190 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002191 {
George Liude6694e2024-07-17 15:22:25 +08002192 lg2::error("getSensorDataRecord: sensor record mismatch");
Johnathan Manteyce982772021-07-28 15:08:30 -07002193 return GENERAL_ERROR;
2194 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002195
Willy Tu38e7a2b2021-03-29 15:09:56 -07002196 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002197 if (std::find(interfaces.begin(), interfaces.end(),
2198 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002199 {
Willy Tu11d68892022-01-20 10:37:34 -08002200 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002201
Hao Jiange39d4d82021-04-16 17:02:40 -07002202 // If the request doesn't read SDR body, construct only header and key
2203 // part to avoid additional DBus transaction.
2204 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2205 {
2206 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2207 }
Willy Tu4eca2512022-06-20 21:14:51 -07002208 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2209 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002210 {
2211 return GENERAL_ERROR;
2212 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002213
selvaganapathi7b2e5502023-02-14 07:10:47 +05302214 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2215 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002216
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002217 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002218 }
Willy Tu61992ad2021-03-29 15:33:20 -07002219
Scron Chang2703b022021-07-06 15:47:45 +08002220#ifdef FEATURE_HYBRID_SENSORS
2221 if (auto sensor = findStaticSensor(path);
2222 sensor != ipmi::sensor::sensors.end() &&
2223 getSensorEventTypeFromPath(path) !=
2224 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2225 {
Willy Tu11d68892022-01-20 10:37:34 -08002226 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002227
2228 // If the request doesn't read SDR body, construct only header and key
2229 // part to avoid additional DBus transaction.
2230 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2231 {
2232 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2233 }
2234 else
2235 {
2236 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2237 }
2238
selvaganapathi7b2e5502023-02-14 07:10:47 +05302239 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2240 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002241
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002242 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002243 }
2244#endif
2245
Willy Tu61992ad2021-03-29 15:33:20 -07002246 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002247 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2248 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002249 {
Willy Tu11d68892022-01-20 10:37:34 -08002250 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002251
Hao Jiange39d4d82021-04-16 17:02:40 -07002252 // If the request doesn't read SDR body, construct only header and key
2253 // part to avoid additional DBus transaction.
2254 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2255 {
2256 constructEventSdrHeaderKey(sensorNum, recordID, record);
2257 }
Willy Tu4eca2512022-06-20 21:14:51 -07002258 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2259 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002260 {
2261 return GENERAL_ERROR;
2262 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302263 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2264 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002265 }
2266
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002267 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002268}
2269
2270/** @brief implements the get SDR Info command
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002271 * @param operation : 0 or not supplied returns sensor count
2272 * 1 return SDR count
Willy Tude54f482021-01-26 15:59:09 -08002273 *
2274 * @returns IPMI completion code plus response data
2275 * - sdrCount - sensor/SDR count
2276 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2277 */
2278static ipmi::RspType<uint8_t, // respcount
2279 uint8_t, // dynamic population flags
2280 uint32_t // last time a sensor was added
2281 >
2282 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002283 std::optional<uint8_t> operation)
Willy Tude54f482021-01-26 15:59:09 -08002284{
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002285 auto& sensorTree{getSensorTree()};
2286 uint8_t sdrCount{};
2287 // Sensors are dynamically allocated
2288 uint8_t lunsAndDynamicPopulation{0x80};
2289 constexpr uint8_t getSdrCount{1};
2290 constexpr uint8_t getSensorCount{0};
Willy Tude54f482021-01-26 15:59:09 -08002291
2292 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2293 {
2294 return ipmi::responseResponseError();
2295 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002296 uint16_t numSensors{ipmi::getNumberOfSensors()};
2297 if (operation.value_or(0) == getSdrCount)
Willy Tude54f482021-01-26 15:59:09 -08002298 {
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002299 sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
Willy Tude54f482021-01-26 15:59:09 -08002300 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002301 else if (operation.value_or(0) == getSensorCount)
Willy Tude54f482021-01-26 15:59:09 -08002302 {
2303 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002304 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002305 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002306 sdrCount =
2307 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002308 }
Harvey Wu75893062023-03-22 17:17:31 +08002309 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002310 {
2311 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2312 ? maxSensorsPerLUN
2313 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2314 }
Harvey Wu75893062023-03-22 17:17:31 +08002315 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002316 {
2317 if (numSensors <= maxIPMISensors)
2318 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002319 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2320 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002321 }
2322 else
2323 {
Willy Tude54f482021-01-26 15:59:09 -08002324 throw std::out_of_range(
2325 "Maximum number of IPMI sensors exceeded.");
2326 }
2327 }
2328 }
2329 else
2330 {
2331 return ipmi::responseInvalidFieldRequest();
2332 }
2333
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002334 // Flag which LUNs have sensors associated
Willy Tude54f482021-01-26 15:59:09 -08002335 if (numSensors > 0)
2336 {
2337 lunsAndDynamicPopulation |= 1;
2338 }
2339 if (numSensors > maxSensorsPerLUN)
2340 {
2341 lunsAndDynamicPopulation |= 2;
2342 }
2343 if (numSensors >= (maxSensorsPerLUN * 2))
2344 {
2345 lunsAndDynamicPopulation |= 8;
2346 }
2347 if (numSensors > maxIPMISensors)
2348 {
Willy Tude54f482021-01-26 15:59:09 -08002349 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2350 }
2351
2352 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2353 sdrLastAdd);
2354}
2355
2356/* end sensor commands */
2357
2358/* storage commands */
2359
2360ipmi::RspType<uint8_t, // sdr version
2361 uint16_t, // record count
2362 uint16_t, // free space
2363 uint32_t, // most recent addition
2364 uint32_t, // most recent erase
2365 uint8_t // operationSupport
2366 >
2367 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2368{
Willy Tude54f482021-01-26 15:59:09 -08002369 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Patrick Williams1318a5e2024-08-16 15:19:54 -04002370 uint16_t recordCount =
2371 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002372
2373 uint8_t operationSupport = static_cast<uint8_t>(
2374 SdrRepositoryInfoOps::overflow); // write not supported
2375
2376 operationSupport |=
2377 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2378 operationSupport |= static_cast<uint8_t>(
2379 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2380 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2381 unspecifiedFreeSpace, sdrLastAdd,
2382 sdrLastRemove, operationSupport);
2383}
2384
2385/** @brief implements the get SDR allocation info command
2386 *
2387 * @returns IPMI completion code plus response data
2388 * - allocUnits - Number of possible allocation units
2389 * - allocUnitSize - Allocation unit size in bytes.
2390 * - allocUnitFree - Number of free allocation units
2391 * - allocUnitLargestFree - Largest free block in allocation units
2392 * - maxRecordSize - Maximum record size in allocation units.
2393 */
2394ipmi::RspType<uint16_t, // allocUnits
2395 uint16_t, // allocUnitSize
2396 uint16_t, // allocUnitFree
2397 uint16_t, // allocUnitLargestFree
2398 uint8_t // maxRecordSize
2399 >
2400 ipmiStorageGetSDRAllocationInfo()
2401{
2402 // 0000h unspecified number of alloc units
2403 constexpr uint16_t allocUnits = 0;
2404
2405 constexpr uint16_t allocUnitFree = 0;
2406 constexpr uint16_t allocUnitLargestFree = 0;
2407 // only allow one block at a time
2408 constexpr uint8_t maxRecordSize = 1;
2409
2410 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2411 allocUnitLargestFree, maxRecordSize);
2412}
2413
2414/** @brief implements the reserve SDR command
2415 * @returns IPMI completion code plus response data
2416 * - sdrReservationID
2417 */
2418ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2419{
2420 sdrReservationID++;
2421 if (sdrReservationID == 0)
2422 {
2423 sdrReservationID++;
2424 }
2425
2426 return ipmi::responseSuccess(sdrReservationID);
2427}
2428
2429ipmi::RspType<uint16_t, // next record ID
2430 std::vector<uint8_t> // payload
2431 >
2432 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2433 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2434{
2435 // reservation required for partial reads with non zero offset into
2436 // record
2437 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2438 {
George Liude6694e2024-07-17 15:22:25 +08002439 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002440 return ipmi::responseInvalidReservationId();
2441 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002442
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002443 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002444 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002445 {
George Liude6694e2024-07-17 15:22:25 +08002446 lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002447 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002448 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002449
Willy Tu4eca2512022-06-20 21:14:51 -07002450 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2451
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002452 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002453 int nextRecordId = getSensorDataRecord(
2454 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2455 record, recordID, offset + bytesToRead);
2456
2457 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002458 {
George Liude6694e2024-07-17 15:22:25 +08002459 lg2::error("ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002460 return ipmi::responseInvalidFieldRequest();
2461 }
Willy Tude54f482021-01-26 15:59:09 -08002462 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002463 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002464 if (!hdr)
2465 {
George Liude6694e2024-07-17 15:22:25 +08002466 lg2::error("ipmiStorageGetSDR: record header is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002467 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002468 }
2469
Patrick Williams1318a5e2024-08-16 15:19:54 -04002470 size_t sdrLength =
2471 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002472 if (offset >= sdrLength)
2473 {
George Liude6694e2024-07-17 15:22:25 +08002474 lg2::error("ipmiStorageGetSDR: offset is outside the record");
Vernon Mauery6dbea082023-07-21 11:43:00 -07002475 return ipmi::responseParmOutOfRange();
2476 }
Willy Tude54f482021-01-26 15:59:09 -08002477 if (sdrLength < (offset + bytesToRead))
2478 {
2479 bytesToRead = sdrLength - offset;
2480 }
2481
2482 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2483 if (!respStart)
2484 {
George Liude6694e2024-07-17 15:22:25 +08002485 lg2::error("ipmiStorageGetSDR: record is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002486 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002487 }
2488
2489 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002490
Willy Tude54f482021-01-26 15:59:09 -08002491 return ipmi::responseSuccess(nextRecordId, recordData);
2492}
adarshgrami042e9db2022-09-15 10:34:34 +05302493namespace dcmi
2494{
2495
Thang Tranb1416ef2023-08-02 13:57:09 +07002496std::tuple<uint8_t, // Total of instance sensors
2497 std::vector<sensorInfo> // The list of sensors
2498 >
2499 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2500 uint8_t entityInstance, uint8_t instanceStart)
2501{
2502 std::vector<sensorInfo> sensorList;
2503 uint8_t totalInstSensor = 0;
2504 auto match = ipmi::dcmi::validEntityId.find(entityId);
2505
2506 if (match == ipmi::dcmi::validEntityId.end())
2507 {
2508 return std::make_tuple(totalInstSensor, sensorList);
2509 }
2510
2511 auto& sensorTree = getSensorTree();
2512 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2513 {
2514 return std::make_tuple(totalInstSensor, sensorList);
2515 }
2516
2517 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2518
Willy Tu62e3ca82024-01-31 17:28:34 +00002519 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002520 for (const auto& sensor : sensorTree)
2521 {
2522 const std::string& sensorObjPath = sensor.first;
2523 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2524
2525 /*
2526 * In the DCMI specification, it only supports the sensor type is 0x01
2527 * (temperature type) for both Get Sensor Info and Get Temperature
2528 * Readings commands.
2529 */
2530 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2531 {
2532 continue;
2533 }
2534
2535 const auto& connection = sensor.second.begin()->first;
2536 DbusInterfaceMap sensorMap;
2537
2538 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2539 sensorMapSdrUpdatePeriod))
2540 {
George Liude6694e2024-07-17 15:22:25 +08002541 lg2::error("Failed to update sensor map for threshold sensor, "
2542 "service: {SERVICE}, path: {PATH}",
2543 "SERVICE", connection, "PATH", sensorObjPath);
Thang Tranb1416ef2023-08-02 13:57:09 +07002544 continue;
2545 }
2546
2547 uint8_t entityIdValue = 0;
2548 uint8_t entityInstanceValue = 0;
2549
2550 /*
2551 * Get the Entity ID, Entity Instance information which are configured
2552 * in the Entity-Manger.
2553 */
2554 updateIpmiFromAssociation(
2555 sensorObjPath,
2556 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2557 sensorMap, entityIdValue, entityInstanceValue);
2558
2559 if (entityIdValue == match->first || entityIdValue == match->second)
2560 {
2561 totalInstSensor++;
2562
2563 /*
2564 * When Entity Instance parameter is not 0, we only get the first
2565 * sensor whose Entity Instance number is equal input Entity
2566 * Instance parameter.
2567 */
2568 if (entityInstance)
2569 {
2570 if (!sensorList.empty())
2571 {
2572 continue;
2573 }
2574
2575 if (entityInstanceValue == entityInstance)
2576 {
2577 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002578 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002579 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002580 ++invalidSensorNumberErrCount;
2581 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002582 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002583 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2584 recordId, entityIdValue,
2585 entityInstanceValue);
2586 }
2587 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002588 else if (entityInstanceValue >= instanceStart)
2589 {
2590 auto recordId = getSensorNumberFromPath(sensorObjPath);
2591 if (recordId == invalidSensorNumber)
2592 {
2593 ++invalidSensorNumberErrCount;
2594 continue;
2595 }
2596 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2597 recordId, entityIdValue,
2598 entityInstanceValue);
2599 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002600 }
2601 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002602 if (invalidSensorNumberErrCount != 0)
2603 {
George Liude6694e2024-07-17 15:22:25 +08002604 lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2605 "{ERR_COUNT} times",
2606 "ERR_COUNT", invalidSensorNumberErrCount);
Willy Tu62e3ca82024-01-31 17:28:34 +00002607 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002608
2609 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2610 return first.entityInstance <= second.entityInstance;
2611 };
2612
2613 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2614
2615 return std::make_tuple(totalInstSensor, sensorList);
2616}
2617
Thang Tran3dad8262023-08-17 15:20:56 +07002618std::tuple<bool, // Reading result
2619 uint7_t, // Temp value
2620 bool> // Sign bit
2621 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2622{
2623 std::string service{};
2624 boost::system::error_code ec =
2625 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2626 if (ec.value())
2627 {
2628 return std::make_tuple(false, 0, false);
2629 }
2630
2631 ipmi::PropertyMap properties{};
2632 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2633 sensor::sensorInterface, properties);
2634 if (ec.value())
2635 {
2636 return std::make_tuple(false, 0, false);
2637 }
2638
2639 auto scaleIt = properties.find("Scale");
2640 double scaleVal = 0.0;
2641 if (scaleIt != properties.end())
2642 {
2643 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2644 }
2645
2646 auto tempValIt = properties.find("Value");
2647 double tempVal = 0.0;
2648 if (tempValIt == properties.end())
2649 {
2650 return std::make_tuple(false, 0, false);
2651 }
2652
2653 const double maxTemp = 127;
2654 double absTempVal = 0.0;
2655 bool signBit = false;
2656
2657 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2658 tempVal = std::pow(10, scaleVal) * tempVal;
2659 absTempVal = std::abs(tempVal);
2660 absTempVal = std::min(absTempVal, maxTemp);
2661 signBit = (tempVal < 0) ? true : false;
2662
2663 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2664}
2665
adarshgrami042e9db2022-09-15 10:34:34 +05302666ipmi::RspType<uint8_t, // No of instances for requested id
2667 uint8_t, // No of record ids in the response
2668 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2669 // IDs
2670 >
2671 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002672 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302673{
2674 auto match = ipmi::dcmi::validEntityId.find(entityId);
2675 if (match == ipmi::dcmi::validEntityId.end())
2676 {
George Liude6694e2024-07-17 15:22:25 +08002677 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
adarshgrami042e9db2022-09-15 10:34:34 +05302678
2679 return ipmi::responseInvalidFieldRequest();
2680 }
2681
2682 if (sensorType != ipmi::dcmi::temperatureSensorType)
2683 {
George Liude6694e2024-07-17 15:22:25 +08002684 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2685 sensorType);
adarshgrami042e9db2022-09-15 10:34:34 +05302686
2687 return ipmi::responseInvalidFieldRequest();
2688 }
adarshgrami042e9db2022-09-15 10:34:34 +05302689
2690 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002691 const auto& [totalSensorInst, sensorList] =
2692 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302693
Thang Tranb1416ef2023-08-02 13:57:09 +07002694 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302695 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002696 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2697 }
adarshgrami042e9db2022-09-15 10:34:34 +05302698
Thang Tranb1416ef2023-08-02 13:57:09 +07002699 /*
2700 * As DCMI specification, the maximum number of Record Ids of response data
2701 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2702 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2703 * in response data.
2704 */
2705 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2706
2707 for (const auto& sensor : sensorList)
2708 {
2709 sensorRec.emplace_back(sensor.recordId);
2710 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302711 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002712 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302713 }
2714 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002715
2716 return ipmi::responseSuccess(
2717 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302718}
Thang Tran3dad8262023-08-17 15:20:56 +07002719
2720ipmi::RspType<uint8_t, // No of instances for requested id
2721 uint8_t, // No of record ids in the response
2722 std::vector< // Temperature Data
2723 std::tuple<uint7_t, // Temperature value
2724 bool, // Sign bit
2725 uint8_t // Entity Instance of sensor
2726 >>>
2727 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2728 uint8_t entityId, uint8_t entityInstance,
2729 uint8_t instanceStart)
2730{
2731 auto match = ipmi::dcmi::validEntityId.find(entityId);
2732 if (match == ipmi::dcmi::validEntityId.end())
2733 {
George Liude6694e2024-07-17 15:22:25 +08002734 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Thang Tran3dad8262023-08-17 15:20:56 +07002735
2736 return ipmi::responseInvalidFieldRequest();
2737 }
2738
2739 if (sensorType != ipmi::dcmi::temperatureSensorType)
2740 {
George Liude6694e2024-07-17 15:22:25 +08002741 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2742 sensorType);
Thang Tran3dad8262023-08-17 15:20:56 +07002743
2744 return ipmi::responseInvalidFieldRequest();
2745 }
2746
2747 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2748 const auto& [totalSensorInst, sensorList] =
2749 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2750
2751 if (sensorList.empty())
2752 {
2753 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2754 }
2755
2756 /*
2757 * As DCMI specification, the maximum number of Record Ids of response data
2758 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2759 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2760 * in response data.
2761 */
2762 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2763
2764 for (const auto& sensor : sensorList)
2765 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002766 const auto& [readResult, tempVal, signBit] =
2767 readTemp(ctx, sensor.objectPath);
Thang Tran3dad8262023-08-17 15:20:56 +07002768
2769 if (readResult)
2770 {
2771 tempReadingVal.emplace_back(
2772 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2773
2774 if (tempReadingVal.size() >= numOfRec)
2775 {
2776 break;
2777 }
2778 }
2779 }
2780
2781 return ipmi::responseSuccess(totalSensorInst,
2782 static_cast<uint8_t>(tempReadingVal.size()),
2783 tempReadingVal);
2784}
2785
adarshgrami042e9db2022-09-15 10:34:34 +05302786} // namespace dcmi
2787
Willy Tude54f482021-01-26 15:59:09 -08002788/* end storage commands */
2789
2790void registerSensorFunctions()
2791{
2792 // <Platform Event>
2793 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2794 ipmi::sensor_event::cmdPlatformEvent,
2795 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2796
Willy Tudbafbce2021-03-29 00:37:05 -07002797 // <Set Sensor Reading and Event Status>
2798 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2799 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2800 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002801
Willy Tude54f482021-01-26 15:59:09 -08002802 // <Get Sensor Reading>
2803 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2804 ipmi::sensor_event::cmdGetSensorReading,
2805 ipmi::Privilege::User, ipmiSenGetSensorReading);
2806
2807 // <Get Sensor Threshold>
2808 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2809 ipmi::sensor_event::cmdGetSensorThreshold,
2810 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2811
2812 // <Set Sensor Threshold>
2813 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2814 ipmi::sensor_event::cmdSetSensorThreshold,
2815 ipmi::Privilege::Operator,
2816 ipmiSenSetSensorThresholds);
2817
2818 // <Get Sensor Event Enable>
2819 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2820 ipmi::sensor_event::cmdGetSensorEventEnable,
2821 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2822
2823 // <Get Sensor Event Status>
2824 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2825 ipmi::sensor_event::cmdGetSensorEventStatus,
2826 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2827
2828 // register all storage commands for both Sensor and Storage command
2829 // versions
2830
2831 // <Get SDR Repository Info>
2832 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2833 ipmi::storage::cmdGetSdrRepositoryInfo,
2834 ipmi::Privilege::User,
2835 ipmiStorageGetSDRRepositoryInfo);
2836
2837 // <Get Device SDR Info>
2838 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2839 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2840 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2841
2842 // <Get SDR Allocation Info>
2843 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2844 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2845 ipmi::Privilege::User,
2846 ipmiStorageGetSDRAllocationInfo);
2847
2848 // <Reserve SDR Repo>
2849 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2850 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2851 ipmi::Privilege::User, ipmiStorageReserveSDR);
2852
2853 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2854 ipmi::storage::cmdReserveSdrRepository,
2855 ipmi::Privilege::User, ipmiStorageReserveSDR);
2856
2857 // <Get Sdr>
2858 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2859 ipmi::sensor_event::cmdGetDeviceSdr,
2860 ipmi::Privilege::User, ipmiStorageGetSDR);
2861
2862 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2863 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2864 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302865 // <Get DCMI Sensor Info>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002866 ipmi::registerGroupHandler(
2867 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2868 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2869 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002870 // <Get Temperature Readings>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002871 ipmi::registerGroupHandler(
2872 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2873 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2874 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002875}
2876} // namespace ipmi