blob: 6d0346824dd45d3d88905943a0de5d7a4ab091d6 [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 Hansen653aec02025-11-14 13:38:08 +010034#include <xyz/openbmc_project/Sensor/Threshold/Critical/common.hpp>
35#include <xyz/openbmc_project/Sensor/Threshold/Warning/common.hpp>
Alexander Hansenf365c7f2025-11-14 12:08:34 +010036#include <xyz/openbmc_project/Sensor/Value/common.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050037
38#include <algorithm>
39#include <array>
Willy Tude54f482021-01-26 15:59:09 -080040#include <chrono>
41#include <cmath>
42#include <cstring>
Willy Tu62e3ca82024-01-31 17:28:34 +000043#include <format>
Willy Tude54f482021-01-26 15:59:09 -080044#include <map>
Willy Tude54f482021-01-26 15:59:09 -080045#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080046#include <stdexcept>
47#include <string>
48#include <utility>
49#include <variant>
50
Alexander Hansenf365c7f2025-11-14 12:08:34 +010051using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
Alexander Hansen653aec02025-11-14 13:38:08 +010052using SensorThresholdWarning =
53 sdbusplus::common::xyz::openbmc_project::sensor::threshold::Warning;
54using SensorThresholdCritical =
55 sdbusplus::common::xyz::openbmc_project::sensor::threshold::Critical;
Alexander Hansenf365c7f2025-11-14 12:08:34 +010056
Scron Chang2703b022021-07-06 15:47:45 +080057#ifdef FEATURE_HYBRID_SENSORS
58
59#include "sensordatahandler.hpp"
60namespace ipmi
61{
62namespace sensor
63{
64extern const IdInfoMap sensors;
65} // namespace sensor
66} // namespace ipmi
67#endif
adarshgrami042e9db2022-09-15 10:34:34 +053068namespace ipmi
69{
70namespace dcmi
71{
72// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
73static const std::map<uint8_t, uint8_t> validEntityId{
74 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
75 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
76constexpr uint8_t temperatureSensorType = 0x01;
77constexpr uint8_t maxRecords = 8;
78} // namespace dcmi
79} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080080constexpr std::array<const char*, 7> suffixes = {
81 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
82 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080083namespace ipmi
84{
Hao Jiangd48c9212021-02-03 15:45:06 -080085
86using phosphor::logging::entry;
87using phosphor::logging::level;
88using phosphor::logging::log;
89
Willy Tude54f482021-01-26 15:59:09 -080090static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070091static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080092
Willy Tu38e7a2b2021-03-29 15:09:56 -070093// BMC I2C address is generally at 0x20
94static constexpr uint8_t bmcI2CAddr = 0x20;
95
Willy Tude54f482021-01-26 15:59:09 -080096constexpr size_t maxSDRTotalSize =
97 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
98constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
99
100static uint16_t sdrReservationID;
101static uint32_t sdrLastAdd = noTimestamp;
102static uint32_t sdrLastRemove = noTimestamp;
103static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700104
105// The IPMI spec defines four Logical Units (LUN), each capable of supporting
106// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
107// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800108// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700109// location.
110static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
111
Harvey Wu75893062023-03-22 17:17:31 +0800112static constexpr uint8_t lun0 = 0x0;
113static constexpr uint8_t lun1 = 0x1;
114static constexpr uint8_t lun3 = 0x3;
115
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700116static constexpr size_t lun0MaxSensorNum = 0xfe;
117static constexpr size_t lun1MaxSensorNum = 0x1fe;
118static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800119static constexpr int GENERAL_ERROR = -1;
120
Willy Tude54f482021-01-26 15:59:09 -0800121static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
122
123// Specify the comparison required to sort and find char* map objects
124struct CmpStr
125{
126 bool operator()(const char* a, const char* b) const
127 {
128 return std::strcmp(a, b) < 0;
129 }
130};
131const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
132 sensorUnits{{{"temperature", SensorUnits::degreesC},
133 {"voltage", SensorUnits::volts},
134 {"current", SensorUnits::amps},
135 {"fan_tach", SensorUnits::rpm},
Johnathan Mantey7b037272024-06-17 12:12:40 -0700136 {"power", SensorUnits::watts},
137 {"energy", SensorUnits::joules}}};
Willy Tude54f482021-01-26 15:59:09 -0800138
139void registerSensorFunctions() __attribute__((constructor));
140
Patrick Williams5d82f472022-07-22 19:26:53 -0500141static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800142 *getSdBus(),
143 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
144 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500145 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400146 getSensorTree().clear();
147 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
148 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
149 std::chrono::system_clock::now().time_since_epoch())
150 .count();
151 });
Willy Tude54f482021-01-26 15:59:09 -0800152
Patrick Williams5d82f472022-07-22 19:26:53 -0500153static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800154 *getSdBus(),
155 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
156 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500157 [](sdbusplus::message_t&) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400158 getSensorTree().clear();
159 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
160 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
161 std::chrono::system_clock::now().time_since_epoch())
162 .count();
163 });
Willy Tude54f482021-01-26 15:59:09 -0800164
George Liu23c868c2025-07-04 09:31:35 +0800165ipmi::Cc getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
166 std::string& connection, std::string& path,
167 std::vector<std::string>* interfaces)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700168{
169 auto& sensorTree = getSensorTree();
170 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
171 {
George Liu879c1d82025-07-03 09:36:57 +0800172 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700173 }
174
175 if (ctx == nullptr)
176 {
George Liu879c1d82025-07-03 09:36:57 +0800177 return ipmi::ccResponseError;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700178 }
179
180 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
181 if (path.empty())
182 {
George Liu879c1d82025-07-03 09:36:57 +0800183 return ipmi::ccInvalidFieldRequest;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700184 }
185
186 for (const auto& sensor : sensorTree)
187 {
188 if (path == sensor.first)
189 {
190 connection = sensor.second.begin()->first;
191 if (interfaces)
192 *interfaces = sensor.second.begin()->second;
193 break;
194 }
195 }
196
197 return 0;
198}
199
200SensorSubTree& getSensorTree()
201{
202 static SensorSubTree sensorTree;
203 return sensorTree;
204}
205
Willy Tude54f482021-01-26 15:59:09 -0800206// this keeps track of deassertions for sensor event status command. A
207// deasertion can only happen if an assertion was seen first.
208static boost::container::flat_map<
209 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
210 thresholdDeassertMap;
211
Patrick Williams5d82f472022-07-22 19:26:53 -0500212static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800213 *getSdBus(),
214 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
215 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500216 [](sdbusplus::message_t& m) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400217 boost::container::flat_map<std::string, std::variant<bool, double>>
218 values;
219 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800220
Patrick Williams1318a5e2024-08-16 15:19:54 -0400221 auto findAssert =
222 std::find_if(values.begin(), values.end(), [](const auto& pair) {
223 return pair.first.find("Alarm") != std::string::npos;
224 });
225 if (findAssert != values.end())
Willy Tude54f482021-01-26 15:59:09 -0800226 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400227 auto ptr = std::get_if<bool>(&(findAssert->second));
228 if (ptr == nullptr)
229 {
230 lg2::error("thresholdChanged: Assert non bool");
231 return;
232 }
233 if (*ptr)
Willy Tude54f482021-01-26 15:59:09 -0800234 {
George Liude6694e2024-07-17 15:22:25 +0800235 lg2::info(
Patrick Williams1318a5e2024-08-16 15:19:54 -0400236 "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
George Liude6694e2024-07-17 15:22:25 +0800237 "SENSOR_PATH", m.get_path());
Patrick Williams1318a5e2024-08-16 15:19:54 -0400238 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
239 }
240 else
241 {
242 auto& value =
243 thresholdDeassertMap[m.get_path()][findAssert->first];
244 if (value)
245 {
246 lg2::info(
247 "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
248 "SENSOR_PATH", m.get_path());
249 value = *ptr;
250 }
Willy Tude54f482021-01-26 15:59:09 -0800251 }
252 }
Patrick Williams1318a5e2024-08-16 15:19:54 -0400253 });
Willy Tude54f482021-01-26 15:59:09 -0800254
Hao Jiangd2afd052020-12-10 15:09:32 -0800255namespace sensor
256{
257static constexpr const char* vrInterface =
258 "xyz.openbmc_project.Control.VoltageRegulatorMode";
Hao Jiangd2afd052020-12-10 15:09:32 -0800259} // namespace sensor
260
Willy Tude54f482021-01-26 15:59:09 -0800261static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
262 double& min)
263{
264 max = 127;
265 min = -128;
266
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100267 auto sensorObject = sensorMap.find(SensorValue::interface);
Alexander Hansen653aec02025-11-14 13:38:08 +0100268 auto critical = sensorMap.find(SensorThresholdCritical::interface);
269 auto warning = sensorMap.find(SensorThresholdWarning::interface);
Willy Tude54f482021-01-26 15:59:09 -0800270
271 if (sensorObject != sensorMap.end())
272 {
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100273 auto maxMap =
274 sensorObject->second.find(SensorValue::property_names::max_value);
275 auto minMap =
276 sensorObject->second.find(SensorValue::property_names::min_value);
Willy Tude54f482021-01-26 15:59:09 -0800277
278 if (maxMap != sensorObject->second.end())
279 {
280 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
281 }
282 if (minMap != sensorObject->second.end())
283 {
284 min = std::visit(VariantToDoubleVisitor(), minMap->second);
285 }
286 }
287 if (critical != sensorMap.end())
288 {
Alexander Hansen653aec02025-11-14 13:38:08 +0100289 auto lower = critical->second.find(
290 SensorThresholdCritical::property_names::critical_low);
291 auto upper = critical->second.find(
292 SensorThresholdCritical::property_names::critical_high);
Willy Tude54f482021-01-26 15:59:09 -0800293 if (lower != critical->second.end())
294 {
295 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300296 if (std::isfinite(value))
297 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700298 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300299 }
Willy Tude54f482021-01-26 15:59:09 -0800300 }
301 if (upper != critical->second.end())
302 {
303 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300304 if (std::isfinite(value))
305 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700306 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300307 }
Willy Tude54f482021-01-26 15:59:09 -0800308 }
309 }
310 if (warning != sensorMap.end())
311 {
Alexander Hansen653aec02025-11-14 13:38:08 +0100312 auto lower = warning->second.find(
313 SensorThresholdWarning::property_names::warning_low);
314 auto upper = warning->second.find(
315 SensorThresholdWarning::property_names::warning_high);
Willy Tude54f482021-01-26 15:59:09 -0800316 if (lower != warning->second.end())
317 {
318 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300319 if (std::isfinite(value))
320 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700321 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300322 }
Willy Tude54f482021-01-26 15:59:09 -0800323 }
324 if (upper != warning->second.end())
325 {
326 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300327 if (std::isfinite(value))
328 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700329 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300330 }
Willy Tude54f482021-01-26 15:59:09 -0800331 }
332 }
333}
334
335static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700336 std::string sensorPath, DbusInterfaceMap& sensorMap,
337 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800338{
Scron Chang2703b022021-07-06 15:47:45 +0800339#ifdef FEATURE_HYBRID_SENSORS
340 if (auto sensor = findStaticSensor(sensorPath);
341 sensor != ipmi::sensor::sensors.end() &&
342 getSensorEventTypeFromPath(sensorPath) !=
343 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
344 {
345 // If the incoming sensor is a discrete sensor, it might fail in
346 // getManagedObjects(), return true, and use its own getFunc to get
347 // value.
348 return true;
349 }
350#endif
351
Willy Tude54f482021-01-26 15:59:09 -0800352 static boost::container::flat_map<
353 std::string, std::chrono::time_point<std::chrono::steady_clock>>
354 updateTimeMap;
355
356 auto updateFind = updateTimeMap.find(sensorConnection);
357 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
358 if (updateFind != updateTimeMap.end())
359 {
360 lastUpdate = updateFind->second;
361 }
362
363 auto now = std::chrono::steady_clock::now();
364
365 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700366 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800367 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800368 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800369
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800370 // Object managers for different kinds of OpenBMC DBus interfaces.
371 // Documented in the phosphor-dbus-interfaces repository.
372 const char* paths[] = {
373 "/xyz/openbmc_project/sensors",
374 "/xyz/openbmc_project/vr",
375 };
George Liuf2807ed2025-04-03 15:52:57 +0800376 constexpr size_t numPaths = sizeof(paths) / sizeof(paths[0]);
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800377 ObjectValueTree allManagedObjects;
378
George Liuf2807ed2025-04-03 15:52:57 +0800379 for (size_t i = 0; i < numPaths; i++)
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800380 {
381 ObjectValueTree managedObjects;
382 boost::system::error_code ec = getManagedObjects(
383 ctx, sensorConnection.c_str(), paths[i], managedObjects);
384 if (ec)
385 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800386 continue;
387 }
388 allManagedObjects.merge(managedObjects);
389 found = true;
390 }
391
392 if (!found)
393 {
George Liude6694e2024-07-17 15:22:25 +0800394 lg2::error("GetMangagedObjects for getSensorMap failed, "
395 "service: {SERVICE}",
396 "SERVICE", sensorConnection);
Tom Tung6615d472023-05-31 18:48:12 +0800397
Willy Tude54f482021-01-26 15:59:09 -0800398 return false;
399 }
400
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800401 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700402 // Update time after finish building the map which allow the
403 // data to be cached for updatePeriod plus the build time.
404 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800405 }
406 auto connection = SensorCache.find(sensorConnection);
407 if (connection == SensorCache.end())
408 {
409 return false;
410 }
411 auto path = connection->second.find(sensorPath);
412 if (path == connection->second.end())
413 {
414 return false;
415 }
416 sensorMap = path->second;
417
418 return true;
419}
420
Hao Jiangd2afd052020-12-10 15:09:32 -0800421namespace sensor
422{
Hao Jiangd48c9212021-02-03 15:45:06 -0800423// Read VR profiles from sensor(daemon) interface
Patrick Williams69b4c282025-03-03 11:19:13 -0500424static std::optional<std::vector<std::string>> getSupportedVrProfiles(
425 const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800426{
427 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800428 auto supportedProperty = object.find("Supported");
429 if (supportedProperty == object.end() ||
430 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800431 {
George Liude6694e2024-07-17 15:22:25 +0800432 lg2::error("Missing the required Supported and Selected properties");
Hao Jiangd2afd052020-12-10 15:09:32 -0800433 return std::nullopt;
434 }
435
436 const auto profilesPtr =
437 std::get_if<std::vector<std::string>>(&supportedProperty->second);
438
439 if (profilesPtr == nullptr)
440 {
George Liude6694e2024-07-17 15:22:25 +0800441 lg2::error("property is not array of string");
Hao Jiangd2afd052020-12-10 15:09:32 -0800442 return std::nullopt;
443 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800444 return *profilesPtr;
445}
446
447// Calculate VR Mode from input IPMI discrete event bytes
Patrick Williams1318a5e2024-08-16 15:19:54 -0400448static std::optional<std::string> calculateVRMode(
449 uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
Hao Jiangd48c9212021-02-03 15:45:06 -0800450{
451 // get VR mode profiles from Supported Interface
452 auto profiles = getSupportedVrProfiles(VRObject);
453 if (!profiles)
454 {
455 return std::nullopt;
456 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800457
458 // interpret IPMI cmd bits into profiles' index
459 long unsigned int index = 0;
460 // only one bit should be set and the highest bit should not be used.
461 if (assertOffset == 0 || assertOffset == (1u << 15) ||
462 (assertOffset & (assertOffset - 1)))
463 {
George Liude6694e2024-07-17 15:22:25 +0800464 lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
465 lg2::hex, static_cast<uint16_t>(assertOffset));
Hao Jiangd2afd052020-12-10 15:09:32 -0800466 return std::nullopt;
467 }
468
469 while (assertOffset != 1)
470 {
471 assertOffset >>= 1;
472 index++;
473 }
474
Hao Jiangd48c9212021-02-03 15:45:06 -0800475 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800476 {
George Liude6694e2024-07-17 15:22:25 +0800477 lg2::error("profile index out of boundary");
Hao Jiangd2afd052020-12-10 15:09:32 -0800478 return std::nullopt;
479 }
480
Hao Jiangd48c9212021-02-03 15:45:06 -0800481 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800482}
483
484// Calculate sensor value from IPMI reading byte
Patrick Williams69b4c282025-03-03 11:19:13 -0500485static std::optional<double> calculateValue(
486 uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
487 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
Hao Jiangd2afd052020-12-10 15:09:32 -0800488{
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100489 if (valueObject.find(SensorValue::property_names::value) ==
490 valueObject.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800491 {
George Liude6694e2024-07-17 15:22:25 +0800492 lg2::error("Missing the required Value property");
Hao Jiangd2afd052020-12-10 15:09:32 -0800493 return std::nullopt;
494 }
495
496 double max = 0;
497 double min = 0;
498 getSensorMaxMin(sensorMap, max, min);
499
500 int16_t mValue = 0;
501 int16_t bValue = 0;
502 int8_t rExp = 0;
503 int8_t bExp = 0;
504 bool bSigned = false;
505
506 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
507 {
508 return std::nullopt;
509 }
510
511 double value = bSigned ? ((int8_t)reading) : reading;
512
513 value *= ((double)mValue);
514 value += ((double)bValue) * std::pow(10.0, bExp);
515 value *= std::pow(10.0, rExp);
516
517 return value;
518}
519
Willy Tu38e7a2b2021-03-29 15:09:56 -0700520// Extract file name from sensor path as the sensors SDR ID. Simplify the name
521// if it is too long.
522std::string parseSdrIdFromPath(const std::string& path)
523{
524 std::string name;
525 size_t nameStart = path.rfind("/");
526 if (nameStart != std::string::npos)
527 {
528 name = path.substr(nameStart + 1, std::string::npos - nameStart);
529 }
530
Willy Tu38e7a2b2021-03-29 15:09:56 -0700531 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
532 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200533#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800534 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700535 {
George Liuc024b392025-08-21 16:38:58 +0800536 if (name.ends_with(suffix))
JeffLind950f412021-10-20 18:49:34 +0800537 {
538 boost::replace_all(name, suffix, "");
539 break;
540 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700541 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200542#endif
543#ifdef SHORTNAME_REPLACE_WORDS
544 constexpr std::array<std::pair<const char*, const char*>, 2>
545 replaceWords = {std::make_pair("Output", "Out"),
546 std::make_pair("Input", "In")};
547 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800548 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200549 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800550 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200551#endif
552
553 // as a backup and if nothing else is configured
554 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700555 }
556 return name;
557}
558
Hao Jiangd48c9212021-02-03 15:45:06 -0800559bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
560 const std::string& path,
561 const ipmi::DbusInterfaceMap::mapped_type& object,
562 std::bitset<16>& assertions)
563{
564 auto profiles = sensor::getSupportedVrProfiles(object);
565 if (!profiles)
566 {
567 return false;
568 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700569 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800570
571 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700572 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800573 if (ec)
574 {
George Liude6694e2024-07-17 15:22:25 +0800575 lg2::error("Failed to get Selected, path: {PATH}, "
576 "interface: {INTERFACE}, error: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100577 "PATH", path, "INTERFACE", SensorValue::interface, "ERROR",
George Liude6694e2024-07-17 15:22:25 +0800578 ec.message());
Hao Jiangd48c9212021-02-03 15:45:06 -0800579 return false;
580 }
581
Willy Tu8366f0b2022-04-29 05:00:17 -0700582 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800583 if (itr == profiles->end())
584 {
George Liude6694e2024-07-17 15:22:25 +0800585 lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
586 "PATH", path);
Hao Jiangd48c9212021-02-03 15:45:06 -0800587 return false;
588 }
589 std::size_t index =
590 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
591
Willy Tubef102a2022-06-09 15:36:09 -0700592 // map index to response event assertion bit.
593 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800594 {
Willy Tubef102a2022-06-09 15:36:09 -0700595 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800596 }
597 else
598 {
George Liude6694e2024-07-17 15:22:25 +0800599 lg2::error("VR profile index reaches max assertion bit, "
600 "path: {PATH}, index: {INDEX}",
601 "PATH", path, "INDEX", index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800602 return false;
603 }
604 if constexpr (debug)
605 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800606 lg2::error("VR sensor {PATH} mode is: [{INDEX}] {MODE}", "PATH",
607 sensor::parseSdrIdFromPath(path), "INDEX", index, "MODE",
608 mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800609 }
610 return true;
611}
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700612
613/*
614 * Handle every Sensor Data Record besides Type 01
615 *
616 * The D-Bus sensors work well for generating Type 01 SDRs.
617 * After the Type 01 sensors are processed the remaining sensor types require
618 * special handling. Each BMC vendor is going to have their own requirements for
619 * insertion of non-Type 01 records.
620 * Manage non-Type 01 records:
621 *
622 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
623 * Populate it with the two weakly linked functions below, without adding the
624 * 'weak' attribute definition prior to the function definition.
625 * getOtherSensorsCount(...)
626 * getOtherSensorsDataRecord(...)
627 * Example contents are provided in the weak definitions below
628 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
629 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
630 * The contents of the sensorcommands_oem.cpp file will then override the code
631 * provided below.
632 */
633
634size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
635size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
636{
637 size_t fruCount = 0;
638
639 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
640 if (ret != ipmi::ccSuccess)
641 {
George Liude6694e2024-07-17 15:22:25 +0800642 lg2::error("getOtherSensorsCount: getFruSdrCount error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700643 return std::numeric_limits<size_t>::max();
644 }
645
646 const auto& entityRecords =
647 ipmi::sensor::EntityInfoMapContainer::getContainer()
648 ->getIpmiEntityRecords();
649 size_t entityCount = entityRecords.size();
650
651 return fruCount + ipmi::storage::type12Count + entityCount;
652}
653
654int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
655 std::vector<uint8_t>& recordData)
656 __attribute__((weak));
657int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
658 std::vector<uint8_t>& recordData)
659{
660 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
661 if (otherCount == std::numeric_limits<size_t>::max())
662 {
663 return GENERAL_ERROR;
664 }
665 const auto& entityRecords =
666 ipmi::sensor::EntityInfoMapContainer::getContainer()
667 ->getIpmiEntityRecords();
668
669 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
670 size_t entityCount{entityRecords.size()};
671 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
672
673 if (sdrIndex > otherCount)
674 {
675 return std::numeric_limits<int>::min();
676 }
677 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
678 {
679 // handle type 8 entity map records
680 ipmi::sensor::EntityInfoMap::const_iterator entity =
681 entityRecords.find(static_cast<uint8_t>(
682 sdrIndex - fruCount - ipmi::storage::type12Count));
683
684 if (entity == entityRecords.end())
685 {
686 return GENERAL_ERROR;
687 }
688 recordData = ipmi::storage::getType8SDRs(entity, recordID);
689 }
690 else if (sdrIndex >= fruCount)
691 {
692 // handle type 12 hardcoded records
693 size_t type12Index = sdrIndex - fruCount;
694 if (type12Index >= ipmi::storage::type12Count)
695 {
George Liude6694e2024-07-17 15:22:25 +0800696 lg2::error("getSensorDataRecord: type12Index error");
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700697 return GENERAL_ERROR;
698 }
699 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
700 }
701 else
702 {
703 // handle fru records
704 get_sdr::SensorDataFruRecord data;
705 if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
George Liue8a97bd2024-12-05 17:26:46 +0800706 ret != ipmi::ccSuccess)
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700707 {
708 return GENERAL_ERROR;
709 }
George Liuc7a4da52025-08-19 16:20:31 +0800710 data.header.recordId = recordID;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700711 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
712 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
713 }
714
715 return 0;
716}
717
Hao Jiangd2afd052020-12-10 15:09:32 -0800718} // namespace sensor
719
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000720ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
721 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800722{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000723 constexpr const uint8_t validEnvmRev = 0x04;
724 constexpr const uint8_t lastSensorType = 0x2C;
725 constexpr const uint8_t oemReserved = 0xC0;
726
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700727 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000728 uint8_t evmRev = 0;
729 uint8_t sensorType = 0;
730 uint8_t sensorNum = 0;
731 uint8_t eventType = 0;
732 uint8_t eventData1 = 0;
733 std::optional<uint8_t> eventData2 = 0;
734 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700735 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000736 ipmi::ChannelInfo chInfo;
737
738 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
739 {
George Liude6694e2024-07-17 15:22:25 +0800740 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
741 ctx->channel);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000742 return ipmi::responseUnspecifiedError();
743 }
744
745 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
746 ipmi::EChannelMediumType::systemInterface)
747 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700748 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000749 eventData1, eventData2, eventData3);
Johnathan Manteya440cd42024-06-20 13:21:48 -0700750 constexpr const uint8_t isSoftwareID = 0x01;
751 if (!(sysgeneratorID & isSoftwareID))
752 {
753 return ipmi::responseInvalidFieldRequest();
754 }
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700755 // Refer to IPMI Spec Table 32: SEL Event Records
756 generatorID = (ctx->channel << 12) // Channel
757 | (0x0 << 10) // Reserved
758 | (0x0 << 8) // 0x0 for sys-soft ID
Johnathan Manteya440cd42024-06-20 13:21:48 -0700759 | sysgeneratorID;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000760 }
761 else
762 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000763 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
764 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700765 // Refer to IPMI Spec Table 32: SEL Event Records
766 generatorID = (ctx->channel << 12) // Channel
767 | (0x0 << 10) // Reserved
768 | ((ctx->lun & 0x3) << 8) // Lun
769 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000770 }
771
772 if (!p.fullyUnpacked())
773 {
774 return ipmi::responseReqDataLenInvalid();
775 }
776
777 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
778 if (evmRev != validEnvmRev)
779 {
780 return ipmi::responseInvalidFieldRequest();
781 }
782 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
783 {
784 return ipmi::responseInvalidFieldRequest();
785 }
786
Willy Tude54f482021-01-26 15:59:09 -0800787 return ipmi::responseSuccess();
788}
789
Patrick Williams1318a5e2024-08-16 15:19:54 -0400790ipmi::RspType<> ipmiSetSensorReading(
791 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
792 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700793{
794 std::string connection;
795 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700796 std::vector<std::string> interfaces;
797
Patrick Williams1318a5e2024-08-16 15:19:54 -0400798 ipmi::Cc status =
799 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700800 if (status)
801 {
802 return ipmi::response(status);
803 }
804
Hao Jiangd2afd052020-12-10 15:09:32 -0800805 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700806 if (std::find(interfaces.begin(), interfaces.end(),
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100807 SensorValue::interface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700808 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700809 DbusInterfaceMap sensorMap;
810 if (!getSensorMap(ctx, connection, path, sensorMap))
811 {
812 return ipmi::responseResponseError();
813 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100814 auto sensorObject = sensorMap.find(SensorValue::interface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800815 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700816 {
817 return ipmi::responseResponseError();
818 }
819
Jie Yangf0a89942021-07-29 15:30:25 -0700820 // Only allow external SetSensor if write permission granted
Patrick Williams1318a5e2024-08-16 15:19:54 -0400821 if (!details::sdrWriteTable.getWritePermission(
822 (ctx->lun << 8) | sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700823 {
824 return ipmi::responseResponseError();
825 }
826
Patrick Williams1318a5e2024-08-16 15:19:54 -0400827 auto value =
828 sensor::calculateValue(reading, sensorMap, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800829 if (!value)
830 {
831 return ipmi::responseResponseError();
832 }
833
834 if constexpr (debug)
835 {
George Liude6694e2024-07-17 15:22:25 +0800836 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
837 "byte: {BYTE}, value: {VALUE}",
838 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
839 "VALUE", *value);
Hao Jiangd2afd052020-12-10 15:09:32 -0800840 }
841
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100842 boost::system::error_code ec = setDbusProperty(
843 ctx, connection, path, SensorValue::interface,
844 SensorValue::property_names::value, ipmi::Value(*value));
Hao Jiangd2afd052020-12-10 15:09:32 -0800845
846 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500847 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800848 // callback functions for now (e.g. ipmiSetSensorReading).
849 if (ec)
850 {
George Liude6694e2024-07-17 15:22:25 +0800851 lg2::error("Failed to set Value, path: {PATH}, "
852 "interface: {INTERFACE}, ERROR: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100853 "PATH", path, "INTERFACE", SensorValue::interface,
George Liude6694e2024-07-17 15:22:25 +0800854 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800855 return ipmi::responseResponseError();
856 }
857 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700858 }
859
Hao Jiange39d4d82021-04-16 17:02:40 -0700860 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
861 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700862 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700863 DbusInterfaceMap sensorMap;
864 if (!getSensorMap(ctx, connection, path, sensorMap))
865 {
866 return ipmi::responseResponseError();
867 }
868 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800869 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700870 {
871 return ipmi::responseResponseError();
872 }
873
Hao Jiangd2afd052020-12-10 15:09:32 -0800874 // VR sensors are treated as a special case and we will not check the
875 // write permission for VR sensors, since they always deemed writable
876 // and permission table are not applied to VR sensors.
Patrick Williams1318a5e2024-08-16 15:19:54 -0400877 auto vrMode =
878 sensor::calculateVRMode(assertOffset, sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800879 if (!vrMode)
880 {
881 return ipmi::responseResponseError();
882 }
883 boost::system::error_code ec = setDbusProperty(
884 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
885 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500886 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800887 // callback functions for now (e.g. ipmiSetSensorReading).
888 if (ec)
889 {
George Liude6694e2024-07-17 15:22:25 +0800890 lg2::error("Failed to set Selected, path: {PATH}, "
891 "interface: {INTERFACE}, ERROR: {ERROR}",
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100892 "PATH", path, "INTERFACE", SensorValue::interface,
George Liude6694e2024-07-17 15:22:25 +0800893 "ERROR", ec.message());
Hao Jiangd2afd052020-12-10 15:09:32 -0800894 }
895 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700896 }
897
George Liude6694e2024-07-17 15:22:25 +0800898 lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
Hao Jiangd2afd052020-12-10 15:09:32 -0800899 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700900}
901
Willy Tude54f482021-01-26 15:59:09 -0800902ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
903 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
904{
905 std::string connection;
906 std::string path;
907
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000908 if (sensnum == reservedSensorNumber)
909 {
910 return ipmi::responseInvalidFieldRequest();
911 }
912
Willy Tude54f482021-01-26 15:59:09 -0800913 auto status = getSensorConnection(ctx, sensnum, connection, path);
914 if (status)
915 {
916 return ipmi::response(status);
917 }
918
Scron Chang2703b022021-07-06 15:47:45 +0800919#ifdef FEATURE_HYBRID_SENSORS
920 if (auto sensor = findStaticSensor(path);
921 sensor != ipmi::sensor::sensors.end() &&
922 getSensorEventTypeFromPath(path) !=
923 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
924 {
925 if (ipmi::sensor::Mutability::Read !=
926 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
927 {
928 return ipmi::responseIllegalCommand();
929 }
930
Hariharan Rangasamyd6c397b2025-10-30 11:49:22 +0530931 uint8_t operation = 0;
Scron Chang2703b022021-07-06 15:47:45 +0800932 try
933 {
934 ipmi::sensor::GetSensorResponse getResponse =
935 sensor->second.getFunc(sensor->second);
936
937 if (getResponse.readingOrStateUnavailable)
938 {
939 operation |= static_cast<uint8_t>(
940 IPMISensorReadingByte2::readingStateUnavailable);
941 }
942 if (getResponse.scanningEnabled)
943 {
944 operation |= static_cast<uint8_t>(
945 IPMISensorReadingByte2::sensorScanningEnable);
946 }
947 if (getResponse.allEventMessagesEnabled)
948 {
949 operation |= static_cast<uint8_t>(
950 IPMISensorReadingByte2::eventMessagesEnable);
951 }
952 return ipmi::responseSuccess(
953 getResponse.reading, operation,
954 getResponse.thresholdLevelsStates,
955 getResponse.discreteReadingSensorStates);
956 }
957 catch (const std::exception& e)
958 {
959 operation |= static_cast<uint8_t>(
960 IPMISensorReadingByte2::readingStateUnavailable);
961 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
962 }
963 }
964#endif
965
Willy Tude54f482021-01-26 15:59:09 -0800966 DbusInterfaceMap sensorMap;
967 if (!getSensorMap(ctx, connection, path, sensorMap))
968 {
969 return ipmi::responseResponseError();
970 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100971 auto sensorObject = sensorMap.find(SensorValue::interface);
Willy Tude54f482021-01-26 15:59:09 -0800972
973 if (sensorObject == sensorMap.end() ||
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100974 sensorObject->second.find(SensorValue::property_names::value) ==
975 sensorObject->second.end())
Willy Tude54f482021-01-26 15:59:09 -0800976 {
977 return ipmi::responseResponseError();
978 }
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100979 auto& valueVariant =
980 sensorObject->second[SensorValue::property_names::value];
Willy Tude54f482021-01-26 15:59:09 -0800981 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
982
983 double max = 0;
984 double min = 0;
985 getSensorMaxMin(sensorMap, max, min);
986
987 int16_t mValue = 0;
988 int16_t bValue = 0;
989 int8_t rExp = 0;
990 int8_t bExp = 0;
991 bool bSigned = false;
992
993 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
994 {
995 return ipmi::responseResponseError();
996 }
997
Patrick Williams1318a5e2024-08-16 15:19:54 -0400998 uint8_t value =
999 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -08001000 uint8_t operation =
1001 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
1002 operation |=
1003 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
1004 bool notReading = std::isnan(reading);
1005
1006 if (!notReading)
1007 {
1008 auto availableObject =
1009 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1010 if (availableObject != sensorMap.end())
1011 {
1012 auto findAvailable = availableObject->second.find("Available");
1013 if (findAvailable != availableObject->second.end())
1014 {
1015 bool* available = std::get_if<bool>(&(findAvailable->second));
1016 if (available && !(*available))
1017 {
1018 notReading = true;
1019 }
1020 }
1021 }
1022 }
1023
1024 if (notReading)
1025 {
1026 operation |= static_cast<uint8_t>(
1027 IPMISensorReadingByte2::readingStateUnavailable);
1028 }
1029
Josh Lehana55c9532020-10-28 21:59:06 -07001030 if constexpr (details::enableInstrumentation)
1031 {
1032 int byteValue;
1033 if (bSigned)
1034 {
1035 byteValue = static_cast<int>(static_cast<int8_t>(value));
1036 }
1037 else
1038 {
1039 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1040 }
1041
1042 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001043 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1044 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001045 {
1046 // This is the first reading, show the coefficients
1047 double step = (max - min) / 255.0;
Haicheng Zhang041b3752025-07-14 17:13:45 +08001048 lg2::error(
1049 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, "
1050 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} "
1051 "bExp={BEXP} bSigned={BSIGNED}",
1052 "NAME",
1053 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum),
1054 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP",
1055 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned);
Josh Lehana55c9532020-10-28 21:59:06 -07001056 }
1057 }
1058
Willy Tude54f482021-01-26 15:59:09 -08001059 uint8_t thresholds = 0;
1060
Alexander Hansen653aec02025-11-14 13:38:08 +01001061 auto warningObject = sensorMap.find(SensorThresholdWarning::interface);
Willy Tude54f482021-01-26 15:59:09 -08001062 if (warningObject != sensorMap.end())
1063 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001064 auto alarmHigh = warningObject->second.find(
1065 SensorThresholdWarning::property_names::warning_alarm_high);
1066 auto alarmLow = warningObject->second.find(
1067 SensorThresholdWarning::property_names::warning_alarm_low);
Willy Tude54f482021-01-26 15:59:09 -08001068 if (alarmHigh != warningObject->second.end())
1069 {
1070 if (std::get<bool>(alarmHigh->second))
1071 {
1072 thresholds |= static_cast<uint8_t>(
1073 IPMISensorReadingByte3::upperNonCritical);
1074 }
1075 }
1076 if (alarmLow != warningObject->second.end())
1077 {
1078 if (std::get<bool>(alarmLow->second))
1079 {
1080 thresholds |= static_cast<uint8_t>(
1081 IPMISensorReadingByte3::lowerNonCritical);
1082 }
1083 }
1084 }
1085
Alexander Hansen653aec02025-11-14 13:38:08 +01001086 auto criticalObject = sensorMap.find(SensorThresholdCritical::interface);
Willy Tude54f482021-01-26 15:59:09 -08001087 if (criticalObject != sensorMap.end())
1088 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001089 auto alarmHigh = criticalObject->second.find(
1090 SensorThresholdCritical::property_names::critical_alarm_high);
1091 auto alarmLow = criticalObject->second.find(
1092 SensorThresholdCritical::property_names::critical_alarm_low);
Willy Tude54f482021-01-26 15:59:09 -08001093 if (alarmHigh != criticalObject->second.end())
1094 {
1095 if (std::get<bool>(alarmHigh->second))
1096 {
1097 thresholds |=
1098 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1099 }
1100 }
1101 if (alarmLow != criticalObject->second.end())
1102 {
1103 if (std::get<bool>(alarmLow->second))
1104 {
1105 thresholds |=
1106 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1107 }
1108 }
1109 }
1110
1111 // no discrete as of today so optional byte is never returned
1112 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1113}
1114
1115/** @brief implements the Set Sensor threshold command
1116 * @param sensorNumber - sensor number
1117 * @param lowerNonCriticalThreshMask
1118 * @param lowerCriticalThreshMask
1119 * @param lowerNonRecovThreshMask
1120 * @param upperNonCriticalThreshMask
1121 * @param upperCriticalThreshMask
1122 * @param upperNonRecovThreshMask
1123 * @param reserved
1124 * @param lowerNonCritical - lower non-critical threshold
1125 * @param lowerCritical - Lower critical threshold
1126 * @param lowerNonRecoverable - Lower non recovarable threshold
1127 * @param upperNonCritical - Upper non-critical threshold
1128 * @param upperCritical - Upper critical
1129 * @param upperNonRecoverable - Upper Non-recoverable
1130 *
1131 * @returns IPMI completion code
1132 */
1133ipmi::RspType<> ipmiSenSetSensorThresholds(
1134 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1135 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1136 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1137 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001138 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001139 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001140 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001141{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001142 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001143 {
1144 return ipmi::responseInvalidFieldRequest();
1145 }
1146
1147 // lower nc and upper nc not suppported on any sensor
1148 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1149 {
1150 return ipmi::responseInvalidFieldRequest();
1151 }
1152
1153 // if none of the threshold mask are set, nothing to do
1154 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1155 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1156 upperCriticalThreshMask | upperNonRecovThreshMask))
1157 {
1158 return ipmi::responseSuccess();
1159 }
1160
1161 std::string connection;
1162 std::string path;
1163
1164 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1165 if (status)
1166 {
1167 return ipmi::response(status);
1168 }
1169 DbusInterfaceMap sensorMap;
1170 if (!getSensorMap(ctx, connection, path, sensorMap))
1171 {
1172 return ipmi::responseResponseError();
1173 }
1174
1175 double max = 0;
1176 double min = 0;
1177 getSensorMaxMin(sensorMap, max, min);
1178
1179 int16_t mValue = 0;
1180 int16_t bValue = 0;
1181 int8_t rExp = 0;
1182 int8_t bExp = 0;
1183 bool bSigned = false;
1184
1185 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1186 {
1187 return ipmi::responseResponseError();
1188 }
1189
1190 // store a vector of property name, value to set, and interface
1191 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1192
1193 // define the indexes of the tuple
1194 constexpr uint8_t propertyName = 0;
1195 constexpr uint8_t thresholdValue = 1;
1196 constexpr uint8_t interface = 2;
1197 // verifiy all needed fields are present
1198 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1199 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001200 auto findThreshold = sensorMap.find(SensorThresholdCritical::interface);
Willy Tude54f482021-01-26 15:59:09 -08001201 if (findThreshold == sensorMap.end())
1202 {
1203 return ipmi::responseInvalidFieldRequest();
1204 }
1205 if (lowerCriticalThreshMask)
1206 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001207 auto findLower = findThreshold->second.find(
1208 SensorThresholdCritical::property_names::critical_low);
Willy Tude54f482021-01-26 15:59:09 -08001209 if (findLower == findThreshold->second.end())
1210 {
1211 return ipmi::responseInvalidFieldRequest();
1212 }
Alexander Hansen653aec02025-11-14 13:38:08 +01001213 thresholdsToSet.emplace_back(
1214 SensorThresholdCritical::property_names::critical_low,
1215 lowerCritical, findThreshold->first);
Willy Tude54f482021-01-26 15:59:09 -08001216 }
1217 if (upperCriticalThreshMask)
1218 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001219 auto findUpper = findThreshold->second.find(
1220 SensorThresholdCritical::property_names::critical_high);
Willy Tude54f482021-01-26 15:59:09 -08001221 if (findUpper == findThreshold->second.end())
1222 {
1223 return ipmi::responseInvalidFieldRequest();
1224 }
Alexander Hansen653aec02025-11-14 13:38:08 +01001225 thresholdsToSet.emplace_back(
1226 SensorThresholdCritical::property_names::critical_high,
1227 upperCritical, findThreshold->first);
Willy Tude54f482021-01-26 15:59:09 -08001228 }
1229 }
1230 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1231 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001232 auto findThreshold = sensorMap.find(SensorThresholdWarning::interface);
Willy Tude54f482021-01-26 15:59:09 -08001233 if (findThreshold == sensorMap.end())
1234 {
1235 return ipmi::responseInvalidFieldRequest();
1236 }
1237 if (lowerNonCriticalThreshMask)
1238 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001239 auto findLower = findThreshold->second.find(
1240 SensorThresholdWarning::property_names::warning_low);
Willy Tude54f482021-01-26 15:59:09 -08001241 if (findLower == findThreshold->second.end())
1242 {
1243 return ipmi::responseInvalidFieldRequest();
1244 }
Alexander Hansen653aec02025-11-14 13:38:08 +01001245 thresholdsToSet.emplace_back(
1246 SensorThresholdWarning::property_names::warning_low,
1247 lowerNonCritical, findThreshold->first);
Willy Tude54f482021-01-26 15:59:09 -08001248 }
1249 if (upperNonCriticalThreshMask)
1250 {
Alexander Hansen653aec02025-11-14 13:38:08 +01001251 auto findUpper = findThreshold->second.find(
1252 SensorThresholdWarning::property_names::warning_high);
Willy Tude54f482021-01-26 15:59:09 -08001253 if (findUpper == findThreshold->second.end())
1254 {
1255 return ipmi::responseInvalidFieldRequest();
1256 }
Alexander Hansen653aec02025-11-14 13:38:08 +01001257 thresholdsToSet.emplace_back(
1258 SensorThresholdWarning::property_names::warning_high,
1259 upperNonCritical, findThreshold->first);
Willy Tude54f482021-01-26 15:59:09 -08001260 }
1261 }
1262 for (const auto& property : thresholdsToSet)
1263 {
1264 // from section 36.3 in the IPMI Spec, assume all linear
1265 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1266 (bValue * std::pow(10.0, bExp))) *
1267 std::pow(10.0, rExp);
1268 setDbusProperty(
1269 *getSdBus(), connection, path, std::get<interface>(property),
1270 std::get<propertyName>(property), ipmi::Value(valueToSet));
1271 }
1272 return ipmi::responseSuccess();
1273}
1274
1275IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1276{
1277 IPMIThresholds resp;
Alexander Hansen653aec02025-11-14 13:38:08 +01001278 auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1279 auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
Willy Tude54f482021-01-26 15:59:09 -08001280
1281 if ((warningInterface != sensorMap.end()) ||
1282 (criticalInterface != sensorMap.end()))
1283 {
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001284 auto sensorPair = sensorMap.find(SensorValue::interface);
Willy Tude54f482021-01-26 15:59:09 -08001285
1286 if (sensorPair == sensorMap.end())
1287 {
1288 // should not have been able to find a sensor not implementing
1289 // the sensor object
1290 throw std::runtime_error("Invalid sensor map");
1291 }
1292
1293 double max = 0;
1294 double min = 0;
1295 getSensorMaxMin(sensorMap, max, min);
1296
1297 int16_t mValue = 0;
1298 int16_t bValue = 0;
1299 int8_t rExp = 0;
1300 int8_t bExp = 0;
1301 bool bSigned = false;
1302
1303 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1304 {
1305 throw std::runtime_error("Invalid sensor atrributes");
1306 }
1307 if (warningInterface != sensorMap.end())
1308 {
1309 auto& warningMap = warningInterface->second;
1310
Alexander Hansen653aec02025-11-14 13:38:08 +01001311 auto warningHigh = warningMap.find(
1312 SensorThresholdWarning::property_names::warning_high);
1313 auto warningLow = warningMap.find(
1314 SensorThresholdWarning::property_names::warning_low);
Willy Tude54f482021-01-26 15:59:09 -08001315
1316 if (warningHigh != warningMap.end())
1317 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001318 double value =
1319 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001320 if (std::isfinite(value))
1321 {
1322 resp.warningHigh = scaleIPMIValueFromDouble(
1323 value, mValue, rExp, bValue, bExp, bSigned);
1324 }
Willy Tude54f482021-01-26 15:59:09 -08001325 }
1326 if (warningLow != warningMap.end())
1327 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001328 double value =
1329 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001330 if (std::isfinite(value))
1331 {
1332 resp.warningLow = scaleIPMIValueFromDouble(
1333 value, mValue, rExp, bValue, bExp, bSigned);
1334 }
Willy Tude54f482021-01-26 15:59:09 -08001335 }
1336 }
1337 if (criticalInterface != sensorMap.end())
1338 {
1339 auto& criticalMap = criticalInterface->second;
1340
Alexander Hansen653aec02025-11-14 13:38:08 +01001341 auto criticalHigh = criticalMap.find(
1342 SensorThresholdCritical::property_names::critical_high);
1343 auto criticalLow = criticalMap.find(
1344 SensorThresholdCritical::property_names::critical_low);
Willy Tude54f482021-01-26 15:59:09 -08001345
1346 if (criticalHigh != criticalMap.end())
1347 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001348 double value =
1349 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001350 if (std::isfinite(value))
1351 {
1352 resp.criticalHigh = scaleIPMIValueFromDouble(
1353 value, mValue, rExp, bValue, bExp, bSigned);
1354 }
Willy Tude54f482021-01-26 15:59:09 -08001355 }
1356 if (criticalLow != criticalMap.end())
1357 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001358 double value =
1359 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001360 if (std::isfinite(value))
1361 {
1362 resp.criticalLow = scaleIPMIValueFromDouble(
1363 value, mValue, rExp, bValue, bExp, bSigned);
1364 }
Willy Tude54f482021-01-26 15:59:09 -08001365 }
1366 }
1367 }
1368 return resp;
1369}
1370
1371ipmi::RspType<uint8_t, // readable
1372 uint8_t, // lowerNCrit
1373 uint8_t, // lowerCrit
1374 uint8_t, // lowerNrecoverable
1375 uint8_t, // upperNC
1376 uint8_t, // upperCrit
1377 uint8_t> // upperNRecoverable
1378 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1379{
1380 std::string connection;
1381 std::string path;
1382
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001383 if (sensorNumber == reservedSensorNumber)
1384 {
1385 return ipmi::responseInvalidFieldRequest();
1386 }
1387
Willy Tude54f482021-01-26 15:59:09 -08001388 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1389 if (status)
1390 {
1391 return ipmi::response(status);
1392 }
1393
1394 DbusInterfaceMap sensorMap;
1395 if (!getSensorMap(ctx, connection, path, sensorMap))
1396 {
1397 return ipmi::responseResponseError();
1398 }
1399
1400 IPMIThresholds thresholdData;
1401 try
1402 {
1403 thresholdData = getIPMIThresholds(sensorMap);
1404 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001405 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001406 {
1407 return ipmi::responseResponseError();
1408 }
1409
1410 uint8_t readable = 0;
1411 uint8_t lowerNC = 0;
1412 uint8_t lowerCritical = 0;
1413 uint8_t lowerNonRecoverable = 0;
1414 uint8_t upperNC = 0;
1415 uint8_t upperCritical = 0;
1416 uint8_t upperNonRecoverable = 0;
1417
1418 if (thresholdData.warningHigh)
1419 {
1420 readable |=
1421 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1422 upperNC = *thresholdData.warningHigh;
1423 }
1424 if (thresholdData.warningLow)
1425 {
1426 readable |=
1427 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1428 lowerNC = *thresholdData.warningLow;
1429 }
1430
1431 if (thresholdData.criticalHigh)
1432 {
1433 readable |=
1434 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1435 upperCritical = *thresholdData.criticalHigh;
1436 }
1437 if (thresholdData.criticalLow)
1438 {
1439 readable |=
1440 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1441 lowerCritical = *thresholdData.criticalLow;
1442 }
1443
1444 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1445 lowerNonRecoverable, upperNC, upperCritical,
1446 upperNonRecoverable);
1447}
1448
1449/** @brief implements the get Sensor event enable command
1450 * @param sensorNumber - sensor number
1451 *
1452 * @returns IPMI completion code plus response data
1453 * - enabled - Sensor Event messages
1454 * - assertionEnabledLsb - Assertion event messages
1455 * - assertionEnabledMsb - Assertion event messages
1456 * - deassertionEnabledLsb - Deassertion event messages
1457 * - deassertionEnabledMsb - Deassertion event messages
1458 */
1459
1460ipmi::RspType<uint8_t, // enabled
1461 uint8_t, // assertionEnabledLsb
1462 uint8_t, // assertionEnabledMsb
1463 uint8_t, // deassertionEnabledLsb
1464 uint8_t> // deassertionEnabledMsb
1465 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1466{
1467 std::string connection;
1468 std::string path;
1469
1470 uint8_t enabled = 0;
1471 uint8_t assertionEnabledLsb = 0;
1472 uint8_t assertionEnabledMsb = 0;
1473 uint8_t deassertionEnabledLsb = 0;
1474 uint8_t deassertionEnabledMsb = 0;
1475
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001476 if (sensorNum == reservedSensorNumber)
1477 {
1478 return ipmi::responseInvalidFieldRequest();
1479 }
1480
Willy Tude54f482021-01-26 15:59:09 -08001481 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1482 if (status)
1483 {
1484 return ipmi::response(status);
1485 }
1486
Scron Chang2703b022021-07-06 15:47:45 +08001487#ifdef FEATURE_HYBRID_SENSORS
1488 if (auto sensor = findStaticSensor(path);
1489 sensor != ipmi::sensor::sensors.end() &&
1490 getSensorEventTypeFromPath(path) !=
1491 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1492 {
1493 enabled = static_cast<uint8_t>(
1494 IPMISensorEventEnableByte2::sensorScanningEnable);
1495 uint16_t assertionEnabled = 0;
1496 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1497 ->second.begin()
1498 ->second.second)
1499 {
1500 assertionEnabled |= (1 << offsetValMap.first);
1501 }
1502 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1503 assertionEnabledMsb =
1504 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1505
1506 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1507 assertionEnabledMsb, deassertionEnabledLsb,
1508 deassertionEnabledMsb);
1509 }
1510#endif
1511
Willy Tude54f482021-01-26 15:59:09 -08001512 DbusInterfaceMap sensorMap;
1513 if (!getSensorMap(ctx, connection, path, sensorMap))
1514 {
1515 return ipmi::responseResponseError();
1516 }
1517
Alexander Hansen653aec02025-11-14 13:38:08 +01001518 auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1519 auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
Willy Tude54f482021-01-26 15:59:09 -08001520 if ((warningInterface != sensorMap.end()) ||
1521 (criticalInterface != sensorMap.end()))
1522 {
1523 enabled = static_cast<uint8_t>(
1524 IPMISensorEventEnableByte2::sensorScanningEnable);
1525 if (warningInterface != sensorMap.end())
1526 {
1527 auto& warningMap = warningInterface->second;
1528
Alexander Hansen653aec02025-11-14 13:38:08 +01001529 auto warningHigh = warningMap.find(
1530 SensorThresholdWarning::property_names::warning_high);
1531 auto warningLow = warningMap.find(
1532 SensorThresholdWarning::property_names::warning_low);
Willy Tude54f482021-01-26 15:59:09 -08001533 if (warningHigh != warningMap.end())
1534 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001535 double value =
1536 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001537 if (std::isfinite(value))
1538 {
1539 assertionEnabledLsb |= static_cast<uint8_t>(
1540 IPMISensorEventEnableThresholds::
1541 upperNonCriticalGoingHigh);
1542 deassertionEnabledLsb |= static_cast<uint8_t>(
1543 IPMISensorEventEnableThresholds::
1544 upperNonCriticalGoingLow);
1545 }
Willy Tude54f482021-01-26 15:59:09 -08001546 }
1547 if (warningLow != warningMap.end())
1548 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001549 double value =
1550 std::visit(VariantToDoubleVisitor(), warningLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001551 if (std::isfinite(value))
1552 {
1553 assertionEnabledLsb |= static_cast<uint8_t>(
1554 IPMISensorEventEnableThresholds::
1555 lowerNonCriticalGoingLow);
1556 deassertionEnabledLsb |= static_cast<uint8_t>(
1557 IPMISensorEventEnableThresholds::
1558 lowerNonCriticalGoingHigh);
1559 }
Willy Tude54f482021-01-26 15:59:09 -08001560 }
1561 }
1562 if (criticalInterface != sensorMap.end())
1563 {
1564 auto& criticalMap = criticalInterface->second;
1565
Alexander Hansen653aec02025-11-14 13:38:08 +01001566 auto criticalHigh = criticalMap.find(
1567 SensorThresholdCritical::property_names::critical_high);
1568 auto criticalLow = criticalMap.find(
1569 SensorThresholdCritical::property_names::critical_low);
Willy Tude54f482021-01-26 15:59:09 -08001570
1571 if (criticalHigh != criticalMap.end())
1572 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001573 double value =
1574 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001575 if (std::isfinite(value))
1576 {
1577 assertionEnabledMsb |= static_cast<uint8_t>(
1578 IPMISensorEventEnableThresholds::
1579 upperCriticalGoingHigh);
1580 deassertionEnabledMsb |= static_cast<uint8_t>(
1581 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1582 }
Willy Tude54f482021-01-26 15:59:09 -08001583 }
1584 if (criticalLow != criticalMap.end())
1585 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001586 double value =
1587 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Johnathan Mantey92217f02024-06-20 12:43:01 -07001588 if (std::isfinite(value))
1589 {
1590 assertionEnabledLsb |= static_cast<uint8_t>(
1591 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1592 deassertionEnabledLsb |= static_cast<uint8_t>(
1593 IPMISensorEventEnableThresholds::
1594 lowerCriticalGoingHigh);
1595 }
Willy Tude54f482021-01-26 15:59:09 -08001596 }
1597 }
1598 }
1599
1600 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1601 assertionEnabledMsb, deassertionEnabledLsb,
1602 deassertionEnabledMsb);
1603}
1604
1605/** @brief implements the get Sensor event status command
1606 * @param sensorNumber - sensor number, FFh = reserved
1607 *
1608 * @returns IPMI completion code plus response data
1609 * - sensorEventStatus - Sensor Event messages state
1610 * - assertions - Assertion event messages
1611 * - deassertions - Deassertion event messages
1612 */
1613ipmi::RspType<uint8_t, // sensorEventStatus
1614 std::bitset<16>, // assertions
1615 std::bitset<16> // deassertion
1616 >
1617 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1618{
1619 if (sensorNum == reservedSensorNumber)
1620 {
1621 return ipmi::responseInvalidFieldRequest();
1622 }
1623
1624 std::string connection;
1625 std::string path;
1626 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1627 if (status)
1628 {
George Liude6694e2024-07-17 15:22:25 +08001629 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1630 "sensor number: {SENSOR_NUM}",
1631 "SENSOR_NUM", sensorNum);
Willy Tude54f482021-01-26 15:59:09 -08001632 return ipmi::response(status);
1633 }
1634
Scron Chang2703b022021-07-06 15:47:45 +08001635#ifdef FEATURE_HYBRID_SENSORS
1636 if (auto sensor = findStaticSensor(path);
1637 sensor != ipmi::sensor::sensors.end() &&
1638 getSensorEventTypeFromPath(path) !=
1639 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1640 {
1641 auto response = ipmi::sensor::get::mapDbusToAssertion(
1642 sensor->second, path, sensor->second.sensorInterface);
1643 std::bitset<16> assertions;
1644 // deassertions are not used.
1645 std::bitset<16> deassertions = 0;
Hariharan Rangasamyd6c397b2025-10-30 11:49:22 +05301646 uint8_t sensorEventStatus = 0;
Scron Chang2703b022021-07-06 15:47:45 +08001647 if (response.readingOrStateUnavailable)
1648 {
1649 sensorEventStatus |= static_cast<uint8_t>(
1650 IPMISensorReadingByte2::readingStateUnavailable);
1651 }
1652 if (response.scanningEnabled)
1653 {
1654 sensorEventStatus |= static_cast<uint8_t>(
1655 IPMISensorReadingByte2::sensorScanningEnable);
1656 }
1657 if (response.allEventMessagesEnabled)
1658 {
1659 sensorEventStatus |= static_cast<uint8_t>(
1660 IPMISensorReadingByte2::eventMessagesEnable);
1661 }
1662 assertions |= response.discreteReadingSensorStates << 8;
1663 assertions |= response.thresholdLevelsStates;
1664 return ipmi::responseSuccess(sensorEventStatus, assertions,
1665 deassertions);
1666 }
1667#endif
1668
Willy Tude54f482021-01-26 15:59:09 -08001669 DbusInterfaceMap sensorMap;
1670 if (!getSensorMap(ctx, connection, path, sensorMap))
1671 {
George Liude6694e2024-07-17 15:22:25 +08001672 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1673 "sensor path: {SENSOR_PATH}",
1674 "SENSOR_PATH", path);
Willy Tude54f482021-01-26 15:59:09 -08001675 return ipmi::responseResponseError();
1676 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001677
1678 uint8_t sensorEventStatus =
1679 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1680 std::bitset<16> assertions = 0;
1681 std::bitset<16> deassertions = 0;
1682
1683 // handle VR typed sensor
1684 auto vrInterface = sensorMap.find(sensor::vrInterface);
1685 if (vrInterface != sensorMap.end())
1686 {
1687 if (!sensor::getVrEventStatus(ctx, connection, path,
1688 vrInterface->second, assertions))
1689 {
1690 return ipmi::responseResponseError();
1691 }
1692
1693 // both Event Message and Sensor Scanning are disable for VR.
1694 sensorEventStatus = 0;
1695 return ipmi::responseSuccess(sensorEventStatus, assertions,
1696 deassertions);
1697 }
1698
Alexander Hansen653aec02025-11-14 13:38:08 +01001699 auto warningInterface = sensorMap.find(SensorThresholdWarning::interface);
1700 auto criticalInterface = sensorMap.find(SensorThresholdCritical::interface);
Willy Tude54f482021-01-26 15:59:09 -08001701
Alexander Hansen653aec02025-11-14 13:38:08 +01001702 std::optional<bool> criticalDeassertHigh = thresholdDeassertMap
1703 [path][SensorThresholdCritical::property_names::critical_alarm_high];
1704 std::optional<bool> criticalDeassertLow = thresholdDeassertMap
1705 [path][SensorThresholdCritical::property_names::critical_alarm_low];
1706 std::optional<bool> warningDeassertHigh = thresholdDeassertMap
1707 [path][SensorThresholdWarning::property_names::warning_alarm_high];
1708 std::optional<bool> warningDeassertLow = thresholdDeassertMap
1709 [path][SensorThresholdWarning::property_names::warning_alarm_low];
Willy Tude54f482021-01-26 15:59:09 -08001710
Willy Tude54f482021-01-26 15:59:09 -08001711 if (criticalDeassertHigh && !*criticalDeassertHigh)
1712 {
1713 deassertions.set(static_cast<size_t>(
1714 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1715 }
1716 if (criticalDeassertLow && !*criticalDeassertLow)
1717 {
1718 deassertions.set(static_cast<size_t>(
1719 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1720 }
1721 if (warningDeassertHigh && !*warningDeassertHigh)
1722 {
1723 deassertions.set(static_cast<size_t>(
1724 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1725 }
1726 if (warningDeassertLow && !*warningDeassertLow)
1727 {
1728 deassertions.set(static_cast<size_t>(
1729 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1730 }
1731 if ((warningInterface != sensorMap.end()) ||
1732 (criticalInterface != sensorMap.end()))
1733 {
1734 sensorEventStatus = static_cast<size_t>(
1735 IPMISensorEventEnableByte2::eventMessagesEnable);
1736 if (warningInterface != sensorMap.end())
1737 {
1738 auto& warningMap = warningInterface->second;
1739
Alexander Hansen653aec02025-11-14 13:38:08 +01001740 auto warningHigh = warningMap.find(
1741 SensorThresholdWarning::property_names::warning_alarm_high);
1742 auto warningLow = warningMap.find(
1743 SensorThresholdWarning::property_names::warning_alarm_low);
Willy Tude54f482021-01-26 15:59:09 -08001744 auto warningHighAlarm = false;
1745 auto warningLowAlarm = false;
1746
1747 if (warningHigh != warningMap.end())
1748 {
1749 warningHighAlarm = std::get<bool>(warningHigh->second);
1750 }
1751 if (warningLow != warningMap.end())
1752 {
1753 warningLowAlarm = std::get<bool>(warningLow->second);
1754 }
1755 if (warningHighAlarm)
1756 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001757 assertions.set(static_cast<size_t>(
1758 IPMIGetSensorEventEnableThresholds::
1759 upperNonCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001760 }
1761 if (warningLowAlarm)
1762 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001763 assertions.set(static_cast<size_t>(
1764 IPMIGetSensorEventEnableThresholds::
1765 lowerNonCriticalGoingLow));
Willy Tude54f482021-01-26 15:59:09 -08001766 }
1767 }
1768 if (criticalInterface != sensorMap.end())
1769 {
1770 auto& criticalMap = criticalInterface->second;
1771
Alexander Hansen653aec02025-11-14 13:38:08 +01001772 auto criticalHigh = criticalMap.find(
1773 SensorThresholdCritical::property_names::critical_alarm_high);
1774 auto criticalLow = criticalMap.find(
1775 SensorThresholdCritical::property_names::critical_alarm_low);
Willy Tude54f482021-01-26 15:59:09 -08001776 auto criticalHighAlarm = false;
1777 auto criticalLowAlarm = false;
1778
1779 if (criticalHigh != criticalMap.end())
1780 {
1781 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1782 }
1783 if (criticalLow != criticalMap.end())
1784 {
1785 criticalLowAlarm = std::get<bool>(criticalLow->second);
1786 }
1787 if (criticalHighAlarm)
1788 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001789 assertions.set(static_cast<size_t>(
1790 IPMIGetSensorEventEnableThresholds::
1791 upperCriticalGoingHigh));
Willy Tude54f482021-01-26 15:59:09 -08001792 }
1793 if (criticalLowAlarm)
1794 {
1795 assertions.set(static_cast<size_t>(
1796 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1797 }
1798 }
1799 }
1800
1801 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1802}
1803
Willy Tu38e7a2b2021-03-29 15:09:56 -07001804// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001805void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1806 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001807{
Willy Tu38e7a2b2021-03-29 15:09:56 -07001808 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1809 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1810
George Liuc7a4da52025-08-19 16:20:31 +08001811 record.header.recordId = recordID;
George Liu42247d22025-08-19 10:24:47 +08001812 record.header.sdrVersion = ipmiSdrVersion;
1813 record.header.recordType = get_sdr::SENSOR_DATA_FULL_RECORD;
1814 record.header.recordLength = sizeof(get_sdr::SensorDataFullRecord) -
1815 sizeof(get_sdr::SensorDataRecordHeader);
1816 record.key.ownerId = bmcI2CAddr;
1817 record.key.ownerLun = lun;
1818 record.key.sensorNumber = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001819}
Willy Tu4eca2512022-06-20 21:14:51 -07001820bool constructSensorSdr(
1821 ipmi::Context::ptr ctx,
1822 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1823 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1824 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001825{
Hao Jiange39d4d82021-04-16 17:02:40 -07001826 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1827
1828 DbusInterfaceMap sensorMap;
1829 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1830 {
George Liude6694e2024-07-17 15:22:25 +08001831 lg2::error("Failed to update sensor map for threshold sensor, "
1832 "service: {SERVICE}, path: {PATH}",
1833 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07001834 return false;
1835 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001836
George Liu42247d22025-08-19 10:24:47 +08001837 record.body.sensorCapabilities = 0x68; // auto rearm - todo hysteresis
1838 record.body.sensorType = getSensorTypeFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001839 std::string type = getSensorTypeStringFromPath(path);
1840 auto typeCstr = type.c_str();
1841 auto findUnits = sensorUnits.find(typeCstr);
1842 if (findUnits != sensorUnits.end())
1843 {
George Liu42247d22025-08-19 10:24:47 +08001844 record.body.sensorUnits2Base = static_cast<uint8_t>(findUnits->second);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001845 } // else default 0x0 unspecified
1846
George Liu42247d22025-08-19 10:24:47 +08001847 record.body.eventReadingType = getSensorEventTypeFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001848
Alexander Hansenf365c7f2025-11-14 12:08:34 +01001849 auto sensorObject = sensorMap.find(SensorValue::interface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001850 if (sensorObject == sensorMap.end())
1851 {
George Liude6694e2024-07-17 15:22:25 +08001852 lg2::error("constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001853 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001854 }
1855
1856 uint8_t entityId = 0;
1857 uint8_t entityInstance = 0x01;
1858
1859 // follow the association chain to get the parent board's entityid and
1860 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001861 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1862 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001863
George Liu42247d22025-08-19 10:24:47 +08001864 record.body.entityId = entityId;
1865 record.body.entityInstance = entityInstance;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001866
Shakeeb Pasha93889722021-10-14 10:20:13 +05301867 double max = 0;
1868 double min = 0;
1869 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001870
1871 int16_t mValue = 0;
1872 int8_t rExp = 0;
1873 int16_t bValue = 0;
1874 int8_t bExp = 0;
1875 bool bSigned = false;
1876
1877 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1878 {
George Liude6694e2024-07-17 15:22:25 +08001879 lg2::error("constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001880 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001881 }
1882
1883 // The record.body is a struct SensorDataFullRecordBody
1884 // from sensorhandler.hpp in phosphor-ipmi-host.
1885 // The meaning of these bits appears to come from
1886 // table 43.1 of the IPMI spec.
1887 // The above 5 sensor attributes are stuffed in as follows:
1888 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1889 // Byte 22-24 are for other purposes
1890 // Byte 25 = MMMMMMMM = LSB of M
1891 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1892 // Byte 27 = BBBBBBBB = LSB of B
1893 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1894 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1895 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1896
1897 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
George Liu42247d22025-08-19 10:24:47 +08001898 record.body.mLsb = mValue & 0xFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001899
1900 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1901 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1902
1903 // move the smallest bit of the MSB into place (bit 9)
George Liu42247d22025-08-19 10:24:47 +08001904 // the MSbs are bits 7:8 in mMsbAndToLerance
1905 record.body.mMsbAndTolerance = (mBitSign << 7) | (mBitNine << 6);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001906
George Liu42247d22025-08-19 10:24:47 +08001907 record.body.bLsb = bValue & 0xFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001908
1909 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1910 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1911
1912 // move the smallest bit of the MSB into place (bit 9)
George Liu42247d22025-08-19 10:24:47 +08001913 // the MSbs are bits 7:8 in bMsbAndAccuracyLsb
1914 record.body.bMsbAndAccuracyLsb = (bBitSign << 7) | (bBitNine << 6);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001915
1916 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1917 uint8_t rExpBits = rExp & 0x07;
1918
1919 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1920 uint8_t bExpBits = bExp & 0x07;
1921
1922 // move rExp and bExp into place
George Liu42247d22025-08-19 10:24:47 +08001923 record.body.rbExponents =
Patrick Williams1318a5e2024-08-16 15:19:54 -04001924 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001925
1926 // Set the analog reading byte interpretation accordingly
George Liu42247d22025-08-19 10:24:47 +08001927 record.body.sensorUnits1 = (bSigned ? 1 : 0) << 7;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001928
1929 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1930 // These seem redundant, but derivable from the above 5 attributes
1931 // Original comment said "todo fill out rest of units"
1932
1933 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001934 auto name = sensor::parseSdrIdFromPath(path);
George Liuf60be692025-08-19 11:28:42 +08001935 get_sdr::body::setIdStrLen(name.size(), record.body);
1936 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08001937 std::memcpy(record.body.idString, name.c_str(),
1938 std::min(name.length() + 1, sizeof(record.body.idString)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001939
Josh Lehana55c9532020-10-28 21:59:06 -07001940 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001941 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001942
Jie Yangf0a89942021-07-29 15:30:25 -07001943 bool sensorSettable = false;
1944 auto mutability =
1945 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1946 if (mutability != sensorMap.end())
1947 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001948 sensorSettable =
1949 mappedVariant<bool>(mutability->second, "Mutable", false);
Jie Yangf0a89942021-07-29 15:30:25 -07001950 }
George Liuf60be692025-08-19 11:28:42 +08001951 get_sdr::body::initSettableState(sensorSettable, record.body);
Jie Yangf0a89942021-07-29 15:30:25 -07001952
1953 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001954 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001955
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001956 IPMIThresholds thresholdData;
1957 try
1958 {
1959 thresholdData = getIPMIThresholds(sensorMap);
1960 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001961 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001962 {
George Liude6694e2024-07-17 15:22:25 +08001963 lg2::error("constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001964 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001965 }
1966
1967 if (thresholdData.criticalHigh)
1968 {
George Liu42247d22025-08-19 10:24:47 +08001969 record.body.upperCriticalThreshold = *thresholdData.criticalHigh;
1970 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001971 IPMISensorEventEnableThresholds::criticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001972 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001973 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001974 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001975 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001976 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001977 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1978 }
1979 if (thresholdData.warningHigh)
1980 {
George Liu42247d22025-08-19 10:24:47 +08001981 record.body.upperNoncriticalThreshold = *thresholdData.warningHigh;
1982 record.body.supportedDeassertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001983 IPMISensorEventEnableThresholds::nonCriticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001984 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001985 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001986 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001987 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
George Liu42247d22025-08-19 10:24:47 +08001988 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001989 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1990 }
1991 if (thresholdData.criticalLow)
1992 {
George Liu42247d22025-08-19 10:24:47 +08001993 record.body.lowerCriticalThreshold = *thresholdData.criticalLow;
1994 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001995 IPMISensorEventEnableThresholds::criticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08001996 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001997 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08001998 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001999 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08002000 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002001 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
2002 }
2003 if (thresholdData.warningLow)
2004 {
George Liu42247d22025-08-19 10:24:47 +08002005 record.body.lowerNoncriticalThreshold = *thresholdData.warningLow;
2006 record.body.supportedAssertions[1] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002007 IPMISensorEventEnableThresholds::nonCriticalThreshold);
George Liu42247d22025-08-19 10:24:47 +08002008 record.body.supportedDeassertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002009 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08002010 record.body.supportedAssertions[0] |= static_cast<uint8_t>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002011 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
George Liu42247d22025-08-19 10:24:47 +08002012 record.body.discreteReadingSettingMask[0] |=
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002013 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
2014 }
2015
2016 // everything that is readable is setable
George Liu42247d22025-08-19 10:24:47 +08002017 record.body.discreteReadingSettingMask[1] =
2018 record.body.discreteReadingSettingMask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07002019 return true;
2020}
2021
Scron Chang2703b022021-07-06 15:47:45 +08002022#ifdef FEATURE_HYBRID_SENSORS
2023// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08002024void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08002025 uint16_t recordID,
2026 ipmi::sensor::IdInfoMap::const_iterator sensor,
2027 get_sdr::SensorDataFullRecord& record)
2028{
2029 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2030
George Liu42247d22025-08-19 10:24:47 +08002031 record.body.entityId = sensor->second.entityType;
2032 record.body.sensorType = sensor->second.sensorType;
2033 record.body.eventReadingType = sensor->second.sensorReadingType;
2034 record.body.entityInstance = sensor->second.instance;
Scron Chang2703b022021-07-06 15:47:45 +08002035 if (ipmi::sensor::Mutability::Write ==
2036 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2037 {
George Liuf60be692025-08-19 11:28:42 +08002038 get_sdr::body::initSettableState(true, record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002039 }
2040
George Liu42247d22025-08-19 10:24:47 +08002041 auto idString = sensor->second.sensorName;
Scron Chang2703b022021-07-06 15:47:45 +08002042
George Liu42247d22025-08-19 10:24:47 +08002043 if (idString.empty())
Scron Chang2703b022021-07-06 15:47:45 +08002044 {
George Liu42247d22025-08-19 10:24:47 +08002045 idString = sensor->second.sensorNameFunc(sensor->second);
Scron Chang2703b022021-07-06 15:47:45 +08002046 }
2047
George Liu42247d22025-08-19 10:24:47 +08002048 if (idString.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
Scron Chang2703b022021-07-06 15:47:45 +08002049 {
George Liuf60be692025-08-19 11:28:42 +08002050 get_sdr::body::setIdStrLen(FULL_RECORD_ID_STR_MAX_LENGTH, record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002051 }
2052 else
2053 {
George Liuf60be692025-08-19 11:28:42 +08002054 get_sdr::body::setIdStrLen(idString.length(), record.body);
Scron Chang2703b022021-07-06 15:47:45 +08002055 }
George Liuf60be692025-08-19 11:28:42 +08002056 get_sdr::body::setIdType(3, &record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08002057 std::strncpy(record.body.idString, idString.c_str(),
George Liuf60be692025-08-19 11:28:42 +08002058 get_sdr::body::setIdStrLen(record.body));
Scron Chang2703b022021-07-06 15:47:45 +08002059}
2060#endif
2061
Hao Jiange39d4d82021-04-16 17:02:40 -07002062// Construct type 3 SDR header and key (for VR and other discrete sensors)
2063void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2064 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002065{
2066 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2067 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2068
George Liuc7a4da52025-08-19 16:20:31 +08002069 record.header.recordId = recordID;
George Liu42247d22025-08-19 10:24:47 +08002070 record.header.sdrVersion = ipmiSdrVersion;
2071 record.header.recordType = get_sdr::SENSOR_DATA_EVENT_RECORD;
2072 record.header.recordLength = sizeof(get_sdr::SensorDataEventRecord) -
2073 sizeof(get_sdr::SensorDataRecordHeader);
2074 record.key.ownerId = bmcI2CAddr;
2075 record.key.ownerLun = lun;
2076 record.key.sensorNumber = sensornumber;
Willy Tu61992ad2021-03-29 15:33:20 -07002077
George Liu42247d22025-08-19 10:24:47 +08002078 record.body.entityId = 0x00;
2079 record.body.entityInstance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002080}
Willy Tu61992ad2021-03-29 15:33:20 -07002081
Hao Jiange39d4d82021-04-16 17:02:40 -07002082// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002083bool constructVrSdr(ipmi::Context::ptr ctx,
2084 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2085 uint16_t sensorNum, uint16_t recordID,
2086 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002087 get_sdr::SensorDataEventRecord& record)
2088{
Hao Jiange39d4d82021-04-16 17:02:40 -07002089 constructEventSdrHeaderKey(sensorNum, recordID, record);
2090
2091 DbusInterfaceMap sensorMap;
2092 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2093 {
George Liude6694e2024-07-17 15:22:25 +08002094 lg2::error("Failed to update sensor map for VR sensor, "
2095 "service: {SERVICE}, path: {PATH}",
2096 "SERVICE", service, "PATH", path);
Hao Jiange39d4d82021-04-16 17:02:40 -07002097 return false;
2098 }
Willy Tu61992ad2021-03-29 15:33:20 -07002099 // follow the association chain to get the parent board's entityid and
2100 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002101 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
George Liu42247d22025-08-19 10:24:47 +08002102 record.body.entityId, record.body.entityInstance);
Willy Tu61992ad2021-03-29 15:33:20 -07002103
2104 // Sensor type is hardcoded as a module/board type instead of parsing from
2105 // sensor path. This is because VR control is allocated in an independent
2106 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2107 // types.
George Liuf2807ed2025-04-03 15:52:57 +08002108 static constexpr const uint8_t moduleBoardType = 0x15;
George Liu42247d22025-08-19 10:24:47 +08002109 record.body.sensorType = moduleBoardType;
2110 record.body.eventReadingType = 0x00;
Willy Tu61992ad2021-03-29 15:33:20 -07002111
George Liu42247d22025-08-19 10:24:47 +08002112 record.body.sensorRecordSharing1 = 0x00;
2113 record.body.sensorRecordSharing2 = 0x00;
Willy Tu61992ad2021-03-29 15:33:20 -07002114
2115 // populate sensor name from path
2116 auto name = sensor::parseSdrIdFromPath(path);
George Liu42247d22025-08-19 10:24:47 +08002117 int nameSize = std::min(name.size(), sizeof(record.body.idString));
George Liuf60be692025-08-19 11:28:42 +08002118 get_sdr::body::setIdStrLen(nameSize, record.body);
2119 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1"
George Liu42247d22025-08-19 10:24:47 +08002120 std::memset(record.body.idString, 0x00, sizeof(record.body.idString));
2121 std::memcpy(record.body.idString, name.c_str(), nameSize);
Willy Tu61992ad2021-03-29 15:33:20 -07002122
2123 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002124 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002125
2126 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002127}
2128
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002129uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002130{
2131 return std::min(getSensorTree().size(), maxIPMISensors);
2132}
2133
Willy Tu4eca2512022-06-20 21:14:51 -07002134static int getSensorDataRecord(
2135 ipmi::Context::ptr ctx,
2136 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2137 std::vector<uint8_t>& recordData, uint16_t recordID,
2138 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002139{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302140 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002141 size_t lastRecord = ipmi::getNumberOfSensors() +
2142 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2143 uint16_t nextRecord(recordID + 1);
2144
Willy Tu38e7a2b2021-03-29 15:09:56 -07002145 if (recordID == lastRecordIndex)
2146 {
2147 recordID = lastRecord;
2148 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002149 if (recordID == lastRecord)
2150 {
2151 nextRecord = lastRecordIndex;
2152 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002153 if (recordID > lastRecord)
2154 {
George Liude6694e2024-07-17 15:22:25 +08002155 lg2::error("getSensorDataRecord: recordID > lastRecord error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002156 return GENERAL_ERROR;
2157 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002158 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002159 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002160 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2161 recordData);
2162 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002163 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002164 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002165 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002166 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002167 }
2168
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002169 // Perform a incremental scan of the SDR Record ID's and translate the
2170 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2171 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2172 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2173 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002174 std::string connection;
2175 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002176 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002177 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002178 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002179 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002180 // LUN 0 has one reserved sensor number. Compensate here by adding one
2181 // to the record ID
2182 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002183 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002184 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002185 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002186 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002187 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2188 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2189 // rules governing its use.
2190 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002191 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002192 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002193
Patrick Williams1318a5e2024-08-16 15:19:54 -04002194 auto status =
2195 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2196 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002197 if (status)
2198 {
George Liude6694e2024-07-17 15:22:25 +08002199 lg2::error("getSensorDataRecord: getSensorConnection error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002200 return GENERAL_ERROR;
2201 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002202 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002203 // Return an error on LUN 2 assingments, and any sensor number beyond the
2204 // range of LUN 3
2205 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2206 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002207 {
George Liude6694e2024-07-17 15:22:25 +08002208 lg2::error("getSensorDataRecord: invalidSensorNumber");
Willy Tu38e7a2b2021-03-29 15:09:56 -07002209 return GENERAL_ERROR;
2210 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002211 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2212 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2213
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002214 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2215 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002216 {
George Liude6694e2024-07-17 15:22:25 +08002217 lg2::error("getSensorDataRecord: sensor record mismatch");
Johnathan Manteyce982772021-07-28 15:08:30 -07002218 return GENERAL_ERROR;
2219 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002220
Willy Tu38e7a2b2021-03-29 15:09:56 -07002221 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002222 if (std::find(interfaces.begin(), interfaces.end(),
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002223 SensorValue::interface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002224 {
Willy Tu11d68892022-01-20 10:37:34 -08002225 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002226
Hao Jiange39d4d82021-04-16 17:02:40 -07002227 // If the request doesn't read SDR body, construct only header and key
2228 // part to avoid additional DBus transaction.
2229 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2230 {
2231 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2232 }
Willy Tu4eca2512022-06-20 21:14:51 -07002233 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2234 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002235 {
2236 return GENERAL_ERROR;
2237 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002238
selvaganapathi7b2e5502023-02-14 07:10:47 +05302239 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2240 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002241
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002242 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002243 }
Willy Tu61992ad2021-03-29 15:33:20 -07002244
Scron Chang2703b022021-07-06 15:47:45 +08002245#ifdef FEATURE_HYBRID_SENSORS
2246 if (auto sensor = findStaticSensor(path);
2247 sensor != ipmi::sensor::sensors.end() &&
2248 getSensorEventTypeFromPath(path) !=
2249 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2250 {
Willy Tu11d68892022-01-20 10:37:34 -08002251 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002252
2253 // If the request doesn't read SDR body, construct only header and key
2254 // part to avoid additional DBus transaction.
2255 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2256 {
2257 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2258 }
2259 else
2260 {
2261 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2262 }
2263
selvaganapathi7b2e5502023-02-14 07:10:47 +05302264 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2265 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002266
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002267 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002268 }
2269#endif
2270
Willy Tu61992ad2021-03-29 15:33:20 -07002271 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002272 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2273 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002274 {
Willy Tu11d68892022-01-20 10:37:34 -08002275 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002276
Hao Jiange39d4d82021-04-16 17:02:40 -07002277 // If the request doesn't read SDR body, construct only header and key
2278 // part to avoid additional DBus transaction.
2279 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2280 {
2281 constructEventSdrHeaderKey(sensorNum, recordID, record);
2282 }
Willy Tu4eca2512022-06-20 21:14:51 -07002283 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2284 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002285 {
2286 return GENERAL_ERROR;
2287 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302288 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2289 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002290 }
2291
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002292 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002293}
2294
2295/** @brief implements the get SDR Info command
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002296 * @param operation : 0 or not supplied returns sensor count
2297 * 1 return SDR count
Willy Tude54f482021-01-26 15:59:09 -08002298 *
2299 * @returns IPMI completion code plus response data
2300 * - sdrCount - sensor/SDR count
2301 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2302 */
2303static ipmi::RspType<uint8_t, // respcount
2304 uint8_t, // dynamic population flags
2305 uint32_t // last time a sensor was added
2306 >
2307 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002308 std::optional<uint8_t> operation)
Willy Tude54f482021-01-26 15:59:09 -08002309{
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002310 auto& sensorTree{getSensorTree()};
2311 uint8_t sdrCount{};
2312 // Sensors are dynamically allocated
2313 uint8_t lunsAndDynamicPopulation{0x80};
2314 constexpr uint8_t getSdrCount{1};
2315 constexpr uint8_t getSensorCount{0};
Willy Tude54f482021-01-26 15:59:09 -08002316
2317 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2318 {
2319 return ipmi::responseResponseError();
2320 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002321 uint16_t numSensors{ipmi::getNumberOfSensors()};
2322 if (operation.value_or(0) == getSdrCount)
Willy Tude54f482021-01-26 15:59:09 -08002323 {
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002324 sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
Willy Tude54f482021-01-26 15:59:09 -08002325 }
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002326 else if (operation.value_or(0) == getSensorCount)
Willy Tude54f482021-01-26 15:59:09 -08002327 {
2328 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002329 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002330 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002331 sdrCount =
2332 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002333 }
Harvey Wu75893062023-03-22 17:17:31 +08002334 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002335 {
2336 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2337 ? maxSensorsPerLUN
2338 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2339 }
Harvey Wu75893062023-03-22 17:17:31 +08002340 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002341 {
2342 if (numSensors <= maxIPMISensors)
2343 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002344 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2345 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002346 }
2347 else
2348 {
Willy Tude54f482021-01-26 15:59:09 -08002349 throw std::out_of_range(
2350 "Maximum number of IPMI sensors exceeded.");
2351 }
2352 }
2353 }
2354 else
2355 {
2356 return ipmi::responseInvalidFieldRequest();
2357 }
2358
Johnathan Manteye7ef94d2024-06-18 13:20:57 -07002359 // Flag which LUNs have sensors associated
Willy Tude54f482021-01-26 15:59:09 -08002360 if (numSensors > 0)
2361 {
2362 lunsAndDynamicPopulation |= 1;
2363 }
2364 if (numSensors > maxSensorsPerLUN)
2365 {
2366 lunsAndDynamicPopulation |= 2;
2367 }
2368 if (numSensors >= (maxSensorsPerLUN * 2))
2369 {
2370 lunsAndDynamicPopulation |= 8;
2371 }
2372 if (numSensors > maxIPMISensors)
2373 {
Willy Tude54f482021-01-26 15:59:09 -08002374 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2375 }
2376
2377 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2378 sdrLastAdd);
2379}
2380
2381/* end sensor commands */
2382
2383/* storage commands */
2384
2385ipmi::RspType<uint8_t, // sdr version
2386 uint16_t, // record count
2387 uint16_t, // free space
2388 uint32_t, // most recent addition
2389 uint32_t, // most recent erase
2390 uint8_t // operationSupport
2391 >
2392 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2393{
Willy Tude54f482021-01-26 15:59:09 -08002394 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Patrick Williams1318a5e2024-08-16 15:19:54 -04002395 uint16_t recordCount =
2396 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002397
2398 uint8_t operationSupport = static_cast<uint8_t>(
2399 SdrRepositoryInfoOps::overflow); // write not supported
2400
2401 operationSupport |=
2402 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2403 operationSupport |= static_cast<uint8_t>(
2404 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2405 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2406 unspecifiedFreeSpace, sdrLastAdd,
2407 sdrLastRemove, operationSupport);
2408}
2409
2410/** @brief implements the get SDR allocation info command
2411 *
2412 * @returns IPMI completion code plus response data
2413 * - allocUnits - Number of possible allocation units
2414 * - allocUnitSize - Allocation unit size in bytes.
2415 * - allocUnitFree - Number of free allocation units
2416 * - allocUnitLargestFree - Largest free block in allocation units
2417 * - maxRecordSize - Maximum record size in allocation units.
2418 */
2419ipmi::RspType<uint16_t, // allocUnits
2420 uint16_t, // allocUnitSize
2421 uint16_t, // allocUnitFree
2422 uint16_t, // allocUnitLargestFree
2423 uint8_t // maxRecordSize
2424 >
2425 ipmiStorageGetSDRAllocationInfo()
2426{
2427 // 0000h unspecified number of alloc units
2428 constexpr uint16_t allocUnits = 0;
2429
2430 constexpr uint16_t allocUnitFree = 0;
2431 constexpr uint16_t allocUnitLargestFree = 0;
2432 // only allow one block at a time
2433 constexpr uint8_t maxRecordSize = 1;
2434
2435 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2436 allocUnitLargestFree, maxRecordSize);
2437}
2438
2439/** @brief implements the reserve SDR command
2440 * @returns IPMI completion code plus response data
2441 * - sdrReservationID
2442 */
2443ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2444{
2445 sdrReservationID++;
2446 if (sdrReservationID == 0)
2447 {
2448 sdrReservationID++;
2449 }
2450
2451 return ipmi::responseSuccess(sdrReservationID);
2452}
2453
2454ipmi::RspType<uint16_t, // next record ID
2455 std::vector<uint8_t> // payload
2456 >
2457 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2458 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2459{
2460 // reservation required for partial reads with non zero offset into
2461 // record
2462 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2463 {
George Liude6694e2024-07-17 15:22:25 +08002464 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002465 return ipmi::responseInvalidReservationId();
2466 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002467
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002468 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002469 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002470 {
George Liude6694e2024-07-17 15:22:25 +08002471 lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002472 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002473 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002474
Willy Tu4eca2512022-06-20 21:14:51 -07002475 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2476
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002477 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002478 int nextRecordId = getSensorDataRecord(
2479 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2480 record, recordID, offset + bytesToRead);
2481
2482 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002483 {
George Liude6694e2024-07-17 15:22:25 +08002484 lg2::error("ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002485 return ipmi::responseInvalidFieldRequest();
2486 }
Willy Tude54f482021-01-26 15:59:09 -08002487 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002488 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002489 if (!hdr)
2490 {
George Liude6694e2024-07-17 15:22:25 +08002491 lg2::error("ipmiStorageGetSDR: record header is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002492 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002493 }
2494
Patrick Williams1318a5e2024-08-16 15:19:54 -04002495 size_t sdrLength =
George Liu42247d22025-08-19 10:24:47 +08002496 sizeof(get_sdr::SensorDataRecordHeader) + hdr->recordLength;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002497 if (offset >= sdrLength)
2498 {
George Liude6694e2024-07-17 15:22:25 +08002499 lg2::error("ipmiStorageGetSDR: offset is outside the record");
Vernon Mauery6dbea082023-07-21 11:43:00 -07002500 return ipmi::responseParmOutOfRange();
2501 }
Willy Tude54f482021-01-26 15:59:09 -08002502 if (sdrLength < (offset + bytesToRead))
2503 {
2504 bytesToRead = sdrLength - offset;
2505 }
2506
2507 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2508 if (!respStart)
2509 {
George Liude6694e2024-07-17 15:22:25 +08002510 lg2::error("ipmiStorageGetSDR: record is null");
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002511 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002512 }
2513
2514 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002515
Willy Tude54f482021-01-26 15:59:09 -08002516 return ipmi::responseSuccess(nextRecordId, recordData);
2517}
adarshgrami042e9db2022-09-15 10:34:34 +05302518namespace dcmi
2519{
2520
Thang Tranb1416ef2023-08-02 13:57:09 +07002521std::tuple<uint8_t, // Total of instance sensors
2522 std::vector<sensorInfo> // The list of sensors
2523 >
2524 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2525 uint8_t entityInstance, uint8_t instanceStart)
2526{
2527 std::vector<sensorInfo> sensorList;
2528 uint8_t totalInstSensor = 0;
2529 auto match = ipmi::dcmi::validEntityId.find(entityId);
2530
2531 if (match == ipmi::dcmi::validEntityId.end())
2532 {
2533 return std::make_tuple(totalInstSensor, sensorList);
2534 }
2535
2536 auto& sensorTree = getSensorTree();
2537 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2538 {
2539 return std::make_tuple(totalInstSensor, sensorList);
2540 }
2541
2542 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2543
Willy Tu62e3ca82024-01-31 17:28:34 +00002544 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002545 for (const auto& sensor : sensorTree)
2546 {
2547 const std::string& sensorObjPath = sensor.first;
2548 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2549
2550 /*
2551 * In the DCMI specification, it only supports the sensor type is 0x01
2552 * (temperature type) for both Get Sensor Info and Get Temperature
2553 * Readings commands.
2554 */
2555 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2556 {
2557 continue;
2558 }
2559
2560 const auto& connection = sensor.second.begin()->first;
2561 DbusInterfaceMap sensorMap;
2562
2563 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2564 sensorMapSdrUpdatePeriod))
2565 {
George Liude6694e2024-07-17 15:22:25 +08002566 lg2::error("Failed to update sensor map for threshold sensor, "
2567 "service: {SERVICE}, path: {PATH}",
2568 "SERVICE", connection, "PATH", sensorObjPath);
Thang Tranb1416ef2023-08-02 13:57:09 +07002569 continue;
2570 }
2571
2572 uint8_t entityIdValue = 0;
2573 uint8_t entityInstanceValue = 0;
2574
2575 /*
2576 * Get the Entity ID, Entity Instance information which are configured
2577 * in the Entity-Manger.
2578 */
2579 updateIpmiFromAssociation(
2580 sensorObjPath,
2581 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2582 sensorMap, entityIdValue, entityInstanceValue);
2583
2584 if (entityIdValue == match->first || entityIdValue == match->second)
2585 {
2586 totalInstSensor++;
2587
2588 /*
2589 * When Entity Instance parameter is not 0, we only get the first
2590 * sensor whose Entity Instance number is equal input Entity
2591 * Instance parameter.
2592 */
2593 if (entityInstance)
2594 {
2595 if (!sensorList.empty())
2596 {
2597 continue;
2598 }
2599
2600 if (entityInstanceValue == entityInstance)
2601 {
2602 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002603 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002604 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002605 ++invalidSensorNumberErrCount;
2606 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002607 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002608 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2609 recordId, entityIdValue,
2610 entityInstanceValue);
2611 }
2612 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002613 else if (entityInstanceValue >= instanceStart)
2614 {
2615 auto recordId = getSensorNumberFromPath(sensorObjPath);
2616 if (recordId == invalidSensorNumber)
2617 {
2618 ++invalidSensorNumberErrCount;
2619 continue;
2620 }
2621 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2622 recordId, entityIdValue,
2623 entityInstanceValue);
2624 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002625 }
2626 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002627 if (invalidSensorNumberErrCount != 0)
2628 {
George Liude6694e2024-07-17 15:22:25 +08002629 lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2630 "{ERR_COUNT} times",
2631 "ERR_COUNT", invalidSensorNumberErrCount);
Willy Tu62e3ca82024-01-31 17:28:34 +00002632 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002633
2634 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2635 return first.entityInstance <= second.entityInstance;
2636 };
2637
2638 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2639
2640 return std::make_tuple(totalInstSensor, sensorList);
2641}
2642
Thang Tran3dad8262023-08-17 15:20:56 +07002643std::tuple<bool, // Reading result
2644 uint7_t, // Temp value
2645 bool> // Sign bit
2646 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2647{
2648 std::string service{};
2649 boost::system::error_code ec =
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002650 ipmi::getService(ctx, SensorValue::interface, objectPath, service);
Thang Tran3dad8262023-08-17 15:20:56 +07002651 if (ec.value())
2652 {
2653 return std::make_tuple(false, 0, false);
2654 }
2655
2656 ipmi::PropertyMap properties{};
2657 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002658 SensorValue::interface, properties);
Thang Tran3dad8262023-08-17 15:20:56 +07002659 if (ec.value())
2660 {
2661 return std::make_tuple(false, 0, false);
2662 }
2663
2664 auto scaleIt = properties.find("Scale");
2665 double scaleVal = 0.0;
2666 if (scaleIt != properties.end())
2667 {
2668 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2669 }
2670
Alexander Hansenf365c7f2025-11-14 12:08:34 +01002671 auto tempValIt = properties.find(SensorValue::property_names::value);
Thang Tran3dad8262023-08-17 15:20:56 +07002672 double tempVal = 0.0;
2673 if (tempValIt == properties.end())
2674 {
2675 return std::make_tuple(false, 0, false);
2676 }
2677
2678 const double maxTemp = 127;
2679 double absTempVal = 0.0;
2680 bool signBit = false;
2681
2682 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2683 tempVal = std::pow(10, scaleVal) * tempVal;
2684 absTempVal = std::abs(tempVal);
2685 absTempVal = std::min(absTempVal, maxTemp);
2686 signBit = (tempVal < 0) ? true : false;
2687
2688 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2689}
2690
adarshgrami042e9db2022-09-15 10:34:34 +05302691ipmi::RspType<uint8_t, // No of instances for requested id
2692 uint8_t, // No of record ids in the response
2693 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2694 // IDs
2695 >
2696 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002697 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302698{
2699 auto match = ipmi::dcmi::validEntityId.find(entityId);
2700 if (match == ipmi::dcmi::validEntityId.end())
2701 {
George Liude6694e2024-07-17 15:22:25 +08002702 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
adarshgrami042e9db2022-09-15 10:34:34 +05302703
2704 return ipmi::responseInvalidFieldRequest();
2705 }
2706
2707 if (sensorType != ipmi::dcmi::temperatureSensorType)
2708 {
George Liude6694e2024-07-17 15:22:25 +08002709 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2710 sensorType);
adarshgrami042e9db2022-09-15 10:34:34 +05302711
2712 return ipmi::responseInvalidFieldRequest();
2713 }
adarshgrami042e9db2022-09-15 10:34:34 +05302714
2715 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002716 const auto& [totalSensorInst, sensorList] =
2717 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302718
Thang Tranb1416ef2023-08-02 13:57:09 +07002719 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302720 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002721 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2722 }
adarshgrami042e9db2022-09-15 10:34:34 +05302723
Thang Tranb1416ef2023-08-02 13:57:09 +07002724 /*
2725 * As DCMI specification, the maximum number of Record Ids of response data
2726 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2727 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2728 * in response data.
2729 */
2730 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2731
2732 for (const auto& sensor : sensorList)
2733 {
2734 sensorRec.emplace_back(sensor.recordId);
2735 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302736 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002737 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302738 }
2739 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002740
2741 return ipmi::responseSuccess(
2742 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302743}
Thang Tran3dad8262023-08-17 15:20:56 +07002744
2745ipmi::RspType<uint8_t, // No of instances for requested id
2746 uint8_t, // No of record ids in the response
2747 std::vector< // Temperature Data
2748 std::tuple<uint7_t, // Temperature value
2749 bool, // Sign bit
2750 uint8_t // Entity Instance of sensor
2751 >>>
2752 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2753 uint8_t entityId, uint8_t entityInstance,
2754 uint8_t instanceStart)
2755{
2756 auto match = ipmi::dcmi::validEntityId.find(entityId);
2757 if (match == ipmi::dcmi::validEntityId.end())
2758 {
George Liude6694e2024-07-17 15:22:25 +08002759 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Thang Tran3dad8262023-08-17 15:20:56 +07002760
2761 return ipmi::responseInvalidFieldRequest();
2762 }
2763
2764 if (sensorType != ipmi::dcmi::temperatureSensorType)
2765 {
George Liude6694e2024-07-17 15:22:25 +08002766 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2767 sensorType);
Thang Tran3dad8262023-08-17 15:20:56 +07002768
2769 return ipmi::responseInvalidFieldRequest();
2770 }
2771
2772 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2773 const auto& [totalSensorInst, sensorList] =
2774 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2775
2776 if (sensorList.empty())
2777 {
2778 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2779 }
2780
2781 /*
2782 * As DCMI specification, the maximum number of Record Ids of response data
2783 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2784 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2785 * in response data.
2786 */
2787 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2788
2789 for (const auto& sensor : sensorList)
2790 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04002791 const auto& [readResult, tempVal, signBit] =
2792 readTemp(ctx, sensor.objectPath);
Thang Tran3dad8262023-08-17 15:20:56 +07002793
2794 if (readResult)
2795 {
2796 tempReadingVal.emplace_back(
2797 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2798
2799 if (tempReadingVal.size() >= numOfRec)
2800 {
2801 break;
2802 }
2803 }
2804 }
2805
2806 return ipmi::responseSuccess(totalSensorInst,
2807 static_cast<uint8_t>(tempReadingVal.size()),
2808 tempReadingVal);
2809}
2810
adarshgrami042e9db2022-09-15 10:34:34 +05302811} // namespace dcmi
2812
Willy Tude54f482021-01-26 15:59:09 -08002813/* end storage commands */
2814
2815void registerSensorFunctions()
2816{
2817 // <Platform Event>
2818 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2819 ipmi::sensor_event::cmdPlatformEvent,
2820 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2821
Willy Tudbafbce2021-03-29 00:37:05 -07002822 // <Set Sensor Reading and Event Status>
2823 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2824 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2825 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002826
Willy Tude54f482021-01-26 15:59:09 -08002827 // <Get Sensor Reading>
2828 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2829 ipmi::sensor_event::cmdGetSensorReading,
2830 ipmi::Privilege::User, ipmiSenGetSensorReading);
2831
2832 // <Get Sensor Threshold>
2833 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2834 ipmi::sensor_event::cmdGetSensorThreshold,
2835 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2836
2837 // <Set Sensor Threshold>
2838 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2839 ipmi::sensor_event::cmdSetSensorThreshold,
2840 ipmi::Privilege::Operator,
2841 ipmiSenSetSensorThresholds);
2842
2843 // <Get Sensor Event Enable>
2844 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2845 ipmi::sensor_event::cmdGetSensorEventEnable,
2846 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2847
2848 // <Get Sensor Event Status>
2849 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2850 ipmi::sensor_event::cmdGetSensorEventStatus,
2851 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2852
2853 // register all storage commands for both Sensor and Storage command
2854 // versions
2855
2856 // <Get SDR Repository Info>
2857 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2858 ipmi::storage::cmdGetSdrRepositoryInfo,
2859 ipmi::Privilege::User,
2860 ipmiStorageGetSDRRepositoryInfo);
2861
2862 // <Get Device SDR Info>
2863 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2864 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2865 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2866
2867 // <Get SDR Allocation Info>
2868 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2869 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2870 ipmi::Privilege::User,
2871 ipmiStorageGetSDRAllocationInfo);
2872
2873 // <Reserve SDR Repo>
2874 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2875 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2876 ipmi::Privilege::User, ipmiStorageReserveSDR);
2877
2878 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2879 ipmi::storage::cmdReserveSdrRepository,
2880 ipmi::Privilege::User, ipmiStorageReserveSDR);
2881
2882 // <Get Sdr>
2883 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2884 ipmi::sensor_event::cmdGetDeviceSdr,
2885 ipmi::Privilege::User, ipmiStorageGetSDR);
2886
2887 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2888 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2889 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302890 // <Get DCMI Sensor Info>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002891 ipmi::registerGroupHandler(
2892 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2893 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2894 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002895 // <Get Temperature Readings>
Patrick Williams1318a5e2024-08-16 15:19:54 -04002896 ipmi::registerGroupHandler(
2897 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2898 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2899 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002900}
2901} // namespace ipmi