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