blob: b3786c97ccb86558e3e4ed219e37842691dba1ac [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 <iostream>
Willy Tude54f482021-01-26 15:59:09 -080042#include <map>
Willy Tude54f482021-01-26 15:59:09 -080043#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080044#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48
Scron Chang2703b022021-07-06 15:47:45 +080049#ifdef FEATURE_HYBRID_SENSORS
50
51#include "sensordatahandler.hpp"
52namespace ipmi
53{
54namespace sensor
55{
56extern const IdInfoMap sensors;
57} // namespace sensor
58} // namespace ipmi
59#endif
adarshgrami042e9db2022-09-15 10:34:34 +053060namespace ipmi
61{
62namespace dcmi
63{
64// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
65static const std::map<uint8_t, uint8_t> validEntityId{
66 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
67 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
68constexpr uint8_t temperatureSensorType = 0x01;
69constexpr uint8_t maxRecords = 8;
70} // namespace dcmi
71} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080072constexpr std::array<const char*, 7> suffixes = {
73 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
74 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080075namespace ipmi
76{
Hao Jiangd48c9212021-02-03 15:45:06 -080077
78using phosphor::logging::entry;
79using phosphor::logging::level;
80using phosphor::logging::log;
81
Willy Tude54f482021-01-26 15:59:09 -080082static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070083static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080084
Willy Tu38e7a2b2021-03-29 15:09:56 -070085// BMC I2C address is generally at 0x20
86static constexpr uint8_t bmcI2CAddr = 0x20;
87
Willy Tude54f482021-01-26 15:59:09 -080088constexpr size_t maxSDRTotalSize =
89 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
90constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
91
92static uint16_t sdrReservationID;
93static uint32_t sdrLastAdd = noTimestamp;
94static uint32_t sdrLastRemove = noTimestamp;
95static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070096
97// The IPMI spec defines four Logical Units (LUN), each capable of supporting
98// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
99// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800100// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700101// location.
102static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
103
Harvey Wu75893062023-03-22 17:17:31 +0800104static constexpr uint8_t lun0 = 0x0;
105static constexpr uint8_t lun1 = 0x1;
106static constexpr uint8_t lun3 = 0x3;
107
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700108static constexpr size_t lun0MaxSensorNum = 0xfe;
109static constexpr size_t lun1MaxSensorNum = 0x1fe;
110static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800111static constexpr int GENERAL_ERROR = -1;
112
Willy Tude54f482021-01-26 15:59:09 -0800113static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
114
115// Specify the comparison required to sort and find char* map objects
116struct CmpStr
117{
118 bool operator()(const char* a, const char* b) const
119 {
120 return std::strcmp(a, b) < 0;
121 }
122};
123const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
124 sensorUnits{{{"temperature", SensorUnits::degreesC},
125 {"voltage", SensorUnits::volts},
126 {"current", SensorUnits::amps},
127 {"fan_tach", SensorUnits::rpm},
Johnathan Mantey7b037272024-06-17 12:12:40 -0700128 {"power", SensorUnits::watts},
129 {"energy", SensorUnits::joules}}};
Willy Tude54f482021-01-26 15:59:09 -0800130
131void registerSensorFunctions() __attribute__((constructor));
132
Patrick Williams5d82f472022-07-22 19:26:53 -0500133static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800134 *getSdBus(),
135 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
136 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500137 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400138 getSensorTree().clear();
139 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
140 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
141 std::chrono::system_clock::now().time_since_epoch())
142 .count();
143 });
Willy Tude54f482021-01-26 15:59:09 -0800144
Patrick Williams5d82f472022-07-22 19:26:53 -0500145static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800146 *getSdBus(),
147 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
148 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500149 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400150 getSensorTree().clear();
151 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
152 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
153 std::chrono::system_clock::now().time_since_epoch())
154 .count();
155 });
Willy Tude54f482021-01-26 15:59:09 -0800156
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700157ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
158 std::string& connection, std::string& path,
159 std::vector<std::string>* interfaces)
160{
161 auto& sensorTree = getSensorTree();
162 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
163 {
164 return IPMI_CC_RESPONSE_ERROR;
165 }
166
167 if (ctx == nullptr)
168 {
169 return IPMI_CC_RESPONSE_ERROR;
170 }
171
172 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
173 if (path.empty())
174 {
175 return IPMI_CC_INVALID_FIELD_REQUEST;
176 }
177
178 for (const auto& sensor : sensorTree)
179 {
180 if (path == sensor.first)
181 {
182 connection = sensor.second.begin()->first;
183 if (interfaces)
184 *interfaces = sensor.second.begin()->second;
185 break;
186 }
187 }
188
189 return 0;
190}
191
192SensorSubTree& getSensorTree()
193{
194 static SensorSubTree sensorTree;
195 return sensorTree;
196}
197
Willy Tude54f482021-01-26 15:59:09 -0800198// this keeps track of deassertions for sensor event status command. A
199// deasertion can only happen if an assertion was seen first.
200static boost::container::flat_map<
201 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
202 thresholdDeassertMap;
203
Patrick Williams5d82f472022-07-22 19:26:53 -0500204static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800205 *getSdBus(),
206 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
207 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500208 [](sdbusplus::message_t& m) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400209 boost::container::flat_map<std::string, std::variant<bool, double>>
210 values;
211 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800212
Patrick Williams1318a5e2024-08-16 15:19:54 -0400213 auto findAssert =
214 std::find_if(values.begin(), values.end(), [](const auto& pair) {
215 return pair.first.find("Alarm") != std::string::npos;
216 });
217 if (findAssert != values.end())
Willy Tude54f482021-01-26 15:59:09 -0800218 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400219 auto ptr = std::get_if<bool>(&(findAssert->second));
220 if (ptr == nullptr)
221 {
222 lg2::error("thresholdChanged: Assert non bool");
223 return;
224 }
225 if (*ptr)
Willy Tude54f482021-01-26 15:59:09 -0800226 {
George Liude6694e2024-07-17 15:22:25 +0800227 lg2::info(
Patrick Williams1318a5e2024-08-16 15:19:54 -0400228 "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
George Liude6694e2024-07-17 15:22:25 +0800229 "SENSOR_PATH", m.get_path());
Patrick Williams1318a5e2024-08-16 15:19:54 -0400230 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
231 }
232 else
233 {
234 auto& value =
235 thresholdDeassertMap[m.get_path()][findAssert->first];
236 if (value)
237 {
238 lg2::info(
239 "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
240 "SENSOR_PATH", m.get_path());
241 value = *ptr;
242 }
Willy Tude54f482021-01-26 15:59:09 -0800243 }
244 }
Patrick Williams1318a5e2024-08-16 15:19:54 -0400245 });
Willy Tude54f482021-01-26 15:59:09 -0800246
Hao Jiangd2afd052020-12-10 15:09:32 -0800247namespace sensor
248{
249static constexpr const char* vrInterface =
250 "xyz.openbmc_project.Control.VoltageRegulatorMode";
251static constexpr const char* sensorInterface =
252 "xyz.openbmc_project.Sensor.Value";
253} // namespace sensor
254
Willy Tude54f482021-01-26 15:59:09 -0800255static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
256 double& min)
257{
258 max = 127;
259 min = -128;
260
Hao Jiangd2afd052020-12-10 15:09:32 -0800261 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800262 auto critical =
263 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
264 auto warning =
265 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
266
267 if (sensorObject != sensorMap.end())
268 {
269 auto maxMap = sensorObject->second.find("MaxValue");
270 auto minMap = sensorObject->second.find("MinValue");
271
272 if (maxMap != sensorObject->second.end())
273 {
274 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
275 }
276 if (minMap != sensorObject->second.end())
277 {
278 min = std::visit(VariantToDoubleVisitor(), minMap->second);
279 }
280 }
281 if (critical != sensorMap.end())
282 {
283 auto lower = critical->second.find("CriticalLow");
284 auto upper = critical->second.find("CriticalHigh");
285 if (lower != critical->second.end())
286 {
287 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300288 if (std::isfinite(value))
289 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700290 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300291 }
Willy Tude54f482021-01-26 15:59:09 -0800292 }
293 if (upper != critical->second.end())
294 {
295 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300296 if (std::isfinite(value))
297 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700298 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300299 }
Willy Tude54f482021-01-26 15:59:09 -0800300 }
301 }
302 if (warning != sensorMap.end())
303 {
Willy Tude54f482021-01-26 15:59:09 -0800304 auto lower = warning->second.find("WarningLow");
305 auto upper = warning->second.find("WarningHigh");
306 if (lower != warning->second.end())
307 {
308 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300309 if (std::isfinite(value))
310 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700311 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300312 }
Willy Tude54f482021-01-26 15:59:09 -0800313 }
314 if (upper != warning->second.end())
315 {
316 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300317 if (std::isfinite(value))
318 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700319 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300320 }
Willy Tude54f482021-01-26 15:59:09 -0800321 }
322 }
323}
324
325static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700326 std::string sensorPath, DbusInterfaceMap& sensorMap,
327 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800328{
Scron Chang2703b022021-07-06 15:47:45 +0800329#ifdef FEATURE_HYBRID_SENSORS
330 if (auto sensor = findStaticSensor(sensorPath);
331 sensor != ipmi::sensor::sensors.end() &&
332 getSensorEventTypeFromPath(sensorPath) !=
333 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
334 {
335 // If the incoming sensor is a discrete sensor, it might fail in
336 // getManagedObjects(), return true, and use its own getFunc to get
337 // value.
338 return true;
339 }
340#endif
341
Willy Tude54f482021-01-26 15:59:09 -0800342 static boost::container::flat_map<
343 std::string, std::chrono::time_point<std::chrono::steady_clock>>
344 updateTimeMap;
345
346 auto updateFind = updateTimeMap.find(sensorConnection);
347 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
348 if (updateFind != updateTimeMap.end())
349 {
350 lastUpdate = updateFind->second;
351 }
352
353 auto now = std::chrono::steady_clock::now();
354
355 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700356 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800357 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800358 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800359
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800360 // Object managers for different kinds of OpenBMC DBus interfaces.
361 // Documented in the phosphor-dbus-interfaces repository.
362 const char* paths[] = {
363 "/xyz/openbmc_project/sensors",
364 "/xyz/openbmc_project/vr",
365 };
366 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
367 ObjectValueTree allManagedObjects;
368
369 for (size_t i = 0; i < num_paths; i++)
370 {
371 ObjectValueTree managedObjects;
372 boost::system::error_code ec = getManagedObjects(
373 ctx, sensorConnection.c_str(), paths[i], managedObjects);
374 if (ec)
375 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800376 continue;
377 }
378 allManagedObjects.merge(managedObjects);
379 found = true;
380 }
381
382 if (!found)
383 {
George Liude6694e2024-07-17 15:22:25 +0800384 lg2::error("GetMangagedObjects for getSensorMap failed, "
385 "service: {SERVICE}",
386 "SERVICE", sensorConnection);
Tom Tung6615d472023-05-31 18:48:12 +0800387
Willy Tude54f482021-01-26 15:59:09 -0800388 return false;
389 }
390
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800391 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700392 // Update time after finish building the map which allow the
393 // data to be cached for updatePeriod plus the build time.
394 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800395 }
396 auto connection = SensorCache.find(sensorConnection);
397 if (connection == SensorCache.end())
398 {
399 return false;
400 }
401 auto path = connection->second.find(sensorPath);
402 if (path == connection->second.end())
403 {
404 return false;
405 }
406 sensorMap = path->second;
407
408 return true;
409}
410
Hao Jiangd2afd052020-12-10 15:09:32 -0800411namespace sensor
412{
Hao Jiangd48c9212021-02-03 15:45:06 -0800413// Read VR profiles from sensor(daemon) interface
414static std::optional<std::vector<std::string>>
415 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800416{
417 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800418 auto supportedProperty = object.find("Supported");
419 if (supportedProperty == object.end() ||
420 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800421 {
George Liude6694e2024-07-17 15:22:25 +0800422 lg2::error("Missing the required Supported and Selected properties");
Hao Jiangd2afd052020-12-10 15:09:32 -0800423 return std::nullopt;
424 }
425
426 const auto profilesPtr =
427 std::get_if<std::vector<std::string>>(&supportedProperty->second);
428
429 if (profilesPtr == nullptr)
430 {
George Liude6694e2024-07-17 15:22:25 +0800431 lg2::error("property is not array of string");
Hao Jiangd2afd052020-12-10 15:09:32 -0800432 return std::nullopt;
433 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800434 return *profilesPtr;
435}
436
437// Calculate VR Mode from input IPMI discrete event bytes
Patrick Williams1318a5e2024-08-16 15:19:54 -0400438static std::optional<std::string> calculateVRMode(
439 uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
Hao Jiangd48c9212021-02-03 15:45:06 -0800440{
441 // get VR mode profiles from Supported Interface
442 auto profiles = getSupportedVrProfiles(VRObject);
443 if (!profiles)
444 {
445 return std::nullopt;
446 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800447
448 // interpret IPMI cmd bits into profiles' index
449 long unsigned int index = 0;
450 // only one bit should be set and the highest bit should not be used.
451 if (assertOffset == 0 || assertOffset == (1u << 15) ||
452 (assertOffset & (assertOffset - 1)))
453 {
George Liude6694e2024-07-17 15:22:25 +0800454 lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
455 lg2::hex, static_cast<uint16_t>(assertOffset));
Hao Jiangd2afd052020-12-10 15:09:32 -0800456 return std::nullopt;
457 }
458
459 while (assertOffset != 1)
460 {
461 assertOffset >>= 1;
462 index++;
463 }
464
Hao Jiangd48c9212021-02-03 15:45:06 -0800465 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800466 {
George Liude6694e2024-07-17 15:22:25 +0800467 lg2::error("profile index out of boundary");
Hao Jiangd2afd052020-12-10 15:09:32 -0800468 return std::nullopt;
469 }
470
Hao Jiangd48c9212021-02-03 15:45:06 -0800471 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800472}
473
474// Calculate sensor value from IPMI reading byte
475static std::optional<double>
476 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
477 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
478{
479 if (valueObject.find("Value") == valueObject.end())
480 {
George Liude6694e2024-07-17 15:22:25 +0800481 lg2::error("Missing the required Value property");
Hao Jiangd2afd052020-12-10 15:09:32 -0800482 return std::nullopt;
483 }
484
485 double max = 0;
486 double min = 0;
487 getSensorMaxMin(sensorMap, max, min);
488
489 int16_t mValue = 0;
490 int16_t bValue = 0;
491 int8_t rExp = 0;
492 int8_t bExp = 0;
493 bool bSigned = false;
494
495 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
496 {
497 return std::nullopt;
498 }
499
500 double value = bSigned ? ((int8_t)reading) : reading;
501
502 value *= ((double)mValue);
503 value += ((double)bValue) * std::pow(10.0, bExp);
504 value *= std::pow(10.0, rExp);
505
506 return value;
507}
508
Willy Tu38e7a2b2021-03-29 15:09:56 -0700509// Extract file name from sensor path as the sensors SDR ID. Simplify the name
510// if it is too long.
511std::string parseSdrIdFromPath(const std::string& path)
512{
513 std::string name;
514 size_t nameStart = path.rfind("/");
515 if (nameStart != std::string::npos)
516 {
517 name = path.substr(nameStart + 1, std::string::npos - nameStart);
518 }
519
Willy Tu38e7a2b2021-03-29 15:09:56 -0700520 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
521 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200522#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800523 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700524 {
JeffLind950f412021-10-20 18:49:34 +0800525 if (boost::ends_with(name, suffix))
526 {
527 boost::replace_all(name, suffix, "");
528 break;
529 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700530 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200531#endif
532#ifdef SHORTNAME_REPLACE_WORDS
533 constexpr std::array<std::pair<const char*, const char*>, 2>
534 replaceWords = {std::make_pair("Output", "Out"),
535 std::make_pair("Input", "In")};
536 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800537 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200538 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800539 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200540#endif
541
542 // as a backup and if nothing else is configured
543 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700544 }
545 return name;
546}
547
Hao Jiangd48c9212021-02-03 15:45:06 -0800548bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
549 const std::string& path,
550 const ipmi::DbusInterfaceMap::mapped_type& object,
551 std::bitset<16>& assertions)
552{
553 auto profiles = sensor::getSupportedVrProfiles(object);
554 if (!profiles)
555 {
556 return false;
557 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700558 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800559
560 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700561 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800562 if (ec)
563 {
George Liude6694e2024-07-17 15:22:25 +0800564 lg2::error("Failed to get Selected, path: {PATH}, "
565 "interface: {INTERFACE}, error: {ERROR}",
566 "PATH", path, "INTERFACE", sensor::sensorInterface, "ERROR",
567 ec.message());
Hao Jiangd48c9212021-02-03 15:45:06 -0800568 return false;
569 }
570
Willy Tu8366f0b2022-04-29 05:00:17 -0700571 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800572 if (itr == profiles->end())
573 {
George Liude6694e2024-07-17 15:22:25 +0800574 lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
575 "PATH", path);
Hao Jiangd48c9212021-02-03 15:45:06 -0800576 return false;
577 }
578 std::size_t index =
579 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
580
Willy Tubef102a2022-06-09 15:36:09 -0700581 // map index to response event assertion bit.
582 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800583 {
Willy Tubef102a2022-06-09 15:36:09 -0700584 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800585 }
586 else
587 {
George Liude6694e2024-07-17 15:22:25 +0800588 lg2::error("VR profile index reaches max assertion bit, "
589 "path: {PATH}, index: {INDEX}",
590 "PATH", path, "INDEX", index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800591 return false;
592 }
593 if constexpr (debug)
594 {
595 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700596 << " mode is: [" << index << "] " << mode << std::endl;
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);
694 ret != IPMI_CC_OK)
695 {
696 return GENERAL_ERROR;
697 }
698 data.header.record_id_msb = recordID >> 8;
699 data.header.record_id_lsb = recordID & 0xFF;
700 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
701 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
702 }
703
704 return 0;
705}
706
Hao Jiangd2afd052020-12-10 15:09:32 -0800707} // namespace sensor
708
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000709ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
710 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800711{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000712 constexpr const uint8_t validEnvmRev = 0x04;
713 constexpr const uint8_t lastSensorType = 0x2C;
714 constexpr const uint8_t oemReserved = 0xC0;
715
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700716 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000717 uint8_t evmRev = 0;
718 uint8_t sensorType = 0;
719 uint8_t sensorNum = 0;
720 uint8_t eventType = 0;
721 uint8_t eventData1 = 0;
722 std::optional<uint8_t> eventData2 = 0;
723 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700724 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000725 ipmi::ChannelInfo chInfo;
726
727 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
728 {
George Liude6694e2024-07-17 15:22:25 +0800729 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
730 ctx->channel);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000731 return ipmi::responseUnspecifiedError();
732 }
733
734 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
735 ipmi::EChannelMediumType::systemInterface)
736 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700737 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000738 eventData1, eventData2, eventData3);
Johnathan Manteya440cd42024-06-20 13:21:48 -0700739 constexpr const uint8_t isSoftwareID = 0x01;
740 if (!(sysgeneratorID & isSoftwareID))
741 {
742 return ipmi::responseInvalidFieldRequest();
743 }
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700744 // Refer to IPMI Spec Table 32: SEL Event Records
745 generatorID = (ctx->channel << 12) // Channel
746 | (0x0 << 10) // Reserved
747 | (0x0 << 8) // 0x0 for sys-soft ID
Johnathan Manteya440cd42024-06-20 13:21:48 -0700748 | sysgeneratorID;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000749 }
750 else
751 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000752 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
753 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700754 // Refer to IPMI Spec Table 32: SEL Event Records
755 generatorID = (ctx->channel << 12) // Channel
756 | (0x0 << 10) // Reserved
757 | ((ctx->lun & 0x3) << 8) // Lun
758 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000759 }
760
761 if (!p.fullyUnpacked())
762 {
763 return ipmi::responseReqDataLenInvalid();
764 }
765
766 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
767 if (evmRev != validEnvmRev)
768 {
769 return ipmi::responseInvalidFieldRequest();
770 }
771 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
772 {
773 return ipmi::responseInvalidFieldRequest();
774 }
775
Willy Tude54f482021-01-26 15:59:09 -0800776 return ipmi::responseSuccess();
777}
778
Patrick Williams1318a5e2024-08-16 15:19:54 -0400779ipmi::RspType<> ipmiSetSensorReading(
780 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
781 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700782{
783 std::string connection;
784 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700785 std::vector<std::string> interfaces;
786
Patrick Williams1318a5e2024-08-16 15:19:54 -0400787 ipmi::Cc status =
788 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700789 if (status)
790 {
791 return ipmi::response(status);
792 }
793
Hao Jiangd2afd052020-12-10 15:09:32 -0800794 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700795 if (std::find(interfaces.begin(), interfaces.end(),
796 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700797 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700798 DbusInterfaceMap sensorMap;
799 if (!getSensorMap(ctx, connection, path, sensorMap))
800 {
801 return ipmi::responseResponseError();
802 }
803 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800804 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700805 {
806 return ipmi::responseResponseError();
807 }
808
Jie Yangf0a89942021-07-29 15:30:25 -0700809 // Only allow external SetSensor if write permission granted
Patrick Williams1318a5e2024-08-16 15:19:54 -0400810 if (!details::sdrWriteTable.getWritePermission(
811 (ctx->lun << 8) | sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700812 {
813 return ipmi::responseResponseError();
814 }
815
Patrick Williams1318a5e2024-08-16 15:19:54 -0400816 auto value =
817 sensor::calculateValue(reading, sensorMap, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800818 if (!value)
819 {
820 return ipmi::responseResponseError();
821 }
822
823 if constexpr (debug)
824 {
George Liude6694e2024-07-17 15:22:25 +0800825 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
826 "byte: {BYTE}, value: {VALUE}",
827 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
828 "VALUE", *value);
Hao Jiangd2afd052020-12-10 15:09:32 -0800829 }
830
831 boost::system::error_code ec =
832 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
833 "Value", ipmi::Value(*value));
834
835 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500836 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800837 // callback functions for now (e.g. ipmiSetSensorReading).
838 if (ec)
839 {
George Liude6694e2024-07-17 15:22:25 +0800840 lg2::error("Failed to set Value, path: {PATH}, "
841 "interface: {INTERFACE}, ERROR: {ERROR}",
842 "PATH", path, "INTERFACE", sensor::sensorInterface,
843 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800844 return ipmi::responseResponseError();
845 }
846 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700847 }
848
Hao Jiange39d4d82021-04-16 17:02:40 -0700849 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
850 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700851 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700852 DbusInterfaceMap sensorMap;
853 if (!getSensorMap(ctx, connection, path, sensorMap))
854 {
855 return ipmi::responseResponseError();
856 }
857 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800858 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700859 {
860 return ipmi::responseResponseError();
861 }
862
Hao Jiangd2afd052020-12-10 15:09:32 -0800863 // VR sensors are treated as a special case and we will not check the
864 // write permission for VR sensors, since they always deemed writable
865 // and permission table are not applied to VR sensors.
Patrick Williams1318a5e2024-08-16 15:19:54 -0400866 auto vrMode =
867 sensor::calculateVRMode(assertOffset, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800868 if (!vrMode)
869 {
870 return ipmi::responseResponseError();
871 }
872 boost::system::error_code ec = setDbusProperty(
873 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
874 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500875 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800876 // callback functions for now (e.g. ipmiSetSensorReading).
877 if (ec)
878 {
George Liude6694e2024-07-17 15:22:25 +0800879 lg2::error("Failed to set Selected, path: {PATH}, "
880 "interface: {INTERFACE}, ERROR: {ERROR}",
881 "PATH", path, "INTERFACE", sensor::sensorInterface,
882 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800883 }
884 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700885 }
886
George Liude6694e2024-07-17 15:22:25 +0800887 lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
Hao Jiangd2afd052020-12-10 15:09:32 -0800888 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700889}
890
Willy Tude54f482021-01-26 15:59:09 -0800891ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
892 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
893{
894 std::string connection;
895 std::string path;
896
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000897 if (sensnum == reservedSensorNumber)
898 {
899 return ipmi::responseInvalidFieldRequest();
900 }
901
Willy Tude54f482021-01-26 15:59:09 -0800902 auto status = getSensorConnection(ctx, sensnum, connection, path);
903 if (status)
904 {
905 return ipmi::response(status);
906 }
907
Scron Chang2703b022021-07-06 15:47:45 +0800908#ifdef FEATURE_HYBRID_SENSORS
909 if (auto sensor = findStaticSensor(path);
910 sensor != ipmi::sensor::sensors.end() &&
911 getSensorEventTypeFromPath(path) !=
912 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
913 {
914 if (ipmi::sensor::Mutability::Read !=
915 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
916 {
917 return ipmi::responseIllegalCommand();
918 }
919
920 uint8_t operation;
921 try
922 {
923 ipmi::sensor::GetSensorResponse getResponse =
924 sensor->second.getFunc(sensor->second);
925
926 if (getResponse.readingOrStateUnavailable)
927 {
928 operation |= static_cast<uint8_t>(
929 IPMISensorReadingByte2::readingStateUnavailable);
930 }
931 if (getResponse.scanningEnabled)
932 {
933 operation |= static_cast<uint8_t>(
934 IPMISensorReadingByte2::sensorScanningEnable);
935 }
936 if (getResponse.allEventMessagesEnabled)
937 {
938 operation |= static_cast<uint8_t>(
939 IPMISensorReadingByte2::eventMessagesEnable);
940 }
941 return ipmi::responseSuccess(
942 getResponse.reading, operation,
943 getResponse.thresholdLevelsStates,
944 getResponse.discreteReadingSensorStates);
945 }
946 catch (const std::exception& e)
947 {
948 operation |= static_cast<uint8_t>(
949 IPMISensorReadingByte2::readingStateUnavailable);
950 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
951 }
952 }
953#endif
954
Willy Tude54f482021-01-26 15:59:09 -0800955 DbusInterfaceMap sensorMap;
956 if (!getSensorMap(ctx, connection, path, sensorMap))
957 {
958 return ipmi::responseResponseError();
959 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800960 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800961
962 if (sensorObject == sensorMap.end() ||
963 sensorObject->second.find("Value") == sensorObject->second.end())
964 {
965 return ipmi::responseResponseError();
966 }
967 auto& valueVariant = sensorObject->second["Value"];
968 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
969
970 double max = 0;
971 double min = 0;
972 getSensorMaxMin(sensorMap, max, min);
973
974 int16_t mValue = 0;
975 int16_t bValue = 0;
976 int8_t rExp = 0;
977 int8_t bExp = 0;
978 bool bSigned = false;
979
980 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
981 {
982 return ipmi::responseResponseError();
983 }
984
Patrick Williams1318a5e2024-08-16 15:19:54 -0400985 uint8_t value =
986 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800987 uint8_t operation =
988 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
989 operation |=
990 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
991 bool notReading = std::isnan(reading);
992
993 if (!notReading)
994 {
995 auto availableObject =
996 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
997 if (availableObject != sensorMap.end())
998 {
999 auto findAvailable = availableObject->second.find("Available");
1000 if (findAvailable != availableObject->second.end())
1001 {
1002 bool* available = std::get_if<bool>(&(findAvailable->second));
1003 if (available && !(*available))
1004 {
1005 notReading = true;
1006 }
1007 }
1008 }
1009 }
1010
1011 if (notReading)
1012 {
1013 operation |= static_cast<uint8_t>(
1014 IPMISensorReadingByte2::readingStateUnavailable);
1015 }
1016
Josh Lehana55c9532020-10-28 21:59:06 -07001017 if constexpr (details::enableInstrumentation)
1018 {
1019 int byteValue;
1020 if (bSigned)
1021 {
1022 byteValue = static_cast<int>(static_cast<int8_t>(value));
1023 }
1024 else
1025 {
1026 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1027 }
1028
1029 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001030 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1031 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001032 {
1033 // This is the first reading, show the coefficients
1034 double step = (max - min) / 255.0;
Patrick Williams1318a5e2024-08-16 15:19:54 -04001035 std::cerr
1036 << "IPMI sensor "
1037 << details::sdrStatsTable.getName((ctx->lun << 8) | sensnum)
1038 << ": Range min=" << min << " max=" << max << ", step=" << step
1039 << ", Coefficients mValue=" << static_cast<int>(mValue)
1040 << " rExp=" << static_cast<int>(rExp)
1041 << " bValue=" << static_cast<int>(bValue)
1042 << " bExp=" << static_cast<int>(bExp)
1043 << " bSigned=" << static_cast<int>(bSigned) << "\n";
Josh Lehana55c9532020-10-28 21:59:06 -07001044 }
1045 }
1046
Willy Tude54f482021-01-26 15:59:09 -08001047 uint8_t thresholds = 0;
1048
1049 auto warningObject =
1050 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1051 if (warningObject != sensorMap.end())
1052 {
1053 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1054 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1055 if (alarmHigh != warningObject->second.end())
1056 {
1057 if (std::get<bool>(alarmHigh->second))
1058 {
1059 thresholds |= static_cast<uint8_t>(
1060 IPMISensorReadingByte3::upperNonCritical);
1061 }
1062 }
1063 if (alarmLow != warningObject->second.end())
1064 {
1065 if (std::get<bool>(alarmLow->second))
1066 {
1067 thresholds |= static_cast<uint8_t>(
1068 IPMISensorReadingByte3::lowerNonCritical);
1069 }
1070 }
1071 }
1072
1073 auto criticalObject =
1074 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1075 if (criticalObject != sensorMap.end())
1076 {
1077 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1078 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1079 if (alarmHigh != criticalObject->second.end())
1080 {
1081 if (std::get<bool>(alarmHigh->second))
1082 {
1083 thresholds |=
1084 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1085 }
1086 }
1087 if (alarmLow != criticalObject->second.end())
1088 {
1089 if (std::get<bool>(alarmLow->second))
1090 {
1091 thresholds |=
1092 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1093 }
1094 }
1095 }
1096
1097 // no discrete as of today so optional byte is never returned
1098 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1099}
1100
1101/** @brief implements the Set Sensor threshold command
1102 * @param sensorNumber - sensor number
1103 * @param lowerNonCriticalThreshMask
1104 * @param lowerCriticalThreshMask
1105 * @param lowerNonRecovThreshMask
1106 * @param upperNonCriticalThreshMask
1107 * @param upperCriticalThreshMask
1108 * @param upperNonRecovThreshMask
1109 * @param reserved
1110 * @param lowerNonCritical - lower non-critical threshold
1111 * @param lowerCritical - Lower critical threshold
1112 * @param lowerNonRecoverable - Lower non recovarable threshold
1113 * @param upperNonCritical - Upper non-critical threshold
1114 * @param upperCritical - Upper critical
1115 * @param upperNonRecoverable - Upper Non-recoverable
1116 *
1117 * @returns IPMI completion code
1118 */
1119ipmi::RspType<> ipmiSenSetSensorThresholds(
1120 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1121 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1122 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1123 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001124 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001125 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001126 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001127{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001128 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001129 {
1130 return ipmi::responseInvalidFieldRequest();
1131 }
1132
1133 // lower nc and upper nc not suppported on any sensor
1134 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1135 {
1136 return ipmi::responseInvalidFieldRequest();
1137 }
1138
1139 // if none of the threshold mask are set, nothing to do
1140 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1141 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1142 upperCriticalThreshMask | upperNonRecovThreshMask))
1143 {
1144 return ipmi::responseSuccess();
1145 }
1146
1147 std::string connection;
1148 std::string path;
1149
1150 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1151 if (status)
1152 {
1153 return ipmi::response(status);
1154 }
1155 DbusInterfaceMap sensorMap;
1156 if (!getSensorMap(ctx, connection, path, sensorMap))
1157 {
1158 return ipmi::responseResponseError();
1159 }
1160
1161 double max = 0;
1162 double min = 0;
1163 getSensorMaxMin(sensorMap, max, min);
1164
1165 int16_t mValue = 0;
1166 int16_t bValue = 0;
1167 int8_t rExp = 0;
1168 int8_t bExp = 0;
1169 bool bSigned = false;
1170
1171 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1172 {
1173 return ipmi::responseResponseError();
1174 }
1175
1176 // store a vector of property name, value to set, and interface
1177 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1178
1179 // define the indexes of the tuple
1180 constexpr uint8_t propertyName = 0;
1181 constexpr uint8_t thresholdValue = 1;
1182 constexpr uint8_t interface = 2;
1183 // verifiy all needed fields are present
1184 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1185 {
1186 auto findThreshold =
1187 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1188 if (findThreshold == sensorMap.end())
1189 {
1190 return ipmi::responseInvalidFieldRequest();
1191 }
1192 if (lowerCriticalThreshMask)
1193 {
1194 auto findLower = findThreshold->second.find("CriticalLow");
1195 if (findLower == findThreshold->second.end())
1196 {
1197 return ipmi::responseInvalidFieldRequest();
1198 }
1199 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1200 findThreshold->first);
1201 }
1202 if (upperCriticalThreshMask)
1203 {
1204 auto findUpper = findThreshold->second.find("CriticalHigh");
1205 if (findUpper == findThreshold->second.end())
1206 {
1207 return ipmi::responseInvalidFieldRequest();
1208 }
1209 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1210 findThreshold->first);
1211 }
1212 }
1213 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1214 {
1215 auto findThreshold =
1216 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1217 if (findThreshold == sensorMap.end())
1218 {
1219 return ipmi::responseInvalidFieldRequest();
1220 }
1221 if (lowerNonCriticalThreshMask)
1222 {
1223 auto findLower = findThreshold->second.find("WarningLow");
1224 if (findLower == findThreshold->second.end())
1225 {
1226 return ipmi::responseInvalidFieldRequest();
1227 }
1228 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1229 findThreshold->first);
1230 }
1231 if (upperNonCriticalThreshMask)
1232 {
1233 auto findUpper = findThreshold->second.find("WarningHigh");
1234 if (findUpper == findThreshold->second.end())
1235 {
1236 return ipmi::responseInvalidFieldRequest();
1237 }
1238 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1239 findThreshold->first);
1240 }
1241 }
1242 for (const auto& property : thresholdsToSet)
1243 {
1244 // from section 36.3 in the IPMI Spec, assume all linear
1245 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1246 (bValue * std::pow(10.0, bExp))) *
1247 std::pow(10.0, rExp);
1248 setDbusProperty(
1249 *getSdBus(), connection, path, std::get<interface>(property),
1250 std::get<propertyName>(property), ipmi::Value(valueToSet));
1251 }
1252 return ipmi::responseSuccess();
1253}
1254
1255IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1256{
1257 IPMIThresholds resp;
1258 auto warningInterface =
1259 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1260 auto criticalInterface =
1261 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1262
1263 if ((warningInterface != sensorMap.end()) ||
1264 (criticalInterface != sensorMap.end()))
1265 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001266 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001267
1268 if (sensorPair == sensorMap.end())
1269 {
1270 // should not have been able to find a sensor not implementing
1271 // the sensor object
1272 throw std::runtime_error("Invalid sensor map");
1273 }
1274
1275 double max = 0;
1276 double min = 0;
1277 getSensorMaxMin(sensorMap, max, min);
1278
1279 int16_t mValue = 0;
1280 int16_t bValue = 0;
1281 int8_t rExp = 0;
1282 int8_t bExp = 0;
1283 bool bSigned = false;
1284
1285 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1286 {
1287 throw std::runtime_error("Invalid sensor atrributes");
1288 }
1289 if (warningInterface != sensorMap.end())
1290 {
1291 auto& warningMap = warningInterface->second;
1292
1293 auto warningHigh = warningMap.find("WarningHigh");
1294 auto warningLow = warningMap.find("WarningLow");
1295
1296 if (warningHigh != warningMap.end())
1297 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001298 double value =
1299 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001300 if (std::isfinite(value))
1301 {
1302 resp.warningHigh = scaleIPMIValueFromDouble(
1303 value, mValue, rExp, bValue, bExp, bSigned);
1304 }
Willy Tude54f482021-01-26 15:59:09 -08001305 }
1306 if (warningLow != warningMap.end())
1307 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001308 double value =
1309 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001310 if (std::isfinite(value))
1311 {
1312 resp.warningLow = scaleIPMIValueFromDouble(
1313 value, mValue, rExp, bValue, bExp, bSigned);
1314 }
Willy Tude54f482021-01-26 15:59:09 -08001315 }
1316 }
1317 if (criticalInterface != sensorMap.end())
1318 {
1319 auto& criticalMap = criticalInterface->second;
1320
1321 auto criticalHigh = criticalMap.find("CriticalHigh");
1322 auto criticalLow = criticalMap.find("CriticalLow");
1323
1324 if (criticalHigh != criticalMap.end())
1325 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001326 double value =
1327 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001328 if (std::isfinite(value))
1329 {
1330 resp.criticalHigh = scaleIPMIValueFromDouble(
1331 value, mValue, rExp, bValue, bExp, bSigned);
1332 }
Willy Tude54f482021-01-26 15:59:09 -08001333 }
1334 if (criticalLow != criticalMap.end())
1335 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001336 double value =
1337 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001338 if (std::isfinite(value))
1339 {
1340 resp.criticalLow = scaleIPMIValueFromDouble(
1341 value, mValue, rExp, bValue, bExp, bSigned);
1342 }
Willy Tude54f482021-01-26 15:59:09 -08001343 }
1344 }
1345 }
1346 return resp;
1347}
1348
1349ipmi::RspType<uint8_t, // readable
1350 uint8_t, // lowerNCrit
1351 uint8_t, // lowerCrit
1352 uint8_t, // lowerNrecoverable
1353 uint8_t, // upperNC
1354 uint8_t, // upperCrit
1355 uint8_t> // upperNRecoverable
1356 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1357{
1358 std::string connection;
1359 std::string path;
1360
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001361 if (sensorNumber == reservedSensorNumber)
1362 {
1363 return ipmi::responseInvalidFieldRequest();
1364 }
1365
Willy Tude54f482021-01-26 15:59:09 -08001366 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1367 if (status)
1368 {
1369 return ipmi::response(status);
1370 }
1371
1372 DbusInterfaceMap sensorMap;
1373 if (!getSensorMap(ctx, connection, path, sensorMap))
1374 {
1375 return ipmi::responseResponseError();
1376 }
1377
1378 IPMIThresholds thresholdData;
1379 try
1380 {
1381 thresholdData = getIPMIThresholds(sensorMap);
1382 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001383 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001384 {
1385 return ipmi::responseResponseError();
1386 }
1387
1388 uint8_t readable = 0;
1389 uint8_t lowerNC = 0;
1390 uint8_t lowerCritical = 0;
1391 uint8_t lowerNonRecoverable = 0;
1392 uint8_t upperNC = 0;
1393 uint8_t upperCritical = 0;
1394 uint8_t upperNonRecoverable = 0;
1395
1396 if (thresholdData.warningHigh)
1397 {
1398 readable |=
1399 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1400 upperNC = *thresholdData.warningHigh;
1401 }
1402 if (thresholdData.warningLow)
1403 {
1404 readable |=
1405 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1406 lowerNC = *thresholdData.warningLow;
1407 }
1408
1409 if (thresholdData.criticalHigh)
1410 {
1411 readable |=
1412 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1413 upperCritical = *thresholdData.criticalHigh;
1414 }
1415 if (thresholdData.criticalLow)
1416 {
1417 readable |=
1418 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1419 lowerCritical = *thresholdData.criticalLow;
1420 }
1421
1422 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1423 lowerNonRecoverable, upperNC, upperCritical,
1424 upperNonRecoverable);
1425}
1426
1427/** @brief implements the get Sensor event enable command
1428 * @param sensorNumber - sensor number
1429 *
1430 * @returns IPMI completion code plus response data
1431 * - enabled - Sensor Event messages
1432 * - assertionEnabledLsb - Assertion event messages
1433 * - assertionEnabledMsb - Assertion event messages
1434 * - deassertionEnabledLsb - Deassertion event messages
1435 * - deassertionEnabledMsb - Deassertion event messages
1436 */
1437
1438ipmi::RspType<uint8_t, // enabled
1439 uint8_t, // assertionEnabledLsb
1440 uint8_t, // assertionEnabledMsb
1441 uint8_t, // deassertionEnabledLsb
1442 uint8_t> // deassertionEnabledMsb
1443 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1444{
1445 std::string connection;
1446 std::string path;
1447
1448 uint8_t enabled = 0;
1449 uint8_t assertionEnabledLsb = 0;
1450 uint8_t assertionEnabledMsb = 0;
1451 uint8_t deassertionEnabledLsb = 0;
1452 uint8_t deassertionEnabledMsb = 0;
1453
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001454 if (sensorNum == reservedSensorNumber)
1455 {
1456 return ipmi::responseInvalidFieldRequest();
1457 }
1458
Willy Tude54f482021-01-26 15:59:09 -08001459 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1460 if (status)
1461 {
1462 return ipmi::response(status);
1463 }
1464
Scron Chang2703b022021-07-06 15:47:45 +08001465#ifdef FEATURE_HYBRID_SENSORS
1466 if (auto sensor = findStaticSensor(path);
1467 sensor != ipmi::sensor::sensors.end() &&
1468 getSensorEventTypeFromPath(path) !=
1469 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1470 {
1471 enabled = static_cast<uint8_t>(
1472 IPMISensorEventEnableByte2::sensorScanningEnable);
1473 uint16_t assertionEnabled = 0;
1474 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1475 ->second.begin()
1476 ->second.second)
1477 {
1478 assertionEnabled |= (1 << offsetValMap.first);
1479 }
1480 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1481 assertionEnabledMsb =
1482 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1483
1484 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1485 assertionEnabledMsb, deassertionEnabledLsb,
1486 deassertionEnabledMsb);
1487 }
1488#endif
1489
Willy Tude54f482021-01-26 15:59:09 -08001490 DbusInterfaceMap sensorMap;
1491 if (!getSensorMap(ctx, connection, path, sensorMap))
1492 {
1493 return ipmi::responseResponseError();
1494 }
1495
1496 auto warningInterface =
1497 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1498 auto criticalInterface =
1499 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1500 if ((warningInterface != sensorMap.end()) ||
1501 (criticalInterface != sensorMap.end()))
1502 {
1503 enabled = static_cast<uint8_t>(
1504 IPMISensorEventEnableByte2::sensorScanningEnable);
1505 if (warningInterface != sensorMap.end())
1506 {
1507 auto& warningMap = warningInterface->second;
1508
1509 auto warningHigh = warningMap.find("WarningHigh");
1510 auto warningLow = warningMap.find("WarningLow");
1511 if (warningHigh != warningMap.end())
1512 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001513 double value =
1514 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001515 if (std::isfinite(value))
1516 {
1517 assertionEnabledLsb |= static_cast<uint8_t>(
1518 IPMISensorEventEnableThresholds::
1519 upperNonCriticalGoingHigh);
1520 deassertionEnabledLsb |= static_cast<uint8_t>(
1521 IPMISensorEventEnableThresholds::
1522 upperNonCriticalGoingLow);
1523 }
Willy Tude54f482021-01-26 15:59:09 -08001524 }
1525 if (warningLow != warningMap.end())
1526 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001527 double value =
1528 std::visit(VariantToDoubleVisitor(), warningLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001529 if (std::isfinite(value))
1530 {
1531 assertionEnabledLsb |= static_cast<uint8_t>(
1532 IPMISensorEventEnableThresholds::
1533 lowerNonCriticalGoingLow);
1534 deassertionEnabledLsb |= static_cast<uint8_t>(
1535 IPMISensorEventEnableThresholds::
1536 lowerNonCriticalGoingHigh);
1537 }
Willy Tude54f482021-01-26 15:59:09 -08001538 }
1539 }
1540 if (criticalInterface != sensorMap.end())
1541 {
1542 auto& criticalMap = criticalInterface->second;
1543
1544 auto criticalHigh = criticalMap.find("CriticalHigh");
1545 auto criticalLow = criticalMap.find("CriticalLow");
1546
1547 if (criticalHigh != criticalMap.end())
1548 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001549 double value =
1550 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001551 if (std::isfinite(value))
1552 {
1553 assertionEnabledMsb |= static_cast<uint8_t>(
1554 IPMISensorEventEnableThresholds::
1555 upperCriticalGoingHigh);
1556 deassertionEnabledMsb |= static_cast<uint8_t>(
1557 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1558 }
Willy Tude54f482021-01-26 15:59:09 -08001559 }
1560 if (criticalLow != criticalMap.end())
1561 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001562 double value =
1563 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001564 if (std::isfinite(value))
1565 {
1566 assertionEnabledLsb |= static_cast<uint8_t>(
1567 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1568 deassertionEnabledLsb |= static_cast<uint8_t>(
1569 IPMISensorEventEnableThresholds::
1570 lowerCriticalGoingHigh);
1571 }
Willy Tude54f482021-01-26 15:59:09 -08001572 }
1573 }
1574 }
1575
1576 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1577 assertionEnabledMsb, deassertionEnabledLsb,
1578 deassertionEnabledMsb);
1579}
1580
1581/** @brief implements the get Sensor event status command
1582 * @param sensorNumber - sensor number, FFh = reserved
1583 *
1584 * @returns IPMI completion code plus response data
1585 * - sensorEventStatus - Sensor Event messages state
1586 * - assertions - Assertion event messages
1587 * - deassertions - Deassertion event messages
1588 */
1589ipmi::RspType<uint8_t, // sensorEventStatus
1590 std::bitset<16>, // assertions
1591 std::bitset<16> // deassertion
1592 >
1593 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1594{
1595 if (sensorNum == reservedSensorNumber)
1596 {
1597 return ipmi::responseInvalidFieldRequest();
1598 }
1599
1600 std::string connection;
1601 std::string path;
1602 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1603 if (status)
1604 {
George Liude6694e2024-07-17 15:22:25 +08001605 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1606 "sensor number: {SENSOR_NUM}",
1607 "SENSOR_NUM", sensorNum);
Willy Tude54f482021-01-26 15:59:09 -08001608 return ipmi::response(status);
1609 }
1610
Scron Chang2703b022021-07-06 15:47:45 +08001611#ifdef FEATURE_HYBRID_SENSORS
1612 if (auto sensor = findStaticSensor(path);
1613 sensor != ipmi::sensor::sensors.end() &&
1614 getSensorEventTypeFromPath(path) !=
1615 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1616 {
1617 auto response = ipmi::sensor::get::mapDbusToAssertion(
1618 sensor->second, path, sensor->second.sensorInterface);
1619 std::bitset<16> assertions;
1620 // deassertions are not used.
1621 std::bitset<16> deassertions = 0;
1622 uint8_t sensorEventStatus;
1623 if (response.readingOrStateUnavailable)
1624 {
1625 sensorEventStatus |= static_cast<uint8_t>(
1626 IPMISensorReadingByte2::readingStateUnavailable);
1627 }
1628 if (response.scanningEnabled)
1629 {
1630 sensorEventStatus |= static_cast<uint8_t>(
1631 IPMISensorReadingByte2::sensorScanningEnable);
1632 }
1633 if (response.allEventMessagesEnabled)
1634 {
1635 sensorEventStatus |= static_cast<uint8_t>(
1636 IPMISensorReadingByte2::eventMessagesEnable);
1637 }
1638 assertions |= response.discreteReadingSensorStates << 8;
1639 assertions |= response.thresholdLevelsStates;
1640 return ipmi::responseSuccess(sensorEventStatus, assertions,
1641 deassertions);
1642 }
1643#endif
1644
Willy Tude54f482021-01-26 15:59:09 -08001645 DbusInterfaceMap sensorMap;
1646 if (!getSensorMap(ctx, connection, path, sensorMap))
1647 {
George Liude6694e2024-07-17 15:22:25 +08001648 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1649 "sensor path: {SENSOR_PATH}",
1650 "SENSOR_PATH", path);
Willy Tude54f482021-01-26 15:59:09 -08001651 return ipmi::responseResponseError();
1652 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001653
1654 uint8_t sensorEventStatus =
1655 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1656 std::bitset<16> assertions = 0;
1657 std::bitset<16> deassertions = 0;
1658
1659 // handle VR typed sensor
1660 auto vrInterface = sensorMap.find(sensor::vrInterface);
1661 if (vrInterface != sensorMap.end())
1662 {
1663 if (!sensor::getVrEventStatus(ctx, connection, path,
1664 vrInterface->second, assertions))
1665 {
1666 return ipmi::responseResponseError();
1667 }
1668
1669 // both Event Message and Sensor Scanning are disable for VR.
1670 sensorEventStatus = 0;
1671 return ipmi::responseSuccess(sensorEventStatus, assertions,
1672 deassertions);
1673 }
1674
Willy Tude54f482021-01-26 15:59:09 -08001675 auto warningInterface =
1676 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1677 auto criticalInterface =
1678 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1679
Willy Tude54f482021-01-26 15:59:09 -08001680 std::optional<bool> criticalDeassertHigh =
1681 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1682 std::optional<bool> criticalDeassertLow =
1683 thresholdDeassertMap[path]["CriticalAlarmLow"];
1684 std::optional<bool> warningDeassertHigh =
1685 thresholdDeassertMap[path]["WarningAlarmHigh"];
1686 std::optional<bool> warningDeassertLow =
1687 thresholdDeassertMap[path]["WarningAlarmLow"];
1688
Willy Tude54f482021-01-26 15:59:09 -08001689 if (criticalDeassertHigh && !*criticalDeassertHigh)
1690 {
1691 deassertions.set(static_cast<size_t>(
1692 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1693 }
1694 if (criticalDeassertLow && !*criticalDeassertLow)
1695 {
1696 deassertions.set(static_cast<size_t>(
1697 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1698 }
1699 if (warningDeassertHigh && !*warningDeassertHigh)
1700 {
1701 deassertions.set(static_cast<size_t>(
1702 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1703 }
1704 if (warningDeassertLow && !*warningDeassertLow)
1705 {
1706 deassertions.set(static_cast<size_t>(
1707 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1708 }
1709 if ((warningInterface != sensorMap.end()) ||
1710 (criticalInterface != sensorMap.end()))
1711 {
1712 sensorEventStatus = static_cast<size_t>(
1713 IPMISensorEventEnableByte2::eventMessagesEnable);
1714 if (warningInterface != sensorMap.end())
1715 {
1716 auto& warningMap = warningInterface->second;
1717
1718 auto warningHigh = warningMap.find("WarningAlarmHigh");
1719 auto warningLow = warningMap.find("WarningAlarmLow");
1720 auto warningHighAlarm = false;
1721 auto warningLowAlarm = false;
1722
1723 if (warningHigh != warningMap.end())
1724 {
1725 warningHighAlarm = std::get<bool>(warningHigh->second);
1726 }
1727 if (warningLow != warningMap.end())
1728 {
1729 warningLowAlarm = std::get<bool>(warningLow->second);
1730 }
1731 if (warningHighAlarm)
1732 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001733 assertions.set(static_cast<size_t>(
1734 IPMIGetSensorEventEnableThresholds::
1735 upperNonCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001736 }
1737 if (warningLowAlarm)
1738 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001739 assertions.set(static_cast<size_t>(
1740 IPMIGetSensorEventEnableThresholds::
1741 lowerNonCriticalGoingLow));
Willy Tude54f482021-01-26 15:59:09 -08001742 }
1743 }
1744 if (criticalInterface != sensorMap.end())
1745 {
1746 auto& criticalMap = criticalInterface->second;
1747
1748 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1749 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1750 auto criticalHighAlarm = false;
1751 auto criticalLowAlarm = false;
1752
1753 if (criticalHigh != criticalMap.end())
1754 {
1755 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1756 }
1757 if (criticalLow != criticalMap.end())
1758 {
1759 criticalLowAlarm = std::get<bool>(criticalLow->second);
1760 }
1761 if (criticalHighAlarm)
1762 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001763 assertions.set(static_cast<size_t>(
1764 IPMIGetSensorEventEnableThresholds::
1765 upperCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001766 }
1767 if (criticalLowAlarm)
1768 {
1769 assertions.set(static_cast<size_t>(
1770 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1771 }
1772 }
1773 }
1774
1775 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1776}
1777
Willy Tu38e7a2b2021-03-29 15:09:56 -07001778// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001779void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1780 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001781{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001782 get_sdr::header::set_record_id(
1783 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1784
Willy Tu38e7a2b2021-03-29 15:09:56 -07001785 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1786 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1787
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001788 record.header.sdr_version = ipmiSdrVersion;
1789 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1790 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1791 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001792 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001793 record.key.owner_lun = lun;
1794 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001795}
Willy Tu4eca2512022-06-20 21:14:51 -07001796bool constructSensorSdr(
1797 ipmi::Context::ptr ctx,
1798 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1799 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1800 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001801{
Hao Jiange39d4d82021-04-16 17:02:40 -07001802 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1803
1804 DbusInterfaceMap sensorMap;
1805 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1806 {
George Liude6694e2024-07-17 15:22:25 +08001807 lg2::error("Failed to update sensor map for threshold sensor, "
1808 "service: {SERVICE}, path: {PATH}",
1809 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07001810 return false;
1811 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001812
1813 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1814 record.body.sensor_type = getSensorTypeFromPath(path);
1815 std::string type = getSensorTypeStringFromPath(path);
1816 auto typeCstr = type.c_str();
1817 auto findUnits = sensorUnits.find(typeCstr);
1818 if (findUnits != sensorUnits.end())
1819 {
1820 record.body.sensor_units_2_base =
1821 static_cast<uint8_t>(findUnits->second);
1822 } // else default 0x0 unspecified
1823
1824 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1825
Hao Jiangd2afd052020-12-10 15:09:32 -08001826 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001827 if (sensorObject == sensorMap.end())
1828 {
George Liude6694e2024-07-17 15:22:25 +08001829 lg2::error("constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001830 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001831 }
1832
1833 uint8_t entityId = 0;
1834 uint8_t entityInstance = 0x01;
1835
1836 // follow the association chain to get the parent board's entityid and
1837 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001838 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1839 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001840
1841 record.body.entity_id = entityId;
1842 record.body.entity_instance = entityInstance;
1843
Shakeeb Pasha93889722021-10-14 10:20:13 +05301844 double max = 0;
1845 double min = 0;
1846 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001847
1848 int16_t mValue = 0;
1849 int8_t rExp = 0;
1850 int16_t bValue = 0;
1851 int8_t bExp = 0;
1852 bool bSigned = false;
1853
1854 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1855 {
George Liude6694e2024-07-17 15:22:25 +08001856 lg2::error("constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001857 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001858 }
1859
1860 // The record.body is a struct SensorDataFullRecordBody
1861 // from sensorhandler.hpp in phosphor-ipmi-host.
1862 // The meaning of these bits appears to come from
1863 // table 43.1 of the IPMI spec.
1864 // The above 5 sensor attributes are stuffed in as follows:
1865 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1866 // Byte 22-24 are for other purposes
1867 // Byte 25 = MMMMMMMM = LSB of M
1868 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1869 // Byte 27 = BBBBBBBB = LSB of B
1870 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1871 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1872 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1873
1874 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1875 record.body.m_lsb = mValue & 0xFF;
1876
1877 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1878 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1879
1880 // move the smallest bit of the MSB into place (bit 9)
1881 // the MSbs are bits 7:8 in m_msb_and_tolerance
1882 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1883
1884 record.body.b_lsb = bValue & 0xFF;
1885
1886 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1887 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1888
1889 // move the smallest bit of the MSB into place (bit 9)
1890 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1891 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1892
1893 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1894 uint8_t rExpBits = rExp & 0x07;
1895
1896 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1897 uint8_t bExpBits = bExp & 0x07;
1898
1899 // move rExp and bExp into place
Patrick Williams1318a5e2024-08-16 15:19:54 -04001900 record.body.r_b_exponents =
1901 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001902
1903 // Set the analog reading byte interpretation accordingly
1904 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1905
1906 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1907 // These seem redundant, but derivable from the above 5 attributes
1908 // Original comment said "todo fill out rest of units"
1909
1910 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001911 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001912 get_sdr::body::set_id_strlen(name.size(), &record.body);
1913 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001914 std::memcpy(record.body.id_string, name.c_str(),
1915 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001916
Josh Lehana55c9532020-10-28 21:59:06 -07001917 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001918 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001919
Jie Yangf0a89942021-07-29 15:30:25 -07001920 bool sensorSettable = false;
1921 auto mutability =
1922 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1923 if (mutability != sensorMap.end())
1924 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001925 sensorSettable =
1926 mappedVariant<bool>(mutability->second, "Mutable", false);
Jie Yangf0a89942021-07-29 15:30:25 -07001927 }
1928 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1929
1930 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001931 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001932
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001933 IPMIThresholds thresholdData;
1934 try
1935 {
1936 thresholdData = getIPMIThresholds(sensorMap);
1937 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001938 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001939 {
George Liude6694e2024-07-17 15:22:25 +08001940 lg2::error("constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001941 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001942 }
1943
1944 if (thresholdData.criticalHigh)
1945 {
1946 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1947 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1948 IPMISensorEventEnableThresholds::criticalThreshold);
1949 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1950 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1951 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1952 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1953 record.body.discrete_reading_setting_mask[0] |=
1954 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1955 }
1956 if (thresholdData.warningHigh)
1957 {
1958 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1959 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1960 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1961 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1962 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1963 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1964 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1965 record.body.discrete_reading_setting_mask[0] |=
1966 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1967 }
1968 if (thresholdData.criticalLow)
1969 {
1970 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1971 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1972 IPMISensorEventEnableThresholds::criticalThreshold);
1973 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1974 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1975 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1976 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1977 record.body.discrete_reading_setting_mask[0] |=
1978 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1979 }
1980 if (thresholdData.warningLow)
1981 {
1982 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1983 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1984 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1985 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1986 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1987 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1988 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1989 record.body.discrete_reading_setting_mask[0] |=
1990 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1991 }
1992
1993 // everything that is readable is setable
1994 record.body.discrete_reading_setting_mask[1] =
1995 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001996 return true;
1997}
1998
Scron Chang2703b022021-07-06 15:47:45 +08001999#ifdef FEATURE_HYBRID_SENSORS
2000// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08002001void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08002002 uint16_t recordID,
2003 ipmi::sensor::IdInfoMap::const_iterator sensor,
2004 get_sdr::SensorDataFullRecord& record)
2005{
2006 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2007
2008 record.body.entity_id = sensor->second.entityType;
2009 record.body.sensor_type = sensor->second.sensorType;
2010 record.body.event_reading_type = sensor->second.sensorReadingType;
2011 record.body.entity_instance = sensor->second.instance;
2012 if (ipmi::sensor::Mutability::Write ==
2013 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2014 {
2015 get_sdr::body::init_settable_state(true, &(record.body));
2016 }
2017
2018 auto id_string = sensor->second.sensorName;
2019
2020 if (id_string.empty())
2021 {
2022 id_string = sensor->second.sensorNameFunc(sensor->second);
2023 }
2024
2025 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2026 {
2027 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2028 &(record.body));
2029 }
2030 else
2031 {
2032 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2033 }
Paul Fertser51136982022-08-18 12:36:41 +00002034 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002035 std::strncpy(record.body.id_string, id_string.c_str(),
2036 get_sdr::body::get_id_strlen(&(record.body)));
2037}
2038#endif
2039
Hao Jiange39d4d82021-04-16 17:02:40 -07002040// Construct type 3 SDR header and key (for VR and other discrete sensors)
2041void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2042 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002043{
2044 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2045 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2046
2047 get_sdr::header::set_record_id(
2048 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2049
2050 record.header.sdr_version = ipmiSdrVersion;
2051 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2052 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2053 sizeof(get_sdr::SensorDataRecordHeader);
2054 record.key.owner_id = bmcI2CAddr;
2055 record.key.owner_lun = lun;
2056 record.key.sensor_number = sensornumber;
2057
2058 record.body.entity_id = 0x00;
2059 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002060}
Willy Tu61992ad2021-03-29 15:33:20 -07002061
Hao Jiange39d4d82021-04-16 17:02:40 -07002062// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002063bool constructVrSdr(ipmi::Context::ptr ctx,
2064 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2065 uint16_t sensorNum, uint16_t recordID,
2066 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002067 get_sdr::SensorDataEventRecord& record)
2068{
Hao Jiange39d4d82021-04-16 17:02:40 -07002069 constructEventSdrHeaderKey(sensorNum, recordID, record);
2070
2071 DbusInterfaceMap sensorMap;
2072 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2073 {
George Liude6694e2024-07-17 15:22:25 +08002074 lg2::error("Failed to update sensor map for VR sensor, "
2075 "service: {SERVICE}, path: {PATH}",
2076 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07002077 return false;
2078 }
Willy Tu61992ad2021-03-29 15:33:20 -07002079 // follow the association chain to get the parent board's entityid and
2080 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002081 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2082 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002083 record.body.entity_instance);
2084
2085 // Sensor type is hardcoded as a module/board type instead of parsing from
2086 // sensor path. This is because VR control is allocated in an independent
2087 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2088 // types.
2089 static constexpr const uint8_t module_board_type = 0x15;
2090 record.body.sensor_type = module_board_type;
2091 record.body.event_reading_type = 0x00;
2092
2093 record.body.sensor_record_sharing_1 = 0x00;
2094 record.body.sensor_record_sharing_2 = 0x00;
2095
2096 // populate sensor name from path
2097 auto name = sensor::parseSdrIdFromPath(path);
2098 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002099 get_sdr::body::set_id_strlen(nameSize, &record.body);
2100 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002101 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2102 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2103
2104 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002105 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002106
2107 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002108}
2109
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002110uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002111{
2112 return std::min(getSensorTree().size(), maxIPMISensors);
2113}
2114
Willy Tu4eca2512022-06-20 21:14:51 -07002115static int getSensorDataRecord(
2116 ipmi::Context::ptr ctx,
2117 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2118 std::vector<uint8_t>& recordData, uint16_t recordID,
2119 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002120{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302121 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002122 size_t lastRecord = ipmi::getNumberOfSensors() +
2123 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2124 uint16_t nextRecord(recordID + 1);
2125
Willy Tu38e7a2b2021-03-29 15:09:56 -07002126 if (recordID == lastRecordIndex)
2127 {
2128 recordID = lastRecord;
2129 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002130 if (recordID == lastRecord)
2131 {
2132 nextRecord = lastRecordIndex;
2133 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002134 if (recordID > lastRecord)
2135 {
George Liude6694e2024-07-17 15:22:25 +08002136 lg2::error("getSensorDataRecord: recordID > lastRecord error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002137 return GENERAL_ERROR;
2138 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002139 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002140 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002141 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2142 recordData);
2143 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002144 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002145 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002146 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002147 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002148 }
2149
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002150 // Perform a incremental scan of the SDR Record ID's and translate the
2151 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2152 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2153 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2154 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002155 std::string connection;
2156 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002157 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002158 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002159 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002160 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002161 // LUN 0 has one reserved sensor number. Compensate here by adding one
2162 // to the record ID
2163 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002164 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002165 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002166 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002167 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002168 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2169 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2170 // rules governing its use.
2171 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002172 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002173 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002174
Patrick Williams1318a5e2024-08-16 15:19:54 -04002175 auto status =
2176 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2177 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002178 if (status)
2179 {
George Liude6694e2024-07-17 15:22:25 +08002180 lg2::error("getSensorDataRecord: getSensorConnection error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002181 return GENERAL_ERROR;
2182 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002183 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002184 // Return an error on LUN 2 assingments, and any sensor number beyond the
2185 // range of LUN 3
2186 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2187 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002188 {
George Liude6694e2024-07-17 15:22:25 +08002189 lg2::error("getSensorDataRecord: invalidSensorNumber");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002190 return GENERAL_ERROR;
2191 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002192 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2193 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2194
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002195 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2196 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002197 {
George Liude6694e2024-07-17 15:22:25 +08002198 lg2::error("getSensorDataRecord: sensor record mismatch");
Johnathan Manteyce982772021-07-28 15:08:30 -07002199 return GENERAL_ERROR;
2200 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002201
Willy Tu38e7a2b2021-03-29 15:09:56 -07002202 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002203 if (std::find(interfaces.begin(), interfaces.end(),
2204 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002205 {
Willy Tu11d68892022-01-20 10:37:34 -08002206 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002207
Hao Jiange39d4d82021-04-16 17:02:40 -07002208 // If the request doesn't read SDR body, construct only header and key
2209 // part to avoid additional DBus transaction.
2210 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2211 {
2212 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2213 }
Willy Tu4eca2512022-06-20 21:14:51 -07002214 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2215 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002216 {
2217 return GENERAL_ERROR;
2218 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002219
selvaganapathi7b2e5502023-02-14 07:10:47 +05302220 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2221 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002222
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002223 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002224 }
Willy Tu61992ad2021-03-29 15:33:20 -07002225
Scron Chang2703b022021-07-06 15:47:45 +08002226#ifdef FEATURE_HYBRID_SENSORS
2227 if (auto sensor = findStaticSensor(path);
2228 sensor != ipmi::sensor::sensors.end() &&
2229 getSensorEventTypeFromPath(path) !=
2230 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2231 {
Willy Tu11d68892022-01-20 10:37:34 -08002232 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002233
2234 // If the request doesn't read SDR body, construct only header and key
2235 // part to avoid additional DBus transaction.
2236 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2237 {
2238 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2239 }
2240 else
2241 {
2242 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2243 }
2244
selvaganapathi7b2e5502023-02-14 07:10:47 +05302245 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2246 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002247
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002248 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002249 }
2250#endif
2251
Willy Tu61992ad2021-03-29 15:33:20 -07002252 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002253 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2254 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002255 {
Willy Tu11d68892022-01-20 10:37:34 -08002256 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002257
Hao Jiange39d4d82021-04-16 17:02:40 -07002258 // If the request doesn't read SDR body, construct only header and key
2259 // part to avoid additional DBus transaction.
2260 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2261 {
2262 constructEventSdrHeaderKey(sensorNum, recordID, record);
2263 }
Willy Tu4eca2512022-06-20 21:14:51 -07002264 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2265 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002266 {
2267 return GENERAL_ERROR;
2268 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302269 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2270 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002271 }
2272
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002273 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002274}
2275
2276/** @brief implements the get SDR Info command
2277 * @param count - Operation
2278 *
2279 * @returns IPMI completion code plus response data
2280 * - sdrCount - sensor/SDR count
2281 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2282 */
2283static ipmi::RspType<uint8_t, // respcount
2284 uint8_t, // dynamic population flags
2285 uint32_t // last time a sensor was added
2286 >
2287 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2288 std::optional<uint8_t> count)
2289{
2290 auto& sensorTree = getSensorTree();
2291 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002292 uint16_t recordID = 0;
2293 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002294 // Sensors are dynamically allocated, and there is at least one LUN
2295 uint8_t lunsAndDynamicPopulation = 0x80;
2296 constexpr uint8_t getSdrCount = 0x01;
2297 constexpr uint8_t getSensorCount = 0x00;
2298
2299 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2300 {
2301 return ipmi::responseResponseError();
2302 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002303 uint16_t numSensors = ipmi::getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002304 if (count.value_or(0) == getSdrCount)
2305 {
Willy Tu4eca2512022-06-20 21:14:51 -07002306 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2307
Harvey Wu75893062023-03-22 17:17:31 +08002308 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002309 {
2310 recordID += maxSensorsPerLUN;
2311 }
Harvey Wu75893062023-03-22 17:17:31 +08002312 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002313 {
2314 recordID += maxSensorsPerLUN * 2;
2315 }
2316
Harvey Wu75893062023-03-22 17:17:31 +08002317 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2318 // assigned to the LUN
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002319 while (getSensorDataRecord(ctx,
2320 ipmiDecoratorPaths.value_or(
2321 std::unordered_set<std::string>()),
2322 record, recordID++) >= 0)
Willy Tude54f482021-01-26 15:59:09 -08002323 {
2324 get_sdr::SensorDataRecordHeader* hdr =
2325 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002326 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302327 if (!hdr)
2328 {
2329 continue;
2330 }
2331
2332 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002333 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002334 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002335 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002336 record.data());
2337 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002338 {
2339 sdrCount++;
2340 }
2341 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302342 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2343 {
2344 get_sdr::SensorDataCompactRecord* recordData =
2345 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2346 record.data());
2347 if (ctx->lun == recordData->key.owner_lun)
2348 {
2349 sdrCount++;
2350 }
2351 }
Harvey Wua3476272023-03-22 10:09:38 +08002352 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2353 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302354 {
2355 sdrCount++;
2356 }
Harvey Wua3476272023-03-22 10:09:38 +08002357
2358 // Because response count data is 1 byte, so sdrCount need to avoid
2359 // overflow.
2360 if (sdrCount == maxSensorsPerLUN)
2361 {
2362 break;
2363 }
Willy Tude54f482021-01-26 15:59:09 -08002364 }
2365 }
2366 else if (count.value_or(0) == getSensorCount)
2367 {
2368 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002369 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002370 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002371 sdrCount =
2372 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002373 }
Harvey Wu75893062023-03-22 17:17:31 +08002374 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002375 {
2376 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2377 ? maxSensorsPerLUN
2378 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2379 }
Harvey Wu75893062023-03-22 17:17:31 +08002380 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002381 {
2382 if (numSensors <= maxIPMISensors)
2383 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002384 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2385 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002386 }
2387 else
2388 {
2389 // error
2390 throw std::out_of_range(
2391 "Maximum number of IPMI sensors exceeded.");
2392 }
2393 }
2394 }
2395 else
2396 {
2397 return ipmi::responseInvalidFieldRequest();
2398 }
2399
2400 // Get Sensor count. This returns the number of sensors
2401 if (numSensors > 0)
2402 {
2403 lunsAndDynamicPopulation |= 1;
2404 }
2405 if (numSensors > maxSensorsPerLUN)
2406 {
2407 lunsAndDynamicPopulation |= 2;
2408 }
2409 if (numSensors >= (maxSensorsPerLUN * 2))
2410 {
2411 lunsAndDynamicPopulation |= 8;
2412 }
2413 if (numSensors > maxIPMISensors)
2414 {
2415 // error
2416 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2417 }
2418
2419 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2420 sdrLastAdd);
2421}
2422
2423/* end sensor commands */
2424
2425/* storage commands */
2426
2427ipmi::RspType<uint8_t, // sdr version
2428 uint16_t, // record count
2429 uint16_t, // free space
2430 uint32_t, // most recent addition
2431 uint32_t, // most recent erase
2432 uint8_t // operationSupport
2433 >
2434 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2435{
Willy Tude54f482021-01-26 15:59:09 -08002436 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Patrick Williams1318a5e2024-08-16 15:19:54 -04002437 uint16_t recordCount =
2438 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002439
2440 uint8_t operationSupport = static_cast<uint8_t>(
2441 SdrRepositoryInfoOps::overflow); // write not supported
2442
2443 operationSupport |=
2444 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2445 operationSupport |= static_cast<uint8_t>(
2446 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2447 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2448 unspecifiedFreeSpace, sdrLastAdd,
2449 sdrLastRemove, operationSupport);
2450}
2451
2452/** @brief implements the get SDR allocation info command
2453 *
2454 * @returns IPMI completion code plus response data
2455 * - allocUnits - Number of possible allocation units
2456 * - allocUnitSize - Allocation unit size in bytes.
2457 * - allocUnitFree - Number of free allocation units
2458 * - allocUnitLargestFree - Largest free block in allocation units
2459 * - maxRecordSize - Maximum record size in allocation units.
2460 */
2461ipmi::RspType<uint16_t, // allocUnits
2462 uint16_t, // allocUnitSize
2463 uint16_t, // allocUnitFree
2464 uint16_t, // allocUnitLargestFree
2465 uint8_t // maxRecordSize
2466 >
2467 ipmiStorageGetSDRAllocationInfo()
2468{
2469 // 0000h unspecified number of alloc units
2470 constexpr uint16_t allocUnits = 0;
2471
2472 constexpr uint16_t allocUnitFree = 0;
2473 constexpr uint16_t allocUnitLargestFree = 0;
2474 // only allow one block at a time
2475 constexpr uint8_t maxRecordSize = 1;
2476
2477 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2478 allocUnitLargestFree, maxRecordSize);
2479}
2480
2481/** @brief implements the reserve SDR command
2482 * @returns IPMI completion code plus response data
2483 * - sdrReservationID
2484 */
2485ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2486{
2487 sdrReservationID++;
2488 if (sdrReservationID == 0)
2489 {
2490 sdrReservationID++;
2491 }
2492
2493 return ipmi::responseSuccess(sdrReservationID);
2494}
2495
2496ipmi::RspType<uint16_t, // next record ID
2497 std::vector<uint8_t> // payload
2498 >
2499 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2500 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2501{
2502 // reservation required for partial reads with non zero offset into
2503 // record
2504 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2505 {
George Liude6694e2024-07-17 15:22:25 +08002506 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002507 return ipmi::responseInvalidReservationId();
2508 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002509
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002510 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002511 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002512 {
George Liude6694e2024-07-17 15:22:25 +08002513 lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002514 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002515 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002516
Willy Tu4eca2512022-06-20 21:14:51 -07002517 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2518
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002519 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002520 int nextRecordId = getSensorDataRecord(
2521 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2522 record, recordID, offset + bytesToRead);
2523
2524 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002525 {
George Liude6694e2024-07-17 15:22:25 +08002526 lg2::error("ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002527 return ipmi::responseInvalidFieldRequest();
2528 }
Willy Tude54f482021-01-26 15:59:09 -08002529 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002530 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002531 if (!hdr)
2532 {
George Liude6694e2024-07-17 15:22:25 +08002533 lg2::error("ipmiStorageGetSDR: record header is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002534 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002535 }
2536
Patrick Williams1318a5e2024-08-16 15:19:54 -04002537 size_t sdrLength =
2538 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002539 if (offset >= sdrLength)
2540 {
George Liude6694e2024-07-17 15:22:25 +08002541 lg2::error("ipmiStorageGetSDR: offset is outside the record");
Vernon Mauery6dbea082023-07-21 11:43:00 -07002542 return ipmi::responseParmOutOfRange();
2543 }
Willy Tude54f482021-01-26 15:59:09 -08002544 if (sdrLength < (offset + bytesToRead))
2545 {
2546 bytesToRead = sdrLength - offset;
2547 }
2548
2549 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2550 if (!respStart)
2551 {
George Liude6694e2024-07-17 15:22:25 +08002552 lg2::error("ipmiStorageGetSDR: record is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002553 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002554 }
2555
2556 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002557
Willy Tude54f482021-01-26 15:59:09 -08002558 return ipmi::responseSuccess(nextRecordId, recordData);
2559}
adarshgrami042e9db2022-09-15 10:34:34 +05302560namespace dcmi
2561{
2562
Thang Tranb1416ef2023-08-02 13:57:09 +07002563std::tuple<uint8_t, // Total of instance sensors
2564 std::vector<sensorInfo> // The list of sensors
2565 >
2566 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2567 uint8_t entityInstance, uint8_t instanceStart)
2568{
2569 std::vector<sensorInfo> sensorList;
2570 uint8_t totalInstSensor = 0;
2571 auto match = ipmi::dcmi::validEntityId.find(entityId);
2572
2573 if (match == ipmi::dcmi::validEntityId.end())
2574 {
2575 return std::make_tuple(totalInstSensor, sensorList);
2576 }
2577
2578 auto& sensorTree = getSensorTree();
2579 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2580 {
2581 return std::make_tuple(totalInstSensor, sensorList);
2582 }
2583
2584 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2585
Willy Tu62e3ca82024-01-31 17:28:34 +00002586 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002587 for (const auto& sensor : sensorTree)
2588 {
2589 const std::string& sensorObjPath = sensor.first;
2590 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2591
2592 /*
2593 * In the DCMI specification, it only supports the sensor type is 0x01
2594 * (temperature type) for both Get Sensor Info and Get Temperature
2595 * Readings commands.
2596 */
2597 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2598 {
2599 continue;
2600 }
2601
2602 const auto& connection = sensor.second.begin()->first;
2603 DbusInterfaceMap sensorMap;
2604
2605 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2606 sensorMapSdrUpdatePeriod))
2607 {
George Liude6694e2024-07-17 15:22:25 +08002608 lg2::error("Failed to update sensor map for threshold sensor, "
2609 "service: {SERVICE}, path: {PATH}",
2610 "SERVICE", connection, "PATH", sensorObjPath);
Thang Tranb1416ef2023-08-02 13:57:09 +07002611 continue;
2612 }
2613
2614 uint8_t entityIdValue = 0;
2615 uint8_t entityInstanceValue = 0;
2616
2617 /*
2618 * Get the Entity ID, Entity Instance information which are configured
2619 * in the Entity-Manger.
2620 */
2621 updateIpmiFromAssociation(
2622 sensorObjPath,
2623 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2624 sensorMap, entityIdValue, entityInstanceValue);
2625
2626 if (entityIdValue == match->first || entityIdValue == match->second)
2627 {
2628 totalInstSensor++;
2629
2630 /*
2631 * When Entity Instance parameter is not 0, we only get the first
2632 * sensor whose Entity Instance number is equal input Entity
2633 * Instance parameter.
2634 */
2635 if (entityInstance)
2636 {
2637 if (!sensorList.empty())
2638 {
2639 continue;
2640 }
2641
2642 if (entityInstanceValue == entityInstance)
2643 {
2644 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002645 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002646 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002647 ++invalidSensorNumberErrCount;
2648 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002649 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002650 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2651 recordId, entityIdValue,
2652 entityInstanceValue);
2653 }
2654 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002655 else if (entityInstanceValue >= instanceStart)
2656 {
2657 auto recordId = getSensorNumberFromPath(sensorObjPath);
2658 if (recordId == invalidSensorNumber)
2659 {
2660 ++invalidSensorNumberErrCount;
2661 continue;
2662 }
2663 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2664 recordId, entityIdValue,
2665 entityInstanceValue);
2666 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002667 }
2668 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002669 if (invalidSensorNumberErrCount != 0)
2670 {
George Liude6694e2024-07-17 15:22:25 +08002671 lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2672 "{ERR_COUNT} times",
2673 "ERR_COUNT", invalidSensorNumberErrCount);
Willy Tu62e3ca82024-01-31 17:28:34 +00002674 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002675
2676 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2677 return first.entityInstance <= second.entityInstance;
2678 };
2679
2680 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2681
2682 return std::make_tuple(totalInstSensor, sensorList);
2683}
2684
Thang Tran3dad8262023-08-17 15:20:56 +07002685std::tuple<bool, // Reading result
2686 uint7_t, // Temp value
2687 bool> // Sign bit
2688 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2689{
2690 std::string service{};
2691 boost::system::error_code ec =
2692 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2693 if (ec.value())
2694 {
2695 return std::make_tuple(false, 0, false);
2696 }
2697
2698 ipmi::PropertyMap properties{};
2699 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2700 sensor::sensorInterface, properties);
2701 if (ec.value())
2702 {
2703 return std::make_tuple(false, 0, false);
2704 }
2705
2706 auto scaleIt = properties.find("Scale");
2707 double scaleVal = 0.0;
2708 if (scaleIt != properties.end())
2709 {
2710 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2711 }
2712
2713 auto tempValIt = properties.find("Value");
2714 double tempVal = 0.0;
2715 if (tempValIt == properties.end())
2716 {
2717 return std::make_tuple(false, 0, false);
2718 }
2719
2720 const double maxTemp = 127;
2721 double absTempVal = 0.0;
2722 bool signBit = false;
2723
2724 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2725 tempVal = std::pow(10, scaleVal) * tempVal;
2726 absTempVal = std::abs(tempVal);
2727 absTempVal = std::min(absTempVal, maxTemp);
2728 signBit = (tempVal < 0) ? true : false;
2729
2730 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2731}
2732
adarshgrami042e9db2022-09-15 10:34:34 +05302733ipmi::RspType<uint8_t, // No of instances for requested id
2734 uint8_t, // No of record ids in the response
2735 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2736 // IDs
2737 >
2738 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002739 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302740{
2741 auto match = ipmi::dcmi::validEntityId.find(entityId);
2742 if (match == ipmi::dcmi::validEntityId.end())
2743 {
George Liude6694e2024-07-17 15:22:25 +08002744 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
adarshgrami042e9db2022-09-15 10:34:34 +05302745
2746 return ipmi::responseInvalidFieldRequest();
2747 }
2748
2749 if (sensorType != ipmi::dcmi::temperatureSensorType)
2750 {
George Liude6694e2024-07-17 15:22:25 +08002751 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2752 sensorType);
adarshgrami042e9db2022-09-15 10:34:34 +05302753
2754 return ipmi::responseInvalidFieldRequest();
2755 }
adarshgrami042e9db2022-09-15 10:34:34 +05302756
2757 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002758 const auto& [totalSensorInst, sensorList] =
2759 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302760
Thang Tranb1416ef2023-08-02 13:57:09 +07002761 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302762 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002763 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2764 }
adarshgrami042e9db2022-09-15 10:34:34 +05302765
Thang Tranb1416ef2023-08-02 13:57:09 +07002766 /*
2767 * As DCMI specification, the maximum number of Record Ids of response data
2768 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2769 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2770 * in response data.
2771 */
2772 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2773
2774 for (const auto& sensor : sensorList)
2775 {
2776 sensorRec.emplace_back(sensor.recordId);
2777 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302778 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002779 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302780 }
2781 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002782
2783 return ipmi::responseSuccess(
2784 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302785}
Thang Tran3dad8262023-08-17 15:20:56 +07002786
2787ipmi::RspType<uint8_t, // No of instances for requested id
2788 uint8_t, // No of record ids in the response
2789 std::vector< // Temperature Data
2790 std::tuple<uint7_t, // Temperature value
2791 bool, // Sign bit
2792 uint8_t // Entity Instance of sensor
2793 >>>
2794 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2795 uint8_t entityId, uint8_t entityInstance,
2796 uint8_t instanceStart)
2797{
2798 auto match = ipmi::dcmi::validEntityId.find(entityId);
2799 if (match == ipmi::dcmi::validEntityId.end())
2800 {
George Liude6694e2024-07-17 15:22:25 +08002801 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Thang Tran3dad8262023-08-17 15:20:56 +07002802
2803 return ipmi::responseInvalidFieldRequest();
2804 }
2805
2806 if (sensorType != ipmi::dcmi::temperatureSensorType)
2807 {
George Liude6694e2024-07-17 15:22:25 +08002808 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2809 sensorType);
Thang Tran3dad8262023-08-17 15:20:56 +07002810
2811 return ipmi::responseInvalidFieldRequest();
2812 }
2813
2814 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2815 const auto& [totalSensorInst, sensorList] =
2816 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2817
2818 if (sensorList.empty())
2819 {
2820 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2821 }
2822
2823 /*
2824 * As DCMI specification, the maximum number of Record Ids of response data
2825 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2826 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2827 * in response data.
2828 */
2829 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2830
2831 for (const auto& sensor : sensorList)
2832 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002833 const auto& [readResult, tempVal, signBit] =
2834 readTemp(ctx, sensor.objectPath);
Thang Tran3dad8262023-08-17 15:20:56 +07002835
2836 if (readResult)
2837 {
2838 tempReadingVal.emplace_back(
2839 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2840
2841 if (tempReadingVal.size() >= numOfRec)
2842 {
2843 break;
2844 }
2845 }
2846 }
2847
2848 return ipmi::responseSuccess(totalSensorInst,
2849 static_cast<uint8_t>(tempReadingVal.size()),
2850 tempReadingVal);
2851}
2852
adarshgrami042e9db2022-09-15 10:34:34 +05302853} // namespace dcmi
2854
Willy Tude54f482021-01-26 15:59:09 -08002855/* end storage commands */
2856
2857void registerSensorFunctions()
2858{
2859 // <Platform Event>
2860 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2861 ipmi::sensor_event::cmdPlatformEvent,
2862 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2863
Willy Tudbafbce2021-03-29 00:37:05 -07002864 // <Set Sensor Reading and Event Status>
2865 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2866 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2867 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002868
Willy Tude54f482021-01-26 15:59:09 -08002869 // <Get Sensor Reading>
2870 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2871 ipmi::sensor_event::cmdGetSensorReading,
2872 ipmi::Privilege::User, ipmiSenGetSensorReading);
2873
2874 // <Get Sensor Threshold>
2875 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2876 ipmi::sensor_event::cmdGetSensorThreshold,
2877 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2878
2879 // <Set Sensor Threshold>
2880 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2881 ipmi::sensor_event::cmdSetSensorThreshold,
2882 ipmi::Privilege::Operator,
2883 ipmiSenSetSensorThresholds);
2884
2885 // <Get Sensor Event Enable>
2886 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2887 ipmi::sensor_event::cmdGetSensorEventEnable,
2888 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2889
2890 // <Get Sensor Event Status>
2891 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2892 ipmi::sensor_event::cmdGetSensorEventStatus,
2893 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2894
2895 // register all storage commands for both Sensor and Storage command
2896 // versions
2897
2898 // <Get SDR Repository Info>
2899 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2900 ipmi::storage::cmdGetSdrRepositoryInfo,
2901 ipmi::Privilege::User,
2902 ipmiStorageGetSDRRepositoryInfo);
2903
2904 // <Get Device SDR Info>
2905 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2906 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2907 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2908
2909 // <Get SDR Allocation Info>
2910 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2911 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2912 ipmi::Privilege::User,
2913 ipmiStorageGetSDRAllocationInfo);
2914
2915 // <Reserve SDR Repo>
2916 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2917 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2918 ipmi::Privilege::User, ipmiStorageReserveSDR);
2919
2920 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2921 ipmi::storage::cmdReserveSdrRepository,
2922 ipmi::Privilege::User, ipmiStorageReserveSDR);
2923
2924 // <Get Sdr>
2925 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2926 ipmi::sensor_event::cmdGetDeviceSdr,
2927 ipmi::Privilege::User, ipmiStorageGetSDR);
2928
2929 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2930 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2931 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302932 // <Get DCMI Sensor Info>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002933 ipmi::registerGroupHandler(
2934 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2935 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2936 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002937 // <Get Temperature Readings>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002938 ipmi::registerGroupHandler(
2939 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2940 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2941 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002942}
2943} // namespace ipmi