blob: 1c4930feeef1f9f25f10b12f2455efacfa050e06 [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>
Alexander Hansenf365c7f2025-11-14 12:08:34 +010034#include <xyz/openbmc_project/Sensor/Value/common.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050035
36#include <algorithm>
37#include <array>
Willy Tude54f482021-01-26 15:59:09 -080038#include <chrono>
39#include <cmath>
40#include <cstring>
Willy Tu62e3ca82024-01-31 17:28:34 +000041#include <format>
Willy Tude54f482021-01-26 15:59:09 -080042#include <map>
Willy Tude54f482021-01-26 15:59:09 -080043#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080044#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48
Alexander Hansenf365c7f2025-11-14 12:08:34 +010049using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
50
Scron Chang2703b022021-07-06 15:47:45 +080051#ifdef FEATURE_HYBRID_SENSORS
52
53#include "sensordatahandler.hpp"
54namespace ipmi
55{
56namespace sensor
57{
58extern const IdInfoMap sensors;
59} // namespace sensor
60} // namespace ipmi
61#endif
adarshgrami042e9db2022-09-15 10:34:34 +053062namespace ipmi
63{
64namespace dcmi
65{
66// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
67static const std::map<uint8_t, uint8_t> validEntityId{
68 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
69 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
70constexpr uint8_t temperatureSensorType = 0x01;
71constexpr uint8_t maxRecords = 8;
72} // namespace dcmi
73} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080074constexpr std::array<const char*, 7> suffixes = {
75 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
76 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080077namespace ipmi
78{
Hao Jiangd48c9212021-02-03 15:45:06 -080079
80using phosphor::logging::entry;
81using phosphor::logging::level;
82using phosphor::logging::log;
83
Willy Tude54f482021-01-26 15:59:09 -080084static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070085static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080086
Willy Tu38e7a2b2021-03-29 15:09:56 -070087// BMC I2C address is generally at 0x20
88static constexpr uint8_t bmcI2CAddr = 0x20;
89
Willy Tude54f482021-01-26 15:59:09 -080090constexpr size_t maxSDRTotalSize =
91 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
92constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
93
94static uint16_t sdrReservationID;
95static uint32_t sdrLastAdd = noTimestamp;
96static uint32_t sdrLastRemove = noTimestamp;
97static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070098
99// The IPMI spec defines four Logical Units (LUN), each capable of supporting
100// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
101// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800102// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700103// location.
104static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
105
Harvey Wu75893062023-03-22 17:17:31 +0800106static constexpr uint8_t lun0 = 0x0;
107static constexpr uint8_t lun1 = 0x1;
108static constexpr uint8_t lun3 = 0x3;
109
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700110static constexpr size_t lun0MaxSensorNum = 0xfe;
111static constexpr size_t lun1MaxSensorNum = 0x1fe;
112static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800113static constexpr int GENERAL_ERROR = -1;
114
Willy Tude54f482021-01-26 15:59:09 -0800115static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
116
117// Specify the comparison required to sort and find char* map objects
118struct CmpStr
119{
120 bool operator()(const char* a, const char* b) const
121 {
122 return std::strcmp(a, b) < 0;
123 }
124};
125const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
126 sensorUnits{{{"temperature", SensorUnits::degreesC},
127 {"voltage", SensorUnits::volts},
128 {"current", SensorUnits::amps},
129 {"fan_tach", SensorUnits::rpm},
Johnathan Mantey7b037272024-06-17 12:12:40 -0700130 {"power", SensorUnits::watts},
131 {"energy", SensorUnits::joules}}};
Willy Tude54f482021-01-26 15:59:09 -0800132
133void registerSensorFunctions() __attribute__((constructor));
134
Patrick Williams5d82f472022-07-22 19:26:53 -0500135static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800136 *getSdBus(),
137 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
138 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500139 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400140 getSensorTree().clear();
141 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
142 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
143 std::chrono::system_clock::now().time_since_epoch())
144 .count();
145 });
Willy Tude54f482021-01-26 15:59:09 -0800146
Patrick Williams5d82f472022-07-22 19:26:53 -0500147static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800148 *getSdBus(),
149 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
150 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500151 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400152 getSensorTree().clear();
153 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
154 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
155 std::chrono::system_clock::now().time_since_epoch())
156 .count();
157 });
Willy Tude54f482021-01-26 15:59:09 -0800158
George Liu23c868c2025-07-04 09:31:35 +0800159ipmi::Cc getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
160 std::string& connection, std::string& path,
161 std::vector<std::string>* interfaces)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700162{
163 auto& sensorTree = getSensorTree();
164 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
165 {
George Liu879c1d82025-07-03 09:36:57 +0800166 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700167 }
168
169 if (ctx == nullptr)
170 {
George Liu879c1d82025-07-03 09:36:57 +0800171 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700172 }
173
174 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
175 if (path.empty())
176 {
George Liu879c1d82025-07-03 09:36:57 +0800177 return ipmi::ccInvalidFieldRequest;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700178 }
179
180 for (const auto& sensor : sensorTree)
181 {
182 if (path == sensor.first)
183 {
184 connection = sensor.second.begin()->first;
185 if (interfaces)
186 *interfaces = sensor.second.begin()->second;
187 break;
188 }
189 }
190
191 return 0;
192}
193
194SensorSubTree& getSensorTree()
195{
196 static SensorSubTree sensorTree;
197 return sensorTree;
198}
199
Willy Tude54f482021-01-26 15:59:09 -0800200// this keeps track of deassertions for sensor event status command. A
201// deasertion can only happen if an assertion was seen first.
202static boost::container::flat_map<
203 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
204 thresholdDeassertMap;
205
Patrick Williams5d82f472022-07-22 19:26:53 -0500206static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800207 *getSdBus(),
208 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
209 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500210 [](sdbusplus::message_t& m) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400211 boost::container::flat_map<std::string, std::variant<bool, double>>
212 values;
213 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800214
Patrick Williams1318a5e2024-08-16 15:19:54 -0400215 auto findAssert =
216 std::find_if(values.begin(), values.end(), [](const auto& pair) {
217 return pair.first.find("Alarm") != std::string::npos;
218 });
219 if (findAssert != values.end())
Willy Tude54f482021-01-26 15:59:09 -0800220 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400221 auto ptr = std::get_if<bool>(&(findAssert->second));
222 if (ptr == nullptr)
223 {
224 lg2::error("thresholdChanged: Assert non bool");
225 return;
226 }
227 if (*ptr)
Willy Tude54f482021-01-26 15:59:09 -0800228 {
George Liude6694e2024-07-17 15:22:25 +0800229 lg2::info(
Patrick Williams1318a5e2024-08-16 15:19:54 -0400230 "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
George Liude6694e2024-07-17 15:22:25 +0800231 "SENSOR_PATH", m.get_path());
Patrick Williams1318a5e2024-08-16 15:19:54 -0400232 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
233 }
234 else
235 {
236 auto& value =
237 thresholdDeassertMap[m.get_path()][findAssert->first];
238 if (value)
239 {
240 lg2::info(
241 "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
242 "SENSOR_PATH", m.get_path());
243 value = *ptr;
244 }
Willy Tude54f482021-01-26 15:59:09 -0800245 }
246 }
Patrick Williams1318a5e2024-08-16 15:19:54 -0400247 });
Willy Tude54f482021-01-26 15:59:09 -0800248
Hao Jiangd2afd052020-12-10 15:09:32 -0800249namespace sensor
250{
251static constexpr const char* vrInterface =
252 "xyz.openbmc_project.Control.VoltageRegulatorMode";
Hao Jiangd2afd052020-12-10 15:09:32 -0800253} // namespace sensor
254
Willy Tude54f482021-01-26 15:59:09 -0800255static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
256 double& min)
257{
258 max = 127;
259 min = -128;
260
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100261 auto sensorObject = sensorMap.find(SensorValue::interface);
Willy Tude54f482021-01-26 15:59:09 -0800262 auto critical =
263 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
264 auto warning =
265 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
266
267 if (sensorObject != sensorMap.end())
268 {
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100269 auto maxMap =
270 sensorObject->second.find(SensorValue::property_names::max_value);
271 auto minMap =
272 sensorObject->second.find(SensorValue::property_names::min_value);
Willy Tude54f482021-01-26 15:59:09 -0800273
274 if (maxMap != sensorObject->second.end())
275 {
276 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
277 }
278 if (minMap != sensorObject->second.end())
279 {
280 min = std::visit(VariantToDoubleVisitor(), minMap->second);
281 }
282 }
283 if (critical != sensorMap.end())
284 {
285 auto lower = critical->second.find("CriticalLow");
286 auto upper = critical->second.find("CriticalHigh");
287 if (lower != critical->second.end())
288 {
289 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300290 if (std::isfinite(value))
291 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700292 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300293 }
Willy Tude54f482021-01-26 15:59:09 -0800294 }
295 if (upper != critical->second.end())
296 {
297 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300298 if (std::isfinite(value))
299 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700300 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300301 }
Willy Tude54f482021-01-26 15:59:09 -0800302 }
303 }
304 if (warning != sensorMap.end())
305 {
Willy Tude54f482021-01-26 15:59:09 -0800306 auto lower = warning->second.find("WarningLow");
307 auto upper = warning->second.find("WarningHigh");
308 if (lower != warning->second.end())
309 {
310 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300311 if (std::isfinite(value))
312 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700313 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300314 }
Willy Tude54f482021-01-26 15:59:09 -0800315 }
316 if (upper != warning->second.end())
317 {
318 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300319 if (std::isfinite(value))
320 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700321 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300322 }
Willy Tude54f482021-01-26 15:59:09 -0800323 }
324 }
325}
326
327static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700328 std::string sensorPath, DbusInterfaceMap& sensorMap,
329 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800330{
Scron Chang2703b022021-07-06 15:47:45 +0800331#ifdef FEATURE_HYBRID_SENSORS
332 if (auto sensor = findStaticSensor(sensorPath);
333 sensor != ipmi::sensor::sensors.end() &&
334 getSensorEventTypeFromPath(sensorPath) !=
335 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
336 {
337 // If the incoming sensor is a discrete sensor, it might fail in
338 // getManagedObjects(), return true, and use its own getFunc to get
339 // value.
340 return true;
341 }
342#endif
343
Willy Tude54f482021-01-26 15:59:09 -0800344 static boost::container::flat_map<
345 std::string, std::chrono::time_point<std::chrono::steady_clock>>
346 updateTimeMap;
347
348 auto updateFind = updateTimeMap.find(sensorConnection);
349 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
350 if (updateFind != updateTimeMap.end())
351 {
352 lastUpdate = updateFind->second;
353 }
354
355 auto now = std::chrono::steady_clock::now();
356
357 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700358 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800359 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800360 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800361
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800362 // Object managers for different kinds of OpenBMC DBus interfaces.
363 // Documented in the phosphor-dbus-interfaces repository.
364 const char* paths[] = {
365 "/xyz/openbmc_project/sensors",
366 "/xyz/openbmc_project/vr",
367 };
George Liuf2807ed2025-04-03 15:52:57 +0800368 constexpr size_t numPaths = sizeof(paths) / sizeof(paths[0]);
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800369 ObjectValueTree allManagedObjects;
370
George Liuf2807ed2025-04-03 15:52:57 +0800371 for (size_t i = 0; i < numPaths; i++)
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800372 {
373 ObjectValueTree managedObjects;
374 boost::system::error_code ec = getManagedObjects(
375 ctx, sensorConnection.c_str(), paths[i], managedObjects);
376 if (ec)
377 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800378 continue;
379 }
380 allManagedObjects.merge(managedObjects);
381 found = true;
382 }
383
384 if (!found)
385 {
George Liude6694e2024-07-17 15:22:25 +0800386 lg2::error("GetMangagedObjects for getSensorMap failed, "
387 "service: {SERVICE}",
388 "SERVICE", sensorConnection);
Tom Tung6615d472023-05-31 18:48:12 +0800389
Willy Tude54f482021-01-26 15:59:09 -0800390 return false;
391 }
392
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800393 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700394 // Update time after finish building the map which allow the
395 // data to be cached for updatePeriod plus the build time.
396 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800397 }
398 auto connection = SensorCache.find(sensorConnection);
399 if (connection == SensorCache.end())
400 {
401 return false;
402 }
403 auto path = connection->second.find(sensorPath);
404 if (path == connection->second.end())
405 {
406 return false;
407 }
408 sensorMap = path->second;
409
410 return true;
411}
412
Hao Jiangd2afd052020-12-10 15:09:32 -0800413namespace sensor
414{
Hao Jiangd48c9212021-02-03 15:45:06 -0800415// Read VR profiles from sensor(daemon) interface
Patrick Williams69b4c282025-03-03 11:19:13 -0500416static std::optional<std::vector<std::string>> getSupportedVrProfiles(
417 const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800418{
419 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800420 auto supportedProperty = object.find("Supported");
421 if (supportedProperty == object.end() ||
422 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800423 {
George Liude6694e2024-07-17 15:22:25 +0800424 lg2::error("Missing the required Supported and Selected properties");
Hao Jiangd2afd052020-12-10 15:09:32 -0800425 return std::nullopt;
426 }
427
428 const auto profilesPtr =
429 std::get_if<std::vector<std::string>>(&supportedProperty->second);
430
431 if (profilesPtr == nullptr)
432 {
George Liude6694e2024-07-17 15:22:25 +0800433 lg2::error("property is not array of string");
Hao Jiangd2afd052020-12-10 15:09:32 -0800434 return std::nullopt;
435 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800436 return *profilesPtr;
437}
438
439// Calculate VR Mode from input IPMI discrete event bytes
Patrick Williams1318a5e2024-08-16 15:19:54 -0400440static std::optional<std::string> calculateVRMode(
441 uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
Hao Jiangd48c9212021-02-03 15:45:06 -0800442{
443 // get VR mode profiles from Supported Interface
444 auto profiles = getSupportedVrProfiles(VRObject);
445 if (!profiles)
446 {
447 return std::nullopt;
448 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800449
450 // interpret IPMI cmd bits into profiles' index
451 long unsigned int index = 0;
452 // only one bit should be set and the highest bit should not be used.
453 if (assertOffset == 0 || assertOffset == (1u << 15) ||
454 (assertOffset & (assertOffset - 1)))
455 {
George Liude6694e2024-07-17 15:22:25 +0800456 lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
457 lg2::hex, static_cast<uint16_t>(assertOffset));
Hao Jiangd2afd052020-12-10 15:09:32 -0800458 return std::nullopt;
459 }
460
461 while (assertOffset != 1)
462 {
463 assertOffset >>= 1;
464 index++;
465 }
466
Hao Jiangd48c9212021-02-03 15:45:06 -0800467 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800468 {
George Liude6694e2024-07-17 15:22:25 +0800469 lg2::error("profile index out of boundary");
Hao Jiangd2afd052020-12-10 15:09:32 -0800470 return std::nullopt;
471 }
472
Hao Jiangd48c9212021-02-03 15:45:06 -0800473 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800474}
475
476// Calculate sensor value from IPMI reading byte
Patrick Williams69b4c282025-03-03 11:19:13 -0500477static std::optional<double> calculateValue(
478 uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
479 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
Hao Jiangd2afd052020-12-10 15:09:32 -0800480{
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100481 if (valueObject.find(SensorValue::property_names::value) ==
482 valueObject.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800483 {
George Liude6694e2024-07-17 15:22:25 +0800484 lg2::error("Missing the required Value property");
Hao Jiangd2afd052020-12-10 15:09:32 -0800485 return std::nullopt;
486 }
487
488 double max = 0;
489 double min = 0;
490 getSensorMaxMin(sensorMap, max, min);
491
492 int16_t mValue = 0;
493 int16_t bValue = 0;
494 int8_t rExp = 0;
495 int8_t bExp = 0;
496 bool bSigned = false;
497
498 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
499 {
500 return std::nullopt;
501 }
502
503 double value = bSigned ? ((int8_t)reading) : reading;
504
505 value *= ((double)mValue);
506 value += ((double)bValue) * std::pow(10.0, bExp);
507 value *= std::pow(10.0, rExp);
508
509 return value;
510}
511
Willy Tu38e7a2b2021-03-29 15:09:56 -0700512// Extract file name from sensor path as the sensors SDR ID. Simplify the name
513// if it is too long.
514std::string parseSdrIdFromPath(const std::string& path)
515{
516 std::string name;
517 size_t nameStart = path.rfind("/");
518 if (nameStart != std::string::npos)
519 {
520 name = path.substr(nameStart + 1, std::string::npos - nameStart);
521 }
522
Willy Tu38e7a2b2021-03-29 15:09:56 -0700523 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
524 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200525#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800526 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700527 {
George Liuc024b392025-08-21 16:38:58 +0800528 if (name.ends_with(suffix))
JeffLind950f412021-10-20 18:49:34 +0800529 {
530 boost::replace_all(name, suffix, "");
531 break;
532 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700533 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200534#endif
535#ifdef SHORTNAME_REPLACE_WORDS
536 constexpr std::array<std::pair<const char*, const char*>, 2>
537 replaceWords = {std::make_pair("Output", "Out"),
538 std::make_pair("Input", "In")};
539 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800540 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200541 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800542 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200543#endif
544
545 // as a backup and if nothing else is configured
546 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700547 }
548 return name;
549}
550
Hao Jiangd48c9212021-02-03 15:45:06 -0800551bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
552 const std::string& path,
553 const ipmi::DbusInterfaceMap::mapped_type& object,
554 std::bitset<16>& assertions)
555{
556 auto profiles = sensor::getSupportedVrProfiles(object);
557 if (!profiles)
558 {
559 return false;
560 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700561 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800562
563 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700564 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800565 if (ec)
566 {
George Liude6694e2024-07-17 15:22:25 +0800567 lg2::error("Failed to get Selected, path: {PATH}, "
568 "interface: {INTERFACE}, error: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100569 "PATH", path, "INTERFACE", SensorValue::interface, "ERROR",
George Liude6694e2024-07-17 15:22:25 +0800570 ec.message());
Hao Jiangd48c9212021-02-03 15:45:06 -0800571 return false;
572 }
573
Willy Tu8366f0b2022-04-29 05:00:17 -0700574 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800575 if (itr == profiles->end())
576 {
George Liude6694e2024-07-17 15:22:25 +0800577 lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
578 "PATH", path);
Hao Jiangd48c9212021-02-03 15:45:06 -0800579 return false;
580 }
581 std::size_t index =
582 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
583
Willy Tubef102a2022-06-09 15:36:09 -0700584 // map index to response event assertion bit.
585 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800586 {
Willy Tubef102a2022-06-09 15:36:09 -0700587 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800588 }
589 else
590 {
George Liude6694e2024-07-17 15:22:25 +0800591 lg2::error("VR profile index reaches max assertion bit, "
592 "path: {PATH}, index: {INDEX}",
593 "PATH", path, "INDEX", index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800594 return false;
595 }
596 if constexpr (debug)
597 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800598 lg2::error("VR sensor {PATH} mode is: [{INDEX}] {MODE}", "PATH",
599 sensor::parseSdrIdFromPath(path), "INDEX", index, "MODE",
600 mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800601 }
602 return true;
603}
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700604
605/*
606 * Handle every Sensor Data Record besides Type 01
607 *
608 * The D-Bus sensors work well for generating Type 01 SDRs.
609 * After the Type 01 sensors are processed the remaining sensor types require
610 * special handling. Each BMC vendor is going to have their own requirements for
611 * insertion of non-Type 01 records.
612 * Manage non-Type 01 records:
613 *
614 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
615 * Populate it with the two weakly linked functions below, without adding the
616 * 'weak' attribute definition prior to the function definition.
617 * getOtherSensorsCount(...)
618 * getOtherSensorsDataRecord(...)
619 * Example contents are provided in the weak definitions below
620 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
621 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
622 * The contents of the sensorcommands_oem.cpp file will then override the code
623 * provided below.
624 */
625
626size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
627size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
628{
629 size_t fruCount = 0;
630
631 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
632 if (ret != ipmi::ccSuccess)
633 {
George Liude6694e2024-07-17 15:22:25 +0800634 lg2::error("getOtherSensorsCount: getFruSdrCount error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700635 return std::numeric_limits<size_t>::max();
636 }
637
638 const auto& entityRecords =
639 ipmi::sensor::EntityInfoMapContainer::getContainer()
640 ->getIpmiEntityRecords();
641 size_t entityCount = entityRecords.size();
642
643 return fruCount + ipmi::storage::type12Count + entityCount;
644}
645
646int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
647 std::vector<uint8_t>& recordData)
648 __attribute__((weak));
649int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
650 std::vector<uint8_t>& recordData)
651{
652 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
653 if (otherCount == std::numeric_limits<size_t>::max())
654 {
655 return GENERAL_ERROR;
656 }
657 const auto& entityRecords =
658 ipmi::sensor::EntityInfoMapContainer::getContainer()
659 ->getIpmiEntityRecords();
660
661 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
662 size_t entityCount{entityRecords.size()};
663 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
664
665 if (sdrIndex > otherCount)
666 {
667 return std::numeric_limits<int>::min();
668 }
669 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
670 {
671 // handle type 8 entity map records
672 ipmi::sensor::EntityInfoMap::const_iterator entity =
673 entityRecords.find(static_cast<uint8_t>(
674 sdrIndex - fruCount - ipmi::storage::type12Count));
675
676 if (entity == entityRecords.end())
677 {
678 return GENERAL_ERROR;
679 }
680 recordData = ipmi::storage::getType8SDRs(entity, recordID);
681 }
682 else if (sdrIndex >= fruCount)
683 {
684 // handle type 12 hardcoded records
685 size_t type12Index = sdrIndex - fruCount;
686 if (type12Index >= ipmi::storage::type12Count)
687 {
George Liude6694e2024-07-17 15:22:25 +0800688 lg2::error("getSensorDataRecord: type12Index error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700689 return GENERAL_ERROR;
690 }
691 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
692 }
693 else
694 {
695 // handle fru records
696 get_sdr::SensorDataFruRecord data;
697 if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
George Liue8a97bd2024-12-05 17:26:46 +0800698 ret != ipmi::ccSuccess)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700699 {
700 return GENERAL_ERROR;
701 }
George Liuc7a4da52025-08-19 16:20:31 +0800702 data.header.recordId = recordID;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700703 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
704 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
705 }
706
707 return 0;
708}
709
Hao Jiangd2afd052020-12-10 15:09:32 -0800710} // namespace sensor
711
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000712ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
713 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800714{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000715 constexpr const uint8_t validEnvmRev = 0x04;
716 constexpr const uint8_t lastSensorType = 0x2C;
717 constexpr const uint8_t oemReserved = 0xC0;
718
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700719 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000720 uint8_t evmRev = 0;
721 uint8_t sensorType = 0;
722 uint8_t sensorNum = 0;
723 uint8_t eventType = 0;
724 uint8_t eventData1 = 0;
725 std::optional<uint8_t> eventData2 = 0;
726 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700727 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000728 ipmi::ChannelInfo chInfo;
729
730 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
731 {
George Liude6694e2024-07-17 15:22:25 +0800732 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
733 ctx->channel);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000734 return ipmi::responseUnspecifiedError();
735 }
736
737 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
738 ipmi::EChannelMediumType::systemInterface)
739 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700740 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000741 eventData1, eventData2, eventData3);
Johnathan Manteya440cd42024-06-20 13:21:48 -0700742 constexpr const uint8_t isSoftwareID = 0x01;
743 if (!(sysgeneratorID & isSoftwareID))
744 {
745 return ipmi::responseInvalidFieldRequest();
746 }
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700747 // Refer to IPMI Spec Table 32: SEL Event Records
748 generatorID = (ctx->channel << 12) // Channel
749 | (0x0 << 10) // Reserved
750 | (0x0 << 8) // 0x0 for sys-soft ID
Johnathan Manteya440cd42024-06-20 13:21:48 -0700751 | sysgeneratorID;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000752 }
753 else
754 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000755 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
756 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700757 // Refer to IPMI Spec Table 32: SEL Event Records
758 generatorID = (ctx->channel << 12) // Channel
759 | (0x0 << 10) // Reserved
760 | ((ctx->lun & 0x3) << 8) // Lun
761 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000762 }
763
764 if (!p.fullyUnpacked())
765 {
766 return ipmi::responseReqDataLenInvalid();
767 }
768
769 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
770 if (evmRev != validEnvmRev)
771 {
772 return ipmi::responseInvalidFieldRequest();
773 }
774 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
775 {
776 return ipmi::responseInvalidFieldRequest();
777 }
778
Willy Tude54f482021-01-26 15:59:09 -0800779 return ipmi::responseSuccess();
780}
781
Patrick Williams1318a5e2024-08-16 15:19:54 -0400782ipmi::RspType<> ipmiSetSensorReading(
783 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
784 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700785{
786 std::string connection;
787 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700788 std::vector<std::string> interfaces;
789
Patrick Williams1318a5e2024-08-16 15:19:54 -0400790 ipmi::Cc status =
791 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700792 if (status)
793 {
794 return ipmi::response(status);
795 }
796
Hao Jiangd2afd052020-12-10 15:09:32 -0800797 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700798 if (std::find(interfaces.begin(), interfaces.end(),
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100799 SensorValue::interface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700800 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700801 DbusInterfaceMap sensorMap;
802 if (!getSensorMap(ctx, connection, path, sensorMap))
803 {
804 return ipmi::responseResponseError();
805 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100806 auto sensorObject = sensorMap.find(SensorValue::interface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800807 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700808 {
809 return ipmi::responseResponseError();
810 }
811
Jie Yangf0a89942021-07-29 15:30:25 -0700812 // Only allow external SetSensor if write permission granted
Patrick Williams1318a5e2024-08-16 15:19:54 -0400813 if (!details::sdrWriteTable.getWritePermission(
814 (ctx->lun << 8) | sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700815 {
816 return ipmi::responseResponseError();
817 }
818
Patrick Williams1318a5e2024-08-16 15:19:54 -0400819 auto value =
820 sensor::calculateValue(reading, sensorMap, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800821 if (!value)
822 {
823 return ipmi::responseResponseError();
824 }
825
826 if constexpr (debug)
827 {
George Liude6694e2024-07-17 15:22:25 +0800828 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
829 "byte: {BYTE}, value: {VALUE}",
830 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
831 "VALUE", *value);
Hao Jiangd2afd052020-12-10 15:09:32 -0800832 }
833
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100834 boost::system::error_code ec = setDbusProperty(
835 ctx, connection, path, SensorValue::interface,
836 SensorValue::property_names::value, ipmi::Value(*value));
Hao Jiangd2afd052020-12-10 15:09:32 -0800837
838 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500839 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800840 // callback functions for now (e.g. ipmiSetSensorReading).
841 if (ec)
842 {
George Liude6694e2024-07-17 15:22:25 +0800843 lg2::error("Failed to set Value, path: {PATH}, "
844 "interface: {INTERFACE}, ERROR: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100845 "PATH", path, "INTERFACE", SensorValue::interface,
George Liude6694e2024-07-17 15:22:25 +0800846 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800847 return ipmi::responseResponseError();
848 }
849 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700850 }
851
Hao Jiange39d4d82021-04-16 17:02:40 -0700852 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
853 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700854 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700855 DbusInterfaceMap sensorMap;
856 if (!getSensorMap(ctx, connection, path, sensorMap))
857 {
858 return ipmi::responseResponseError();
859 }
860 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800861 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700862 {
863 return ipmi::responseResponseError();
864 }
865
Hao Jiangd2afd052020-12-10 15:09:32 -0800866 // VR sensors are treated as a special case and we will not check the
867 // write permission for VR sensors, since they always deemed writable
868 // and permission table are not applied to VR sensors.
Patrick Williams1318a5e2024-08-16 15:19:54 -0400869 auto vrMode =
870 sensor::calculateVRMode(assertOffset, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800871 if (!vrMode)
872 {
873 return ipmi::responseResponseError();
874 }
875 boost::system::error_code ec = setDbusProperty(
876 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
877 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500878 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800879 // callback functions for now (e.g. ipmiSetSensorReading).
880 if (ec)
881 {
George Liude6694e2024-07-17 15:22:25 +0800882 lg2::error("Failed to set Selected, path: {PATH}, "
883 "interface: {INTERFACE}, ERROR: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100884 "PATH", path, "INTERFACE", SensorValue::interface,
George Liude6694e2024-07-17 15:22:25 +0800885 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800886 }
887 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700888 }
889
George Liude6694e2024-07-17 15:22:25 +0800890 lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
Hao Jiangd2afd052020-12-10 15:09:32 -0800891 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700892}
893
Willy Tude54f482021-01-26 15:59:09 -0800894ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
895 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
896{
897 std::string connection;
898 std::string path;
899
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000900 if (sensnum == reservedSensorNumber)
901 {
902 return ipmi::responseInvalidFieldRequest();
903 }
904
Willy Tude54f482021-01-26 15:59:09 -0800905 auto status = getSensorConnection(ctx, sensnum, connection, path);
906 if (status)
907 {
908 return ipmi::response(status);
909 }
910
Scron Chang2703b022021-07-06 15:47:45 +0800911#ifdef FEATURE_HYBRID_SENSORS
912 if (auto sensor = findStaticSensor(path);
913 sensor != ipmi::sensor::sensors.end() &&
914 getSensorEventTypeFromPath(path) !=
915 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
916 {
917 if (ipmi::sensor::Mutability::Read !=
918 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
919 {
920 return ipmi::responseIllegalCommand();
921 }
922
Hariharan Rangasamyd6c397b2025-10-30 11:49:22 +0530923 uint8_t operation = 0;
Scron Chang2703b022021-07-06 15:47:45 +0800924 try
925 {
926 ipmi::sensor::GetSensorResponse getResponse =
927 sensor->second.getFunc(sensor->second);
928
929 if (getResponse.readingOrStateUnavailable)
930 {
931 operation |= static_cast<uint8_t>(
932 IPMISensorReadingByte2::readingStateUnavailable);
933 }
934 if (getResponse.scanningEnabled)
935 {
936 operation |= static_cast<uint8_t>(
937 IPMISensorReadingByte2::sensorScanningEnable);
938 }
939 if (getResponse.allEventMessagesEnabled)
940 {
941 operation |= static_cast<uint8_t>(
942 IPMISensorReadingByte2::eventMessagesEnable);
943 }
944 return ipmi::responseSuccess(
945 getResponse.reading, operation,
946 getResponse.thresholdLevelsStates,
947 getResponse.discreteReadingSensorStates);
948 }
949 catch (const std::exception& e)
950 {
951 operation |= static_cast<uint8_t>(
952 IPMISensorReadingByte2::readingStateUnavailable);
953 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
954 }
955 }
956#endif
957
Willy Tude54f482021-01-26 15:59:09 -0800958 DbusInterfaceMap sensorMap;
959 if (!getSensorMap(ctx, connection, path, sensorMap))
960 {
961 return ipmi::responseResponseError();
962 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100963 auto sensorObject = sensorMap.find(SensorValue::interface);
Willy Tude54f482021-01-26 15:59:09 -0800964
965 if (sensorObject == sensorMap.end() ||
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100966 sensorObject->second.find(SensorValue::property_names::value) ==
967 sensorObject->second.end())
Willy Tude54f482021-01-26 15:59:09 -0800968 {
969 return ipmi::responseResponseError();
970 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100971 auto& valueVariant =
972 sensorObject->second[SensorValue::property_names::value];
Willy Tude54f482021-01-26 15:59:09 -0800973 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
974
975 double max = 0;
976 double min = 0;
977 getSensorMaxMin(sensorMap, max, min);
978
979 int16_t mValue = 0;
980 int16_t bValue = 0;
981 int8_t rExp = 0;
982 int8_t bExp = 0;
983 bool bSigned = false;
984
985 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
986 {
987 return ipmi::responseResponseError();
988 }
989
Patrick Williams1318a5e2024-08-16 15:19:54 -0400990 uint8_t value =
991 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800992 uint8_t operation =
993 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
994 operation |=
995 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
996 bool notReading = std::isnan(reading);
997
998 if (!notReading)
999 {
1000 auto availableObject =
1001 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1002 if (availableObject != sensorMap.end())
1003 {
1004 auto findAvailable = availableObject->second.find("Available");
1005 if (findAvailable != availableObject->second.end())
1006 {
1007 bool* available = std::get_if<bool>(&(findAvailable->second));
1008 if (available && !(*available))
1009 {
1010 notReading = true;
1011 }
1012 }
1013 }
1014 }
1015
1016 if (notReading)
1017 {
1018 operation |= static_cast<uint8_t>(
1019 IPMISensorReadingByte2::readingStateUnavailable);
1020 }
1021
Josh Lehana55c9532020-10-28 21:59:06 -07001022 if constexpr (details::enableInstrumentation)
1023 {
1024 int byteValue;
1025 if (bSigned)
1026 {
1027 byteValue = static_cast<int>(static_cast<int8_t>(value));
1028 }
1029 else
1030 {
1031 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1032 }
1033
1034 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001035 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1036 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001037 {
1038 // This is the first reading, show the coefficients
1039 double step = (max - min) / 255.0;
Haicheng Zhang041b3752025-07-14 17:13:45 +08001040 lg2::error(
1041 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, "
1042 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} "
1043 "bExp={BEXP} bSigned={BSIGNED}",
1044 "NAME",
1045 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum),
1046 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP",
1047 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned);
Josh Lehana55c9532020-10-28 21:59:06 -07001048 }
1049 }
1050
Willy Tude54f482021-01-26 15:59:09 -08001051 uint8_t thresholds = 0;
1052
1053 auto warningObject =
1054 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1055 if (warningObject != sensorMap.end())
1056 {
1057 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1058 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1059 if (alarmHigh != warningObject->second.end())
1060 {
1061 if (std::get<bool>(alarmHigh->second))
1062 {
1063 thresholds |= static_cast<uint8_t>(
1064 IPMISensorReadingByte3::upperNonCritical);
1065 }
1066 }
1067 if (alarmLow != warningObject->second.end())
1068 {
1069 if (std::get<bool>(alarmLow->second))
1070 {
1071 thresholds |= static_cast<uint8_t>(
1072 IPMISensorReadingByte3::lowerNonCritical);
1073 }
1074 }
1075 }
1076
1077 auto criticalObject =
1078 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1079 if (criticalObject != sensorMap.end())
1080 {
1081 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1082 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1083 if (alarmHigh != criticalObject->second.end())
1084 {
1085 if (std::get<bool>(alarmHigh->second))
1086 {
1087 thresholds |=
1088 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1089 }
1090 }
1091 if (alarmLow != criticalObject->second.end())
1092 {
1093 if (std::get<bool>(alarmLow->second))
1094 {
1095 thresholds |=
1096 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1097 }
1098 }
1099 }
1100
1101 // no discrete as of today so optional byte is never returned
1102 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1103}
1104
1105/** @brief implements the Set Sensor threshold command
1106 * @param sensorNumber - sensor number
1107 * @param lowerNonCriticalThreshMask
1108 * @param lowerCriticalThreshMask
1109 * @param lowerNonRecovThreshMask
1110 * @param upperNonCriticalThreshMask
1111 * @param upperCriticalThreshMask
1112 * @param upperNonRecovThreshMask
1113 * @param reserved
1114 * @param lowerNonCritical - lower non-critical threshold
1115 * @param lowerCritical - Lower critical threshold
1116 * @param lowerNonRecoverable - Lower non recovarable threshold
1117 * @param upperNonCritical - Upper non-critical threshold
1118 * @param upperCritical - Upper critical
1119 * @param upperNonRecoverable - Upper Non-recoverable
1120 *
1121 * @returns IPMI completion code
1122 */
1123ipmi::RspType<> ipmiSenSetSensorThresholds(
1124 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1125 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1126 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1127 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001128 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001129 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001130 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001131{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001132 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001133 {
1134 return ipmi::responseInvalidFieldRequest();
1135 }
1136
1137 // lower nc and upper nc not suppported on any sensor
1138 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1139 {
1140 return ipmi::responseInvalidFieldRequest();
1141 }
1142
1143 // if none of the threshold mask are set, nothing to do
1144 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1145 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1146 upperCriticalThreshMask | upperNonRecovThreshMask))
1147 {
1148 return ipmi::responseSuccess();
1149 }
1150
1151 std::string connection;
1152 std::string path;
1153
1154 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1155 if (status)
1156 {
1157 return ipmi::response(status);
1158 }
1159 DbusInterfaceMap sensorMap;
1160 if (!getSensorMap(ctx, connection, path, sensorMap))
1161 {
1162 return ipmi::responseResponseError();
1163 }
1164
1165 double max = 0;
1166 double min = 0;
1167 getSensorMaxMin(sensorMap, max, min);
1168
1169 int16_t mValue = 0;
1170 int16_t bValue = 0;
1171 int8_t rExp = 0;
1172 int8_t bExp = 0;
1173 bool bSigned = false;
1174
1175 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1176 {
1177 return ipmi::responseResponseError();
1178 }
1179
1180 // store a vector of property name, value to set, and interface
1181 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1182
1183 // define the indexes of the tuple
1184 constexpr uint8_t propertyName = 0;
1185 constexpr uint8_t thresholdValue = 1;
1186 constexpr uint8_t interface = 2;
1187 // verifiy all needed fields are present
1188 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1189 {
1190 auto findThreshold =
1191 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1192 if (findThreshold == sensorMap.end())
1193 {
1194 return ipmi::responseInvalidFieldRequest();
1195 }
1196 if (lowerCriticalThreshMask)
1197 {
1198 auto findLower = findThreshold->second.find("CriticalLow");
1199 if (findLower == findThreshold->second.end())
1200 {
1201 return ipmi::responseInvalidFieldRequest();
1202 }
1203 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1204 findThreshold->first);
1205 }
1206 if (upperCriticalThreshMask)
1207 {
1208 auto findUpper = findThreshold->second.find("CriticalHigh");
1209 if (findUpper == findThreshold->second.end())
1210 {
1211 return ipmi::responseInvalidFieldRequest();
1212 }
1213 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1214 findThreshold->first);
1215 }
1216 }
1217 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1218 {
1219 auto findThreshold =
1220 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1221 if (findThreshold == sensorMap.end())
1222 {
1223 return ipmi::responseInvalidFieldRequest();
1224 }
1225 if (lowerNonCriticalThreshMask)
1226 {
1227 auto findLower = findThreshold->second.find("WarningLow");
1228 if (findLower == findThreshold->second.end())
1229 {
1230 return ipmi::responseInvalidFieldRequest();
1231 }
1232 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1233 findThreshold->first);
1234 }
1235 if (upperNonCriticalThreshMask)
1236 {
1237 auto findUpper = findThreshold->second.find("WarningHigh");
1238 if (findUpper == findThreshold->second.end())
1239 {
1240 return ipmi::responseInvalidFieldRequest();
1241 }
1242 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1243 findThreshold->first);
1244 }
1245 }
1246 for (const auto& property : thresholdsToSet)
1247 {
1248 // from section 36.3 in the IPMI Spec, assume all linear
1249 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1250 (bValue * std::pow(10.0, bExp))) *
1251 std::pow(10.0, rExp);
1252 setDbusProperty(
1253 *getSdBus(), connection, path, std::get<interface>(property),
1254 std::get<propertyName>(property), ipmi::Value(valueToSet));
1255 }
1256 return ipmi::responseSuccess();
1257}
1258
1259IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1260{
1261 IPMIThresholds resp;
1262 auto warningInterface =
1263 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1264 auto criticalInterface =
1265 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1266
1267 if ((warningInterface != sensorMap.end()) ||
1268 (criticalInterface != sensorMap.end()))
1269 {
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001270 auto sensorPair = sensorMap.find(SensorValue::interface);
Willy Tude54f482021-01-26 15:59:09 -08001271
1272 if (sensorPair == sensorMap.end())
1273 {
1274 // should not have been able to find a sensor not implementing
1275 // the sensor object
1276 throw std::runtime_error("Invalid sensor map");
1277 }
1278
1279 double max = 0;
1280 double min = 0;
1281 getSensorMaxMin(sensorMap, max, min);
1282
1283 int16_t mValue = 0;
1284 int16_t bValue = 0;
1285 int8_t rExp = 0;
1286 int8_t bExp = 0;
1287 bool bSigned = false;
1288
1289 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1290 {
1291 throw std::runtime_error("Invalid sensor atrributes");
1292 }
1293 if (warningInterface != sensorMap.end())
1294 {
1295 auto& warningMap = warningInterface->second;
1296
1297 auto warningHigh = warningMap.find("WarningHigh");
1298 auto warningLow = warningMap.find("WarningLow");
1299
1300 if (warningHigh != warningMap.end())
1301 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001302 double value =
1303 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001304 if (std::isfinite(value))
1305 {
1306 resp.warningHigh = scaleIPMIValueFromDouble(
1307 value, mValue, rExp, bValue, bExp, bSigned);
1308 }
Willy Tude54f482021-01-26 15:59:09 -08001309 }
1310 if (warningLow != warningMap.end())
1311 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001312 double value =
1313 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001314 if (std::isfinite(value))
1315 {
1316 resp.warningLow = scaleIPMIValueFromDouble(
1317 value, mValue, rExp, bValue, bExp, bSigned);
1318 }
Willy Tude54f482021-01-26 15:59:09 -08001319 }
1320 }
1321 if (criticalInterface != sensorMap.end())
1322 {
1323 auto& criticalMap = criticalInterface->second;
1324
1325 auto criticalHigh = criticalMap.find("CriticalHigh");
1326 auto criticalLow = criticalMap.find("CriticalLow");
1327
1328 if (criticalHigh != criticalMap.end())
1329 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001330 double value =
1331 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001332 if (std::isfinite(value))
1333 {
1334 resp.criticalHigh = scaleIPMIValueFromDouble(
1335 value, mValue, rExp, bValue, bExp, bSigned);
1336 }
Willy Tude54f482021-01-26 15:59:09 -08001337 }
1338 if (criticalLow != criticalMap.end())
1339 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001340 double value =
1341 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001342 if (std::isfinite(value))
1343 {
1344 resp.criticalLow = scaleIPMIValueFromDouble(
1345 value, mValue, rExp, bValue, bExp, bSigned);
1346 }
Willy Tude54f482021-01-26 15:59:09 -08001347 }
1348 }
1349 }
1350 return resp;
1351}
1352
1353ipmi::RspType<uint8_t, // readable
1354 uint8_t, // lowerNCrit
1355 uint8_t, // lowerCrit
1356 uint8_t, // lowerNrecoverable
1357 uint8_t, // upperNC
1358 uint8_t, // upperCrit
1359 uint8_t> // upperNRecoverable
1360 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1361{
1362 std::string connection;
1363 std::string path;
1364
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001365 if (sensorNumber == reservedSensorNumber)
1366 {
1367 return ipmi::responseInvalidFieldRequest();
1368 }
1369
Willy Tude54f482021-01-26 15:59:09 -08001370 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1371 if (status)
1372 {
1373 return ipmi::response(status);
1374 }
1375
1376 DbusInterfaceMap sensorMap;
1377 if (!getSensorMap(ctx, connection, path, sensorMap))
1378 {
1379 return ipmi::responseResponseError();
1380 }
1381
1382 IPMIThresholds thresholdData;
1383 try
1384 {
1385 thresholdData = getIPMIThresholds(sensorMap);
1386 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001387 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001388 {
1389 return ipmi::responseResponseError();
1390 }
1391
1392 uint8_t readable = 0;
1393 uint8_t lowerNC = 0;
1394 uint8_t lowerCritical = 0;
1395 uint8_t lowerNonRecoverable = 0;
1396 uint8_t upperNC = 0;
1397 uint8_t upperCritical = 0;
1398 uint8_t upperNonRecoverable = 0;
1399
1400 if (thresholdData.warningHigh)
1401 {
1402 readable |=
1403 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1404 upperNC = *thresholdData.warningHigh;
1405 }
1406 if (thresholdData.warningLow)
1407 {
1408 readable |=
1409 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1410 lowerNC = *thresholdData.warningLow;
1411 }
1412
1413 if (thresholdData.criticalHigh)
1414 {
1415 readable |=
1416 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1417 upperCritical = *thresholdData.criticalHigh;
1418 }
1419 if (thresholdData.criticalLow)
1420 {
1421 readable |=
1422 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1423 lowerCritical = *thresholdData.criticalLow;
1424 }
1425
1426 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1427 lowerNonRecoverable, upperNC, upperCritical,
1428 upperNonRecoverable);
1429}
1430
1431/** @brief implements the get Sensor event enable command
1432 * @param sensorNumber - sensor number
1433 *
1434 * @returns IPMI completion code plus response data
1435 * - enabled - Sensor Event messages
1436 * - assertionEnabledLsb - Assertion event messages
1437 * - assertionEnabledMsb - Assertion event messages
1438 * - deassertionEnabledLsb - Deassertion event messages
1439 * - deassertionEnabledMsb - Deassertion event messages
1440 */
1441
1442ipmi::RspType<uint8_t, // enabled
1443 uint8_t, // assertionEnabledLsb
1444 uint8_t, // assertionEnabledMsb
1445 uint8_t, // deassertionEnabledLsb
1446 uint8_t> // deassertionEnabledMsb
1447 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1448{
1449 std::string connection;
1450 std::string path;
1451
1452 uint8_t enabled = 0;
1453 uint8_t assertionEnabledLsb = 0;
1454 uint8_t assertionEnabledMsb = 0;
1455 uint8_t deassertionEnabledLsb = 0;
1456 uint8_t deassertionEnabledMsb = 0;
1457
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001458 if (sensorNum == reservedSensorNumber)
1459 {
1460 return ipmi::responseInvalidFieldRequest();
1461 }
1462
Willy Tude54f482021-01-26 15:59:09 -08001463 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1464 if (status)
1465 {
1466 return ipmi::response(status);
1467 }
1468
Scron Chang2703b022021-07-06 15:47:45 +08001469#ifdef FEATURE_HYBRID_SENSORS
1470 if (auto sensor = findStaticSensor(path);
1471 sensor != ipmi::sensor::sensors.end() &&
1472 getSensorEventTypeFromPath(path) !=
1473 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1474 {
1475 enabled = static_cast<uint8_t>(
1476 IPMISensorEventEnableByte2::sensorScanningEnable);
1477 uint16_t assertionEnabled = 0;
1478 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1479 ->second.begin()
1480 ->second.second)
1481 {
1482 assertionEnabled |= (1 << offsetValMap.first);
1483 }
1484 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1485 assertionEnabledMsb =
1486 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1487
1488 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1489 assertionEnabledMsb, deassertionEnabledLsb,
1490 deassertionEnabledMsb);
1491 }
1492#endif
1493
Willy Tude54f482021-01-26 15:59:09 -08001494 DbusInterfaceMap sensorMap;
1495 if (!getSensorMap(ctx, connection, path, sensorMap))
1496 {
1497 return ipmi::responseResponseError();
1498 }
1499
1500 auto warningInterface =
1501 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1502 auto criticalInterface =
1503 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1504 if ((warningInterface != sensorMap.end()) ||
1505 (criticalInterface != sensorMap.end()))
1506 {
1507 enabled = static_cast<uint8_t>(
1508 IPMISensorEventEnableByte2::sensorScanningEnable);
1509 if (warningInterface != sensorMap.end())
1510 {
1511 auto& warningMap = warningInterface->second;
1512
1513 auto warningHigh = warningMap.find("WarningHigh");
1514 auto warningLow = warningMap.find("WarningLow");
1515 if (warningHigh != warningMap.end())
1516 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001517 double value =
1518 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001519 if (std::isfinite(value))
1520 {
1521 assertionEnabledLsb |= static_cast<uint8_t>(
1522 IPMISensorEventEnableThresholds::
1523 upperNonCriticalGoingHigh);
1524 deassertionEnabledLsb |= static_cast<uint8_t>(
1525 IPMISensorEventEnableThresholds::
1526 upperNonCriticalGoingLow);
1527 }
Willy Tude54f482021-01-26 15:59:09 -08001528 }
1529 if (warningLow != warningMap.end())
1530 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001531 double value =
1532 std::visit(VariantToDoubleVisitor(), warningLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001533 if (std::isfinite(value))
1534 {
1535 assertionEnabledLsb |= static_cast<uint8_t>(
1536 IPMISensorEventEnableThresholds::
1537 lowerNonCriticalGoingLow);
1538 deassertionEnabledLsb |= static_cast<uint8_t>(
1539 IPMISensorEventEnableThresholds::
1540 lowerNonCriticalGoingHigh);
1541 }
Willy Tude54f482021-01-26 15:59:09 -08001542 }
1543 }
1544 if (criticalInterface != sensorMap.end())
1545 {
1546 auto& criticalMap = criticalInterface->second;
1547
1548 auto criticalHigh = criticalMap.find("CriticalHigh");
1549 auto criticalLow = criticalMap.find("CriticalLow");
1550
1551 if (criticalHigh != criticalMap.end())
1552 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001553 double value =
1554 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001555 if (std::isfinite(value))
1556 {
1557 assertionEnabledMsb |= static_cast<uint8_t>(
1558 IPMISensorEventEnableThresholds::
1559 upperCriticalGoingHigh);
1560 deassertionEnabledMsb |= static_cast<uint8_t>(
1561 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1562 }
Willy Tude54f482021-01-26 15:59:09 -08001563 }
1564 if (criticalLow != criticalMap.end())
1565 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001566 double value =
1567 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001568 if (std::isfinite(value))
1569 {
1570 assertionEnabledLsb |= static_cast<uint8_t>(
1571 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1572 deassertionEnabledLsb |= static_cast<uint8_t>(
1573 IPMISensorEventEnableThresholds::
1574 lowerCriticalGoingHigh);
1575 }
Willy Tude54f482021-01-26 15:59:09 -08001576 }
1577 }
1578 }
1579
1580 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1581 assertionEnabledMsb, deassertionEnabledLsb,
1582 deassertionEnabledMsb);
1583}
1584
1585/** @brief implements the get Sensor event status command
1586 * @param sensorNumber - sensor number, FFh = reserved
1587 *
1588 * @returns IPMI completion code plus response data
1589 * - sensorEventStatus - Sensor Event messages state
1590 * - assertions - Assertion event messages
1591 * - deassertions - Deassertion event messages
1592 */
1593ipmi::RspType<uint8_t, // sensorEventStatus
1594 std::bitset<16>, // assertions
1595 std::bitset<16> // deassertion
1596 >
1597 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1598{
1599 if (sensorNum == reservedSensorNumber)
1600 {
1601 return ipmi::responseInvalidFieldRequest();
1602 }
1603
1604 std::string connection;
1605 std::string path;
1606 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1607 if (status)
1608 {
George Liude6694e2024-07-17 15:22:25 +08001609 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1610 "sensor number: {SENSOR_NUM}",
1611 "SENSOR_NUM", sensorNum);
Willy Tude54f482021-01-26 15:59:09 -08001612 return ipmi::response(status);
1613 }
1614
Scron Chang2703b022021-07-06 15:47:45 +08001615#ifdef FEATURE_HYBRID_SENSORS
1616 if (auto sensor = findStaticSensor(path);
1617 sensor != ipmi::sensor::sensors.end() &&
1618 getSensorEventTypeFromPath(path) !=
1619 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1620 {
1621 auto response = ipmi::sensor::get::mapDbusToAssertion(
1622 sensor->second, path, sensor->second.sensorInterface);
1623 std::bitset<16> assertions;
1624 // deassertions are not used.
1625 std::bitset<16> deassertions = 0;
Hariharan Rangasamyd6c397b2025-10-30 11:49:22 +05301626 uint8_t sensorEventStatus = 0;
Scron Chang2703b022021-07-06 15:47:45 +08001627 if (response.readingOrStateUnavailable)
1628 {
1629 sensorEventStatus |= static_cast<uint8_t>(
1630 IPMISensorReadingByte2::readingStateUnavailable);
1631 }
1632 if (response.scanningEnabled)
1633 {
1634 sensorEventStatus |= static_cast<uint8_t>(
1635 IPMISensorReadingByte2::sensorScanningEnable);
1636 }
1637 if (response.allEventMessagesEnabled)
1638 {
1639 sensorEventStatus |= static_cast<uint8_t>(
1640 IPMISensorReadingByte2::eventMessagesEnable);
1641 }
1642 assertions |= response.discreteReadingSensorStates << 8;
1643 assertions |= response.thresholdLevelsStates;
1644 return ipmi::responseSuccess(sensorEventStatus, assertions,
1645 deassertions);
1646 }
1647#endif
1648
Willy Tude54f482021-01-26 15:59:09 -08001649 DbusInterfaceMap sensorMap;
1650 if (!getSensorMap(ctx, connection, path, sensorMap))
1651 {
George Liude6694e2024-07-17 15:22:25 +08001652 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1653 "sensor path: {SENSOR_PATH}",
1654 "SENSOR_PATH", path);
Willy Tude54f482021-01-26 15:59:09 -08001655 return ipmi::responseResponseError();
1656 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001657
1658 uint8_t sensorEventStatus =
1659 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1660 std::bitset<16> assertions = 0;
1661 std::bitset<16> deassertions = 0;
1662
1663 // handle VR typed sensor
1664 auto vrInterface = sensorMap.find(sensor::vrInterface);
1665 if (vrInterface != sensorMap.end())
1666 {
1667 if (!sensor::getVrEventStatus(ctx, connection, path,
1668 vrInterface->second, assertions))
1669 {
1670 return ipmi::responseResponseError();
1671 }
1672
1673 // both Event Message and Sensor Scanning are disable for VR.
1674 sensorEventStatus = 0;
1675 return ipmi::responseSuccess(sensorEventStatus, assertions,
1676 deassertions);
1677 }
1678
Willy Tude54f482021-01-26 15:59:09 -08001679 auto warningInterface =
1680 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1681 auto criticalInterface =
1682 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1683
Willy Tude54f482021-01-26 15:59:09 -08001684 std::optional<bool> criticalDeassertHigh =
1685 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1686 std::optional<bool> criticalDeassertLow =
1687 thresholdDeassertMap[path]["CriticalAlarmLow"];
1688 std::optional<bool> warningDeassertHigh =
1689 thresholdDeassertMap[path]["WarningAlarmHigh"];
1690 std::optional<bool> warningDeassertLow =
1691 thresholdDeassertMap[path]["WarningAlarmLow"];
1692
Willy Tude54f482021-01-26 15:59:09 -08001693 if (criticalDeassertHigh && !*criticalDeassertHigh)
1694 {
1695 deassertions.set(static_cast<size_t>(
1696 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1697 }
1698 if (criticalDeassertLow && !*criticalDeassertLow)
1699 {
1700 deassertions.set(static_cast<size_t>(
1701 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1702 }
1703 if (warningDeassertHigh && !*warningDeassertHigh)
1704 {
1705 deassertions.set(static_cast<size_t>(
1706 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1707 }
1708 if (warningDeassertLow && !*warningDeassertLow)
1709 {
1710 deassertions.set(static_cast<size_t>(
1711 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1712 }
1713 if ((warningInterface != sensorMap.end()) ||
1714 (criticalInterface != sensorMap.end()))
1715 {
1716 sensorEventStatus = static_cast<size_t>(
1717 IPMISensorEventEnableByte2::eventMessagesEnable);
1718 if (warningInterface != sensorMap.end())
1719 {
1720 auto& warningMap = warningInterface->second;
1721
1722 auto warningHigh = warningMap.find("WarningAlarmHigh");
1723 auto warningLow = warningMap.find("WarningAlarmLow");
1724 auto warningHighAlarm = false;
1725 auto warningLowAlarm = false;
1726
1727 if (warningHigh != warningMap.end())
1728 {
1729 warningHighAlarm = std::get<bool>(warningHigh->second);
1730 }
1731 if (warningLow != warningMap.end())
1732 {
1733 warningLowAlarm = std::get<bool>(warningLow->second);
1734 }
1735 if (warningHighAlarm)
1736 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001737 assertions.set(static_cast<size_t>(
1738 IPMIGetSensorEventEnableThresholds::
1739 upperNonCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001740 }
1741 if (warningLowAlarm)
1742 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001743 assertions.set(static_cast<size_t>(
1744 IPMIGetSensorEventEnableThresholds::
1745 lowerNonCriticalGoingLow));
Willy Tude54f482021-01-26 15:59:09 -08001746 }
1747 }
1748 if (criticalInterface != sensorMap.end())
1749 {
1750 auto& criticalMap = criticalInterface->second;
1751
1752 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1753 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1754 auto criticalHighAlarm = false;
1755 auto criticalLowAlarm = false;
1756
1757 if (criticalHigh != criticalMap.end())
1758 {
1759 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1760 }
1761 if (criticalLow != criticalMap.end())
1762 {
1763 criticalLowAlarm = std::get<bool>(criticalLow->second);
1764 }
1765 if (criticalHighAlarm)
1766 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001767 assertions.set(static_cast<size_t>(
1768 IPMIGetSensorEventEnableThresholds::
1769 upperCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001770 }
1771 if (criticalLowAlarm)
1772 {
1773 assertions.set(static_cast<size_t>(
1774 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1775 }
1776 }
1777 }
1778
1779 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1780}
1781
Willy Tu38e7a2b2021-03-29 15:09:56 -07001782// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001783void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1784 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001785{
Willy Tu38e7a2b2021-03-29 15:09:56 -07001786 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1787 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1788
George Liuc7a4da52025-08-19 16:20:31 +08001789 record.header.recordId = recordID;
George Liu42247d22025-08-19 10:24:47 +08001790 record.header.sdrVersion = ipmiSdrVersion;
1791 record.header.recordType = get_sdr::SENSOR_DATA_FULL_RECORD;
1792 record.header.recordLength = sizeof(get_sdr::SensorDataFullRecord) -
1793 sizeof(get_sdr::SensorDataRecordHeader);
1794 record.key.ownerId = bmcI2CAddr;
1795 record.key.ownerLun = lun;
1796 record.key.sensorNumber = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001797}
Willy Tu4eca2512022-06-20 21:14:51 -07001798bool constructSensorSdr(
1799 ipmi::Context::ptr ctx,
1800 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1801 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1802 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001803{
Hao Jiange39d4d82021-04-16 17:02:40 -07001804 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1805
1806 DbusInterfaceMap sensorMap;
1807 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1808 {
George Liude6694e2024-07-17 15:22:25 +08001809 lg2::error("Failed to update sensor map for threshold sensor, "
1810 "service: {SERVICE}, path: {PATH}",
1811 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07001812 return false;
1813 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001814
George Liu42247d22025-08-19 10:24:47 +08001815 record.body.sensorCapabilities = 0x68; // auto rearm - todo hysteresis
1816 record.body.sensorType = getSensorTypeFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001817 std::string type = getSensorTypeStringFromPath(path);
1818 auto typeCstr = type.c_str();
1819 auto findUnits = sensorUnits.find(typeCstr);
1820 if (findUnits != sensorUnits.end())
1821 {
George Liu42247d22025-08-19 10:24:47 +08001822 record.body.sensorUnits2Base = static_cast<uint8_t>(findUnits->second);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001823 } // else default 0x0 unspecified
1824
George Liu42247d22025-08-19 10:24:47 +08001825 record.body.eventReadingType = getSensorEventTypeFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001826
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001827 auto sensorObject = sensorMap.find(SensorValue::interface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001828 if (sensorObject == sensorMap.end())
1829 {
George Liude6694e2024-07-17 15:22:25 +08001830 lg2::error("constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001831 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001832 }
1833
1834 uint8_t entityId = 0;
1835 uint8_t entityInstance = 0x01;
1836
1837 // follow the association chain to get the parent board's entityid and
1838 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001839 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1840 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001841
George Liu42247d22025-08-19 10:24:47 +08001842 record.body.entityId = entityId;
1843 record.body.entityInstance = entityInstance;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001844
Shakeeb Pasha93889722021-10-14 10:20:13 +05301845 double max = 0;
1846 double min = 0;
1847 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001848
1849 int16_t mValue = 0;
1850 int8_t rExp = 0;
1851 int16_t bValue = 0;
1852 int8_t bExp = 0;
1853 bool bSigned = false;
1854
1855 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1856 {
George Liude6694e2024-07-17 15:22:25 +08001857 lg2::error("constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001858 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001859 }
1860
1861 // The record.body is a struct SensorDataFullRecordBody
1862 // from sensorhandler.hpp in phosphor-ipmi-host.
1863 // The meaning of these bits appears to come from
1864 // table 43.1 of the IPMI spec.
1865 // The above 5 sensor attributes are stuffed in as follows:
1866 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1867 // Byte 22-24 are for other purposes
1868 // Byte 25 = MMMMMMMM = LSB of M
1869 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1870 // Byte 27 = BBBBBBBB = LSB of B
1871 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1872 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1873 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1874
1875 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
George Liu42247d22025-08-19 10:24:47 +08001876 record.body.mLsb = mValue & 0xFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001877
1878 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1879 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1880
1881 // move the smallest bit of the MSB into place (bit 9)
George Liu42247d22025-08-19 10:24:47 +08001882 // the MSbs are bits 7:8 in mMsbAndToLerance
1883 record.body.mMsbAndTolerance = (mBitSign << 7) | (mBitNine << 6);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001884
George Liu42247d22025-08-19 10:24:47 +08001885 record.body.bLsb = bValue & 0xFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001886
1887 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1888 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1889
1890 // move the smallest bit of the MSB into place (bit 9)
George Liu42247d22025-08-19 10:24:47 +08001891 // the MSbs are bits 7:8 in bMsbAndAccuracyLsb
1892 record.body.bMsbAndAccuracyLsb = (bBitSign << 7) | (bBitNine << 6);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001893
1894 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1895 uint8_t rExpBits = rExp & 0x07;
1896
1897 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1898 uint8_t bExpBits = bExp & 0x07;
1899
1900 // move rExp and bExp into place
George Liu42247d22025-08-19 10:24:47 +08001901 record.body.rbExponents =
Patrick Williams1318a5e2024-08-16 15:19:54 -04001902 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001903
1904 // Set the analog reading byte interpretation accordingly
George Liu42247d22025-08-19 10:24:47 +08001905 record.body.sensorUnits1 = (bSigned ? 1 : 0) << 7;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001906
1907 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1908 // These seem redundant, but derivable from the above 5 attributes
1909 // Original comment said "todo fill out rest of units"
1910
1911 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001912 auto name = sensor::parseSdrIdFromPath(path);
George Liuf60be692025-08-19 11:28:42 +08001913 get_sdr::body::setIdStrLen(name.size(), record.body);
1914 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08001915 std::memcpy(record.body.idString, name.c_str(),
1916 std::min(name.length() + 1, sizeof(record.body.idString)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001917
Josh Lehana55c9532020-10-28 21:59:06 -07001918 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001919 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001920
Jie Yangf0a89942021-07-29 15:30:25 -07001921 bool sensorSettable = false;
1922 auto mutability =
1923 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1924 if (mutability != sensorMap.end())
1925 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001926 sensorSettable =
1927 mappedVariant<bool>(mutability->second, "Mutable", false);
Jie Yangf0a89942021-07-29 15:30:25 -07001928 }
George Liuf60be692025-08-19 11:28:42 +08001929 get_sdr::body::initSettableState(sensorSettable, record.body);
Jie Yangf0a89942021-07-29 15:30:25 -07001930
1931 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001932 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001933
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001934 IPMIThresholds thresholdData;
1935 try
1936 {
1937 thresholdData = getIPMIThresholds(sensorMap);
1938 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001939 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001940 {
George Liude6694e2024-07-17 15:22:25 +08001941 lg2::error("constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001942 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001943 }
1944
1945 if (thresholdData.criticalHigh)
1946 {
George Liu42247d22025-08-19 10:24:47 +08001947 record.body.upperCriticalThreshold = *thresholdData.criticalHigh;
1948 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001949 IPMISensorEventEnableThresholds::criticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001950 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001951 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001952 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001953 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001954 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001955 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1956 }
1957 if (thresholdData.warningHigh)
1958 {
George Liu42247d22025-08-19 10:24:47 +08001959 record.body.upperNoncriticalThreshold = *thresholdData.warningHigh;
1960 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001961 IPMISensorEventEnableThresholds::nonCriticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001962 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001963 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001964 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001965 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001966 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001967 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1968 }
1969 if (thresholdData.criticalLow)
1970 {
George Liu42247d22025-08-19 10:24:47 +08001971 record.body.lowerCriticalThreshold = *thresholdData.criticalLow;
1972 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001973 IPMISensorEventEnableThresholds::criticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001974 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001975 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08001976 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001977 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08001978 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001979 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1980 }
1981 if (thresholdData.warningLow)
1982 {
George Liu42247d22025-08-19 10:24:47 +08001983 record.body.lowerNoncriticalThreshold = *thresholdData.warningLow;
1984 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001985 IPMISensorEventEnableThresholds::nonCriticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001986 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001987 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08001988 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001989 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08001990 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001991 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1992 }
1993
1994 // everything that is readable is setable
George Liu42247d22025-08-19 10:24:47 +08001995 record.body.discreteReadingSettingMask[1] =
1996 record.body.discreteReadingSettingMask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001997 return true;
1998}
1999
Scron Chang2703b022021-07-06 15:47:45 +08002000#ifdef FEATURE_HYBRID_SENSORS
2001// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08002002void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08002003 uint16_t recordID,
2004 ipmi::sensor::IdInfoMap::const_iterator sensor,
2005 get_sdr::SensorDataFullRecord& record)
2006{
2007 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2008
George Liu42247d22025-08-19 10:24:47 +08002009 record.body.entityId = sensor->second.entityType;
2010 record.body.sensorType = sensor->second.sensorType;
2011 record.body.eventReadingType = sensor->second.sensorReadingType;
2012 record.body.entityInstance = sensor->second.instance;
Scron Chang2703b022021-07-06 15:47:45 +08002013 if (ipmi::sensor::Mutability::Write ==
2014 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2015 {
George Liuf60be692025-08-19 11:28:42 +08002016 get_sdr::body::initSettableState(true, record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002017 }
2018
George Liu42247d22025-08-19 10:24:47 +08002019 auto idString = sensor->second.sensorName;
Scron Chang2703b022021-07-06 15:47:45 +08002020
George Liu42247d22025-08-19 10:24:47 +08002021 if (idString.empty())
Scron Chang2703b022021-07-06 15:47:45 +08002022 {
George Liu42247d22025-08-19 10:24:47 +08002023 idString = sensor->second.sensorNameFunc(sensor->second);
Scron Chang2703b022021-07-06 15:47:45 +08002024 }
2025
George Liu42247d22025-08-19 10:24:47 +08002026 if (idString.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
Scron Chang2703b022021-07-06 15:47:45 +08002027 {
George Liuf60be692025-08-19 11:28:42 +08002028 get_sdr::body::setIdStrLen(FULL_RECORD_ID_STR_MAX_LENGTH, record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002029 }
2030 else
2031 {
George Liuf60be692025-08-19 11:28:42 +08002032 get_sdr::body::setIdStrLen(idString.length(), record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002033 }
George Liuf60be692025-08-19 11:28:42 +08002034 get_sdr::body::setIdType(3, &record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08002035 std::strncpy(record.body.idString, idString.c_str(),
George Liuf60be692025-08-19 11:28:42 +08002036 get_sdr::body::setIdStrLen(record.body));
Scron Chang2703b022021-07-06 15:47:45 +08002037}
2038#endif
2039
Hao Jiange39d4d82021-04-16 17:02:40 -07002040// Construct type 3 SDR header and key (for VR and other discrete sensors)
2041void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2042 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002043{
2044 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2045 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2046
George Liuc7a4da52025-08-19 16:20:31 +08002047 record.header.recordId = recordID;
George Liu42247d22025-08-19 10:24:47 +08002048 record.header.sdrVersion = ipmiSdrVersion;
2049 record.header.recordType = get_sdr::SENSOR_DATA_EVENT_RECORD;
2050 record.header.recordLength = sizeof(get_sdr::SensorDataEventRecord) -
2051 sizeof(get_sdr::SensorDataRecordHeader);
2052 record.key.ownerId = bmcI2CAddr;
2053 record.key.ownerLun = lun;
2054 record.key.sensorNumber = sensornumber;
Willy Tu61992ad2021-03-29 15:33:20 -07002055
George Liu42247d22025-08-19 10:24:47 +08002056 record.body.entityId = 0x00;
2057 record.body.entityInstance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002058}
Willy Tu61992ad2021-03-29 15:33:20 -07002059
Hao Jiange39d4d82021-04-16 17:02:40 -07002060// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002061bool constructVrSdr(ipmi::Context::ptr ctx,
2062 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2063 uint16_t sensorNum, uint16_t recordID,
2064 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002065 get_sdr::SensorDataEventRecord& record)
2066{
Hao Jiange39d4d82021-04-16 17:02:40 -07002067 constructEventSdrHeaderKey(sensorNum, recordID, record);
2068
2069 DbusInterfaceMap sensorMap;
2070 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2071 {
George Liude6694e2024-07-17 15:22:25 +08002072 lg2::error("Failed to update sensor map for VR sensor, "
2073 "service: {SERVICE}, path: {PATH}",
2074 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07002075 return false;
2076 }
Willy Tu61992ad2021-03-29 15:33:20 -07002077 // follow the association chain to get the parent board's entityid and
2078 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002079 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
George Liu42247d22025-08-19 10:24:47 +08002080 record.body.entityId, record.body.entityInstance);
Willy Tu61992ad2021-03-29 15:33:20 -07002081
2082 // Sensor type is hardcoded as a module/board type instead of parsing from
2083 // sensor path. This is because VR control is allocated in an independent
2084 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2085 // types.
George Liuf2807ed2025-04-03 15:52:57 +08002086 static constexpr const uint8_t moduleBoardType = 0x15;
George Liu42247d22025-08-19 10:24:47 +08002087 record.body.sensorType = moduleBoardType;
2088 record.body.eventReadingType = 0x00;
Willy Tu61992ad2021-03-29 15:33:20 -07002089
George Liu42247d22025-08-19 10:24:47 +08002090 record.body.sensorRecordSharing1 = 0x00;
2091 record.body.sensorRecordSharing2 = 0x00;
Willy Tu61992ad2021-03-29 15:33:20 -07002092
2093 // populate sensor name from path
2094 auto name = sensor::parseSdrIdFromPath(path);
George Liu42247d22025-08-19 10:24:47 +08002095 int nameSize = std::min(name.size(), sizeof(record.body.idString));
George Liuf60be692025-08-19 11:28:42 +08002096 get_sdr::body::setIdStrLen(nameSize, record.body);
2097 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08002098 std::memset(record.body.idString, 0x00, sizeof(record.body.idString));
2099 std::memcpy(record.body.idString, name.c_str(), nameSize);
Willy Tu61992ad2021-03-29 15:33:20 -07002100
2101 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002102 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002103
2104 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002105}
2106
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002107uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002108{
2109 return std::min(getSensorTree().size(), maxIPMISensors);
2110}
2111
Willy Tu4eca2512022-06-20 21:14:51 -07002112static int getSensorDataRecord(
2113 ipmi::Context::ptr ctx,
2114 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2115 std::vector<uint8_t>& recordData, uint16_t recordID,
2116 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002117{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302118 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002119 size_t lastRecord = ipmi::getNumberOfSensors() +
2120 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2121 uint16_t nextRecord(recordID + 1);
2122
Willy Tu38e7a2b2021-03-29 15:09:56 -07002123 if (recordID == lastRecordIndex)
2124 {
2125 recordID = lastRecord;
2126 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002127 if (recordID == lastRecord)
2128 {
2129 nextRecord = lastRecordIndex;
2130 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002131 if (recordID > lastRecord)
2132 {
George Liude6694e2024-07-17 15:22:25 +08002133 lg2::error("getSensorDataRecord: recordID > lastRecord error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002134 return GENERAL_ERROR;
2135 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002136 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002137 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002138 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2139 recordData);
2140 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002141 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002142 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002143 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002144 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002145 }
2146
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002147 // Perform a incremental scan of the SDR Record ID's and translate the
2148 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2149 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2150 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2151 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002152 std::string connection;
2153 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002154 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002155 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002156 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002157 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002158 // LUN 0 has one reserved sensor number. Compensate here by adding one
2159 // to the record ID
2160 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002161 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002162 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002163 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002164 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002165 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2166 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2167 // rules governing its use.
2168 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002169 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002170 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002171
Patrick Williams1318a5e2024-08-16 15:19:54 -04002172 auto status =
2173 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2174 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002175 if (status)
2176 {
George Liude6694e2024-07-17 15:22:25 +08002177 lg2::error("getSensorDataRecord: getSensorConnection error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002178 return GENERAL_ERROR;
2179 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002180 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002181 // Return an error on LUN 2 assingments, and any sensor number beyond the
2182 // range of LUN 3
2183 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2184 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002185 {
George Liude6694e2024-07-17 15:22:25 +08002186 lg2::error("getSensorDataRecord: invalidSensorNumber");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002187 return GENERAL_ERROR;
2188 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002189 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2190 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2191
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002192 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2193 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002194 {
George Liude6694e2024-07-17 15:22:25 +08002195 lg2::error("getSensorDataRecord: sensor record mismatch");
Johnathan Manteyce982772021-07-28 15:08:30 -07002196 return GENERAL_ERROR;
2197 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002198
Willy Tu38e7a2b2021-03-29 15:09:56 -07002199 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002200 if (std::find(interfaces.begin(), interfaces.end(),
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002201 SensorValue::interface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002202 {
Willy Tu11d68892022-01-20 10:37:34 -08002203 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002204
Hao Jiange39d4d82021-04-16 17:02:40 -07002205 // If the request doesn't read SDR body, construct only header and key
2206 // part to avoid additional DBus transaction.
2207 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2208 {
2209 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2210 }
Willy Tu4eca2512022-06-20 21:14:51 -07002211 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2212 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002213 {
2214 return GENERAL_ERROR;
2215 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002216
selvaganapathi7b2e5502023-02-14 07:10:47 +05302217 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2218 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002219
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002220 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002221 }
Willy Tu61992ad2021-03-29 15:33:20 -07002222
Scron Chang2703b022021-07-06 15:47:45 +08002223#ifdef FEATURE_HYBRID_SENSORS
2224 if (auto sensor = findStaticSensor(path);
2225 sensor != ipmi::sensor::sensors.end() &&
2226 getSensorEventTypeFromPath(path) !=
2227 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2228 {
Willy Tu11d68892022-01-20 10:37:34 -08002229 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002230
2231 // If the request doesn't read SDR body, construct only header and key
2232 // part to avoid additional DBus transaction.
2233 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2234 {
2235 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2236 }
2237 else
2238 {
2239 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2240 }
2241
selvaganapathi7b2e5502023-02-14 07:10:47 +05302242 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2243 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002244
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002245 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002246 }
2247#endif
2248
Willy Tu61992ad2021-03-29 15:33:20 -07002249 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002250 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2251 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002252 {
Willy Tu11d68892022-01-20 10:37:34 -08002253 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002254
Hao Jiange39d4d82021-04-16 17:02:40 -07002255 // If the request doesn't read SDR body, construct only header and key
2256 // part to avoid additional DBus transaction.
2257 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2258 {
2259 constructEventSdrHeaderKey(sensorNum, recordID, record);
2260 }
Willy Tu4eca2512022-06-20 21:14:51 -07002261 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2262 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002263 {
2264 return GENERAL_ERROR;
2265 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302266 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2267 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002268 }
2269
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002270 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002271}
2272
2273/** @brief implements the get SDR Info command
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002274 * @param operation : 0 or not supplied returns sensor count
2275 * 1 return SDR count
Willy Tude54f482021-01-26 15:59:09 -08002276 *
2277 * @returns IPMI completion code plus response data
2278 * - sdrCount - sensor/SDR count
2279 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2280 */
2281static ipmi::RspType<uint8_t, // respcount
2282 uint8_t, // dynamic population flags
2283 uint32_t // last time a sensor was added
2284 >
2285 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002286 std::optional<uint8_t> operation)
Willy Tude54f482021-01-26 15:59:09 -08002287{
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002288 auto& sensorTree{getSensorTree()};
2289 uint8_t sdrCount{};
2290 // Sensors are dynamically allocated
2291 uint8_t lunsAndDynamicPopulation{0x80};
2292 constexpr uint8_t getSdrCount{1};
2293 constexpr uint8_t getSensorCount{0};
Willy Tude54f482021-01-26 15:59:09 -08002294
2295 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2296 {
2297 return ipmi::responseResponseError();
2298 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002299 uint16_t numSensors{ipmi::getNumberOfSensors()};
2300 if (operation.value_or(0) == getSdrCount)
Willy Tude54f482021-01-26 15:59:09 -08002301 {
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002302 sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
Willy Tude54f482021-01-26 15:59:09 -08002303 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002304 else if (operation.value_or(0) == getSensorCount)
Willy Tude54f482021-01-26 15:59:09 -08002305 {
2306 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002307 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002308 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002309 sdrCount =
2310 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002311 }
Harvey Wu75893062023-03-22 17:17:31 +08002312 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002313 {
2314 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2315 ? maxSensorsPerLUN
2316 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2317 }
Harvey Wu75893062023-03-22 17:17:31 +08002318 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002319 {
2320 if (numSensors <= maxIPMISensors)
2321 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002322 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2323 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002324 }
2325 else
2326 {
Willy Tude54f482021-01-26 15:59:09 -08002327 throw std::out_of_range(
2328 "Maximum number of IPMI sensors exceeded.");
2329 }
2330 }
2331 }
2332 else
2333 {
2334 return ipmi::responseInvalidFieldRequest();
2335 }
2336
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002337 // Flag which LUNs have sensors associated
Willy Tude54f482021-01-26 15:59:09 -08002338 if (numSensors > 0)
2339 {
2340 lunsAndDynamicPopulation |= 1;
2341 }
2342 if (numSensors > maxSensorsPerLUN)
2343 {
2344 lunsAndDynamicPopulation |= 2;
2345 }
2346 if (numSensors >= (maxSensorsPerLUN * 2))
2347 {
2348 lunsAndDynamicPopulation |= 8;
2349 }
2350 if (numSensors > maxIPMISensors)
2351 {
Willy Tude54f482021-01-26 15:59:09 -08002352 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2353 }
2354
2355 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2356 sdrLastAdd);
2357}
2358
2359/* end sensor commands */
2360
2361/* storage commands */
2362
2363ipmi::RspType<uint8_t, // sdr version
2364 uint16_t, // record count
2365 uint16_t, // free space
2366 uint32_t, // most recent addition
2367 uint32_t, // most recent erase
2368 uint8_t // operationSupport
2369 >
2370 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2371{
Willy Tude54f482021-01-26 15:59:09 -08002372 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Patrick Williams1318a5e2024-08-16 15:19:54 -04002373 uint16_t recordCount =
2374 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002375
2376 uint8_t operationSupport = static_cast<uint8_t>(
2377 SdrRepositoryInfoOps::overflow); // write not supported
2378
2379 operationSupport |=
2380 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2381 operationSupport |= static_cast<uint8_t>(
2382 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2383 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2384 unspecifiedFreeSpace, sdrLastAdd,
2385 sdrLastRemove, operationSupport);
2386}
2387
2388/** @brief implements the get SDR allocation info command
2389 *
2390 * @returns IPMI completion code plus response data
2391 * - allocUnits - Number of possible allocation units
2392 * - allocUnitSize - Allocation unit size in bytes.
2393 * - allocUnitFree - Number of free allocation units
2394 * - allocUnitLargestFree - Largest free block in allocation units
2395 * - maxRecordSize - Maximum record size in allocation units.
2396 */
2397ipmi::RspType<uint16_t, // allocUnits
2398 uint16_t, // allocUnitSize
2399 uint16_t, // allocUnitFree
2400 uint16_t, // allocUnitLargestFree
2401 uint8_t // maxRecordSize
2402 >
2403 ipmiStorageGetSDRAllocationInfo()
2404{
2405 // 0000h unspecified number of alloc units
2406 constexpr uint16_t allocUnits = 0;
2407
2408 constexpr uint16_t allocUnitFree = 0;
2409 constexpr uint16_t allocUnitLargestFree = 0;
2410 // only allow one block at a time
2411 constexpr uint8_t maxRecordSize = 1;
2412
2413 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2414 allocUnitLargestFree, maxRecordSize);
2415}
2416
2417/** @brief implements the reserve SDR command
2418 * @returns IPMI completion code plus response data
2419 * - sdrReservationID
2420 */
2421ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2422{
2423 sdrReservationID++;
2424 if (sdrReservationID == 0)
2425 {
2426 sdrReservationID++;
2427 }
2428
2429 return ipmi::responseSuccess(sdrReservationID);
2430}
2431
2432ipmi::RspType<uint16_t, // next record ID
2433 std::vector<uint8_t> // payload
2434 >
2435 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2436 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2437{
2438 // reservation required for partial reads with non zero offset into
2439 // record
2440 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2441 {
George Liude6694e2024-07-17 15:22:25 +08002442 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002443 return ipmi::responseInvalidReservationId();
2444 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002445
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002446 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002447 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002448 {
George Liude6694e2024-07-17 15:22:25 +08002449 lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002450 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002451 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002452
Willy Tu4eca2512022-06-20 21:14:51 -07002453 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2454
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002455 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002456 int nextRecordId = getSensorDataRecord(
2457 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2458 record, recordID, offset + bytesToRead);
2459
2460 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002461 {
George Liude6694e2024-07-17 15:22:25 +08002462 lg2::error("ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002463 return ipmi::responseInvalidFieldRequest();
2464 }
Willy Tude54f482021-01-26 15:59:09 -08002465 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002466 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002467 if (!hdr)
2468 {
George Liude6694e2024-07-17 15:22:25 +08002469 lg2::error("ipmiStorageGetSDR: record header is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002470 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002471 }
2472
Patrick Williams1318a5e2024-08-16 15:19:54 -04002473 size_t sdrLength =
George Liu42247d22025-08-19 10:24:47 +08002474 sizeof(get_sdr::SensorDataRecordHeader) + hdr->recordLength;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002475 if (offset >= sdrLength)
2476 {
George Liude6694e2024-07-17 15:22:25 +08002477 lg2::error("ipmiStorageGetSDR: offset is outside the record");
Vernon Mauery6dbea082023-07-21 11:43:00 -07002478 return ipmi::responseParmOutOfRange();
2479 }
Willy Tude54f482021-01-26 15:59:09 -08002480 if (sdrLength < (offset + bytesToRead))
2481 {
2482 bytesToRead = sdrLength - offset;
2483 }
2484
2485 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2486 if (!respStart)
2487 {
George Liude6694e2024-07-17 15:22:25 +08002488 lg2::error("ipmiStorageGetSDR: record is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002489 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002490 }
2491
2492 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002493
Willy Tude54f482021-01-26 15:59:09 -08002494 return ipmi::responseSuccess(nextRecordId, recordData);
2495}
adarshgrami042e9db2022-09-15 10:34:34 +05302496namespace dcmi
2497{
2498
Thang Tranb1416ef2023-08-02 13:57:09 +07002499std::tuple<uint8_t, // Total of instance sensors
2500 std::vector<sensorInfo> // The list of sensors
2501 >
2502 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2503 uint8_t entityInstance, uint8_t instanceStart)
2504{
2505 std::vector<sensorInfo> sensorList;
2506 uint8_t totalInstSensor = 0;
2507 auto match = ipmi::dcmi::validEntityId.find(entityId);
2508
2509 if (match == ipmi::dcmi::validEntityId.end())
2510 {
2511 return std::make_tuple(totalInstSensor, sensorList);
2512 }
2513
2514 auto& sensorTree = getSensorTree();
2515 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2516 {
2517 return std::make_tuple(totalInstSensor, sensorList);
2518 }
2519
2520 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2521
Willy Tu62e3ca82024-01-31 17:28:34 +00002522 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002523 for (const auto& sensor : sensorTree)
2524 {
2525 const std::string& sensorObjPath = sensor.first;
2526 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2527
2528 /*
2529 * In the DCMI specification, it only supports the sensor type is 0x01
2530 * (temperature type) for both Get Sensor Info and Get Temperature
2531 * Readings commands.
2532 */
2533 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2534 {
2535 continue;
2536 }
2537
2538 const auto& connection = sensor.second.begin()->first;
2539 DbusInterfaceMap sensorMap;
2540
2541 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2542 sensorMapSdrUpdatePeriod))
2543 {
George Liude6694e2024-07-17 15:22:25 +08002544 lg2::error("Failed to update sensor map for threshold sensor, "
2545 "service: {SERVICE}, path: {PATH}",
2546 "SERVICE", connection, "PATH", sensorObjPath);
Thang Tranb1416ef2023-08-02 13:57:09 +07002547 continue;
2548 }
2549
2550 uint8_t entityIdValue = 0;
2551 uint8_t entityInstanceValue = 0;
2552
2553 /*
2554 * Get the Entity ID, Entity Instance information which are configured
2555 * in the Entity-Manger.
2556 */
2557 updateIpmiFromAssociation(
2558 sensorObjPath,
2559 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2560 sensorMap, entityIdValue, entityInstanceValue);
2561
2562 if (entityIdValue == match->first || entityIdValue == match->second)
2563 {
2564 totalInstSensor++;
2565
2566 /*
2567 * When Entity Instance parameter is not 0, we only get the first
2568 * sensor whose Entity Instance number is equal input Entity
2569 * Instance parameter.
2570 */
2571 if (entityInstance)
2572 {
2573 if (!sensorList.empty())
2574 {
2575 continue;
2576 }
2577
2578 if (entityInstanceValue == entityInstance)
2579 {
2580 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002581 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002582 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002583 ++invalidSensorNumberErrCount;
2584 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002585 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002586 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2587 recordId, entityIdValue,
2588 entityInstanceValue);
2589 }
2590 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002591 else if (entityInstanceValue >= instanceStart)
2592 {
2593 auto recordId = getSensorNumberFromPath(sensorObjPath);
2594 if (recordId == invalidSensorNumber)
2595 {
2596 ++invalidSensorNumberErrCount;
2597 continue;
2598 }
2599 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2600 recordId, entityIdValue,
2601 entityInstanceValue);
2602 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002603 }
2604 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002605 if (invalidSensorNumberErrCount != 0)
2606 {
George Liude6694e2024-07-17 15:22:25 +08002607 lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2608 "{ERR_COUNT} times",
2609 "ERR_COUNT", invalidSensorNumberErrCount);
Willy Tu62e3ca82024-01-31 17:28:34 +00002610 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002611
2612 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2613 return first.entityInstance <= second.entityInstance;
2614 };
2615
2616 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2617
2618 return std::make_tuple(totalInstSensor, sensorList);
2619}
2620
Thang Tran3dad8262023-08-17 15:20:56 +07002621std::tuple<bool, // Reading result
2622 uint7_t, // Temp value
2623 bool> // Sign bit
2624 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2625{
2626 std::string service{};
2627 boost::system::error_code ec =
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002628 ipmi::getService(ctx, SensorValue::interface, objectPath, service);
Thang Tran3dad8262023-08-17 15:20:56 +07002629 if (ec.value())
2630 {
2631 return std::make_tuple(false, 0, false);
2632 }
2633
2634 ipmi::PropertyMap properties{};
2635 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002636 SensorValue::interface, properties);
Thang Tran3dad8262023-08-17 15:20:56 +07002637 if (ec.value())
2638 {
2639 return std::make_tuple(false, 0, false);
2640 }
2641
2642 auto scaleIt = properties.find("Scale");
2643 double scaleVal = 0.0;
2644 if (scaleIt != properties.end())
2645 {
2646 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2647 }
2648
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002649 auto tempValIt = properties.find(SensorValue::property_names::value);
Thang Tran3dad8262023-08-17 15:20:56 +07002650 double tempVal = 0.0;
2651 if (tempValIt == properties.end())
2652 {
2653 return std::make_tuple(false, 0, false);
2654 }
2655
2656 const double maxTemp = 127;
2657 double absTempVal = 0.0;
2658 bool signBit = false;
2659
2660 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2661 tempVal = std::pow(10, scaleVal) * tempVal;
2662 absTempVal = std::abs(tempVal);
2663 absTempVal = std::min(absTempVal, maxTemp);
2664 signBit = (tempVal < 0) ? true : false;
2665
2666 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2667}
2668
adarshgrami042e9db2022-09-15 10:34:34 +05302669ipmi::RspType<uint8_t, // No of instances for requested id
2670 uint8_t, // No of record ids in the response
2671 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2672 // IDs
2673 >
2674 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002675 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302676{
2677 auto match = ipmi::dcmi::validEntityId.find(entityId);
2678 if (match == ipmi::dcmi::validEntityId.end())
2679 {
George Liude6694e2024-07-17 15:22:25 +08002680 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
adarshgrami042e9db2022-09-15 10:34:34 +05302681
2682 return ipmi::responseInvalidFieldRequest();
2683 }
2684
2685 if (sensorType != ipmi::dcmi::temperatureSensorType)
2686 {
George Liude6694e2024-07-17 15:22:25 +08002687 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2688 sensorType);
adarshgrami042e9db2022-09-15 10:34:34 +05302689
2690 return ipmi::responseInvalidFieldRequest();
2691 }
adarshgrami042e9db2022-09-15 10:34:34 +05302692
2693 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002694 const auto& [totalSensorInst, sensorList] =
2695 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302696
Thang Tranb1416ef2023-08-02 13:57:09 +07002697 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302698 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002699 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2700 }
adarshgrami042e9db2022-09-15 10:34:34 +05302701
Thang Tranb1416ef2023-08-02 13:57:09 +07002702 /*
2703 * As DCMI specification, the maximum number of Record Ids of response data
2704 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2705 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2706 * in response data.
2707 */
2708 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2709
2710 for (const auto& sensor : sensorList)
2711 {
2712 sensorRec.emplace_back(sensor.recordId);
2713 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302714 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002715 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302716 }
2717 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002718
2719 return ipmi::responseSuccess(
2720 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302721}
Thang Tran3dad8262023-08-17 15:20:56 +07002722
2723ipmi::RspType<uint8_t, // No of instances for requested id
2724 uint8_t, // No of record ids in the response
2725 std::vector< // Temperature Data
2726 std::tuple<uint7_t, // Temperature value
2727 bool, // Sign bit
2728 uint8_t // Entity Instance of sensor
2729 >>>
2730 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2731 uint8_t entityId, uint8_t entityInstance,
2732 uint8_t instanceStart)
2733{
2734 auto match = ipmi::dcmi::validEntityId.find(entityId);
2735 if (match == ipmi::dcmi::validEntityId.end())
2736 {
George Liude6694e2024-07-17 15:22:25 +08002737 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Thang Tran3dad8262023-08-17 15:20:56 +07002738
2739 return ipmi::responseInvalidFieldRequest();
2740 }
2741
2742 if (sensorType != ipmi::dcmi::temperatureSensorType)
2743 {
George Liude6694e2024-07-17 15:22:25 +08002744 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2745 sensorType);
Thang Tran3dad8262023-08-17 15:20:56 +07002746
2747 return ipmi::responseInvalidFieldRequest();
2748 }
2749
2750 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2751 const auto& [totalSensorInst, sensorList] =
2752 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2753
2754 if (sensorList.empty())
2755 {
2756 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2757 }
2758
2759 /*
2760 * As DCMI specification, the maximum number of Record Ids of response data
2761 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2762 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2763 * in response data.
2764 */
2765 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2766
2767 for (const auto& sensor : sensorList)
2768 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002769 const auto& [readResult, tempVal, signBit] =
2770 readTemp(ctx, sensor.objectPath);
Thang Tran3dad8262023-08-17 15:20:56 +07002771
2772 if (readResult)
2773 {
2774 tempReadingVal.emplace_back(
2775 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2776
2777 if (tempReadingVal.size() >= numOfRec)
2778 {
2779 break;
2780 }
2781 }
2782 }
2783
2784 return ipmi::responseSuccess(totalSensorInst,
2785 static_cast<uint8_t>(tempReadingVal.size()),
2786 tempReadingVal);
2787}
2788
adarshgrami042e9db2022-09-15 10:34:34 +05302789} // namespace dcmi
2790
Willy Tude54f482021-01-26 15:59:09 -08002791/* end storage commands */
2792
2793void registerSensorFunctions()
2794{
2795 // <Platform Event>
2796 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2797 ipmi::sensor_event::cmdPlatformEvent,
2798 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2799
Willy Tudbafbce2021-03-29 00:37:05 -07002800 // <Set Sensor Reading and Event Status>
2801 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2802 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2803 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002804
Willy Tude54f482021-01-26 15:59:09 -08002805 // <Get Sensor Reading>
2806 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2807 ipmi::sensor_event::cmdGetSensorReading,
2808 ipmi::Privilege::User, ipmiSenGetSensorReading);
2809
2810 // <Get Sensor Threshold>
2811 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2812 ipmi::sensor_event::cmdGetSensorThreshold,
2813 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2814
2815 // <Set Sensor Threshold>
2816 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2817 ipmi::sensor_event::cmdSetSensorThreshold,
2818 ipmi::Privilege::Operator,
2819 ipmiSenSetSensorThresholds);
2820
2821 // <Get Sensor Event Enable>
2822 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2823 ipmi::sensor_event::cmdGetSensorEventEnable,
2824 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2825
2826 // <Get Sensor Event Status>
2827 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2828 ipmi::sensor_event::cmdGetSensorEventStatus,
2829 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2830
2831 // register all storage commands for both Sensor and Storage command
2832 // versions
2833
2834 // <Get SDR Repository Info>
2835 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2836 ipmi::storage::cmdGetSdrRepositoryInfo,
2837 ipmi::Privilege::User,
2838 ipmiStorageGetSDRRepositoryInfo);
2839
2840 // <Get Device SDR Info>
2841 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2842 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2843 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2844
2845 // <Get SDR Allocation Info>
2846 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2847 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2848 ipmi::Privilege::User,
2849 ipmiStorageGetSDRAllocationInfo);
2850
2851 // <Reserve SDR Repo>
2852 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2853 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2854 ipmi::Privilege::User, ipmiStorageReserveSDR);
2855
2856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2857 ipmi::storage::cmdReserveSdrRepository,
2858 ipmi::Privilege::User, ipmiStorageReserveSDR);
2859
2860 // <Get Sdr>
2861 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2862 ipmi::sensor_event::cmdGetDeviceSdr,
2863 ipmi::Privilege::User, ipmiStorageGetSDR);
2864
2865 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2866 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2867 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302868 // <Get DCMI Sensor Info>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002869 ipmi::registerGroupHandler(
2870 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2871 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2872 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002873 // <Get Temperature Readings>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002874 ipmi::registerGroupHandler(
2875 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2876 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2877 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002878}
2879} // namespace ipmi