blob: 3989ed2a5a46ca5ed4d26ede07c60bac578d9b56 [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>
31#include <phosphor-logging/log.hpp>
32#include <sdbusplus/bus.hpp>
33#include <user_channel/channel_layer.hpp>
34
35#include <algorithm>
36#include <array>
Willy Tude54f482021-01-26 15:59:09 -080037#include <chrono>
38#include <cmath>
39#include <cstring>
Willy Tu62e3ca82024-01-31 17:28:34 +000040#include <format>
Willy Tude54f482021-01-26 15:59:09 -080041#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080042#include <map>
Willy Tude54f482021-01-26 15:59:09 -080043#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080044#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48
Scron Chang2703b022021-07-06 15:47:45 +080049#ifdef FEATURE_HYBRID_SENSORS
50
51#include "sensordatahandler.hpp"
52namespace ipmi
53{
54namespace sensor
55{
56extern const IdInfoMap sensors;
57} // namespace sensor
58} // namespace ipmi
59#endif
adarshgrami042e9db2022-09-15 10:34:34 +053060namespace ipmi
61{
62namespace dcmi
63{
64// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
65static const std::map<uint8_t, uint8_t> validEntityId{
66 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
67 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
68constexpr uint8_t temperatureSensorType = 0x01;
69constexpr uint8_t maxRecords = 8;
70} // namespace dcmi
71} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080072constexpr std::array<const char*, 7> suffixes = {
73 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
74 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080075namespace ipmi
76{
Hao Jiangd48c9212021-02-03 15:45:06 -080077
78using phosphor::logging::entry;
79using phosphor::logging::level;
80using phosphor::logging::log;
81
Willy Tude54f482021-01-26 15:59:09 -080082static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070083static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080084
Willy Tu38e7a2b2021-03-29 15:09:56 -070085// BMC I2C address is generally at 0x20
86static constexpr uint8_t bmcI2CAddr = 0x20;
87
Willy Tude54f482021-01-26 15:59:09 -080088constexpr size_t maxSDRTotalSize =
89 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
90constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
91
92static uint16_t sdrReservationID;
93static uint32_t sdrLastAdd = noTimestamp;
94static uint32_t sdrLastRemove = noTimestamp;
95static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070096
97// The IPMI spec defines four Logical Units (LUN), each capable of supporting
98// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
99// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800100// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700101// location.
102static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
103
Harvey Wu75893062023-03-22 17:17:31 +0800104static constexpr uint8_t lun0 = 0x0;
105static constexpr uint8_t lun1 = 0x1;
106static constexpr uint8_t lun3 = 0x3;
107
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700108static constexpr size_t lun0MaxSensorNum = 0xfe;
109static constexpr size_t lun1MaxSensorNum = 0x1fe;
110static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800111static constexpr int GENERAL_ERROR = -1;
112
Willy Tude54f482021-01-26 15:59:09 -0800113static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
114
115// Specify the comparison required to sort and find char* map objects
116struct CmpStr
117{
118 bool operator()(const char* a, const char* b) const
119 {
120 return std::strcmp(a, b) < 0;
121 }
122};
123const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
124 sensorUnits{{{"temperature", SensorUnits::degreesC},
125 {"voltage", SensorUnits::volts},
126 {"current", SensorUnits::amps},
127 {"fan_tach", SensorUnits::rpm},
Johnathan Mantey7b037272024-06-17 12:12:40 -0700128 {"power", SensorUnits::watts},
129 {"energy", SensorUnits::joules}}};
Willy Tude54f482021-01-26 15:59:09 -0800130
131void registerSensorFunctions() __attribute__((constructor));
132
Patrick Williams5d82f472022-07-22 19:26:53 -0500133static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800134 *getSdBus(),
135 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
136 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500137 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500138 getSensorTree().clear();
139 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
140 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
141 std::chrono::system_clock::now().time_since_epoch())
142 .count();
Patrick Williams369824e2023-10-20 11:18:23 -0500143});
Willy Tude54f482021-01-26 15:59:09 -0800144
Patrick Williams5d82f472022-07-22 19:26:53 -0500145static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800146 *getSdBus(),
147 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
148 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500149 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500150 getSensorTree().clear();
151 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
152 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
153 std::chrono::system_clock::now().time_since_epoch())
154 .count();
Patrick Williams369824e2023-10-20 11:18:23 -0500155});
Willy Tude54f482021-01-26 15:59:09 -0800156
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700157ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
158 std::string& connection, std::string& path,
159 std::vector<std::string>* interfaces)
160{
161 auto& sensorTree = getSensorTree();
162 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
163 {
164 return IPMI_CC_RESPONSE_ERROR;
165 }
166
167 if (ctx == nullptr)
168 {
169 return IPMI_CC_RESPONSE_ERROR;
170 }
171
172 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
173 if (path.empty())
174 {
175 return IPMI_CC_INVALID_FIELD_REQUEST;
176 }
177
178 for (const auto& sensor : sensorTree)
179 {
180 if (path == sensor.first)
181 {
182 connection = sensor.second.begin()->first;
183 if (interfaces)
184 *interfaces = sensor.second.begin()->second;
185 break;
186 }
187 }
188
189 return 0;
190}
191
192SensorSubTree& getSensorTree()
193{
194 static SensorSubTree sensorTree;
195 return sensorTree;
196}
197
Willy Tude54f482021-01-26 15:59:09 -0800198// this keeps track of deassertions for sensor event status command. A
199// deasertion can only happen if an assertion was seen first.
200static boost::container::flat_map<
201 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
202 thresholdDeassertMap;
203
Patrick Williams5d82f472022-07-22 19:26:53 -0500204static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800205 *getSdBus(),
206 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
207 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500208 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500209 boost::container::flat_map<std::string, std::variant<bool, double>> values;
210 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800211
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500212 auto findAssert = std::find_if(values.begin(), values.end(),
213 [](const auto& pair) {
214 return pair.first.find("Alarm") != std::string::npos;
215 });
216 if (findAssert != values.end())
217 {
218 auto ptr = std::get_if<bool>(&(findAssert->second));
219 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800220 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500221 phosphor::logging::log<phosphor::logging::level::ERR>(
222 "thresholdChanged: Assert non bool");
223 return;
224 }
225 if (*ptr)
226 {
227 phosphor::logging::log<phosphor::logging::level::INFO>(
228 "thresholdChanged: Assert",
229 phosphor::logging::entry("SENSOR=%s", m.get_path()));
230 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
231 }
232 else
233 {
234 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
235 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800236 {
237 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500238 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800239 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500240 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800241 }
242 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500243 }
Patrick Williams369824e2023-10-20 11:18:23 -0500244});
Willy Tude54f482021-01-26 15:59:09 -0800245
Hao Jiangd2afd052020-12-10 15:09:32 -0800246namespace sensor
247{
248static constexpr const char* vrInterface =
249 "xyz.openbmc_project.Control.VoltageRegulatorMode";
250static constexpr const char* sensorInterface =
251 "xyz.openbmc_project.Sensor.Value";
252} // namespace sensor
253
Willy Tude54f482021-01-26 15:59:09 -0800254static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
255 double& min)
256{
257 max = 127;
258 min = -128;
259
Hao Jiangd2afd052020-12-10 15:09:32 -0800260 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800261 auto critical =
262 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
263 auto warning =
264 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
265
266 if (sensorObject != sensorMap.end())
267 {
268 auto maxMap = sensorObject->second.find("MaxValue");
269 auto minMap = sensorObject->second.find("MinValue");
270
271 if (maxMap != sensorObject->second.end())
272 {
273 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
274 }
275 if (minMap != sensorObject->second.end())
276 {
277 min = std::visit(VariantToDoubleVisitor(), minMap->second);
278 }
279 }
280 if (critical != sensorMap.end())
281 {
282 auto lower = critical->second.find("CriticalLow");
283 auto upper = critical->second.find("CriticalHigh");
284 if (lower != critical->second.end())
285 {
286 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300287 if (std::isfinite(value))
288 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700289 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300290 }
Willy Tude54f482021-01-26 15:59:09 -0800291 }
292 if (upper != critical->second.end())
293 {
294 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300295 if (std::isfinite(value))
296 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700297 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300298 }
Willy Tude54f482021-01-26 15:59:09 -0800299 }
300 }
301 if (warning != sensorMap.end())
302 {
Willy Tude54f482021-01-26 15:59:09 -0800303 auto lower = warning->second.find("WarningLow");
304 auto upper = warning->second.find("WarningHigh");
305 if (lower != warning->second.end())
306 {
307 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300308 if (std::isfinite(value))
309 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700310 min = std::fmin(value, min);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300311 }
Willy Tude54f482021-01-26 15:59:09 -0800312 }
313 if (upper != warning->second.end())
314 {
315 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300316 if (std::isfinite(value))
317 {
Johnathan Mantey92217f02024-06-20 12:43:01 -0700318 max = std::fmax(value, max);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300319 }
Willy Tude54f482021-01-26 15:59:09 -0800320 }
321 }
322}
323
324static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700325 std::string sensorPath, DbusInterfaceMap& sensorMap,
326 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800327{
Scron Chang2703b022021-07-06 15:47:45 +0800328#ifdef FEATURE_HYBRID_SENSORS
329 if (auto sensor = findStaticSensor(sensorPath);
330 sensor != ipmi::sensor::sensors.end() &&
331 getSensorEventTypeFromPath(sensorPath) !=
332 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
333 {
334 // If the incoming sensor is a discrete sensor, it might fail in
335 // getManagedObjects(), return true, and use its own getFunc to get
336 // value.
337 return true;
338 }
339#endif
340
Willy Tude54f482021-01-26 15:59:09 -0800341 static boost::container::flat_map<
342 std::string, std::chrono::time_point<std::chrono::steady_clock>>
343 updateTimeMap;
344
345 auto updateFind = updateTimeMap.find(sensorConnection);
346 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
347 if (updateFind != updateTimeMap.end())
348 {
349 lastUpdate = updateFind->second;
350 }
351
352 auto now = std::chrono::steady_clock::now();
353
354 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700355 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800356 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800357 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800358
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800359 // Object managers for different kinds of OpenBMC DBus interfaces.
360 // Documented in the phosphor-dbus-interfaces repository.
361 const char* paths[] = {
362 "/xyz/openbmc_project/sensors",
363 "/xyz/openbmc_project/vr",
364 };
365 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
366 ObjectValueTree allManagedObjects;
367
368 for (size_t i = 0; i < num_paths; i++)
369 {
370 ObjectValueTree managedObjects;
371 boost::system::error_code ec = getManagedObjects(
372 ctx, sensorConnection.c_str(), paths[i], managedObjects);
373 if (ec)
374 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800375 continue;
376 }
377 allManagedObjects.merge(managedObjects);
378 found = true;
379 }
380
381 if (!found)
382 {
Tom Tung6615d472023-05-31 18:48:12 +0800383 phosphor::logging::log<phosphor::logging::level::ERR>(
384 "GetMangagedObjects for getSensorMap failed",
385 phosphor::logging::entry("SERVICE=%s",
386 sensorConnection.c_str()));
387
Willy Tude54f482021-01-26 15:59:09 -0800388 return false;
389 }
390
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800391 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700392 // Update time after finish building the map which allow the
393 // data to be cached for updatePeriod plus the build time.
394 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800395 }
396 auto connection = SensorCache.find(sensorConnection);
397 if (connection == SensorCache.end())
398 {
399 return false;
400 }
401 auto path = connection->second.find(sensorPath);
402 if (path == connection->second.end())
403 {
404 return false;
405 }
406 sensorMap = path->second;
407
408 return true;
409}
410
Hao Jiangd2afd052020-12-10 15:09:32 -0800411namespace sensor
412{
Hao Jiangd48c9212021-02-03 15:45:06 -0800413// Read VR profiles from sensor(daemon) interface
414static std::optional<std::vector<std::string>>
415 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800416{
417 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800418 auto supportedProperty = object.find("Supported");
419 if (supportedProperty == object.end() ||
420 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800421 {
422 phosphor::logging::log<phosphor::logging::level::ERR>(
423 "Missing the required Supported and Selected properties");
424 return std::nullopt;
425 }
426
427 const auto profilesPtr =
428 std::get_if<std::vector<std::string>>(&supportedProperty->second);
429
430 if (profilesPtr == nullptr)
431 {
432 phosphor::logging::log<phosphor::logging::level::ERR>(
433 "property is not array of string");
434 return std::nullopt;
435 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800436 return *profilesPtr;
437}
438
439// Calculate VR Mode from input IPMI discrete event bytes
440static std::optional<std::string>
441 calculateVRMode(uint15_t assertOffset,
442 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
443{
444 // get VR mode profiles from Supported Interface
445 auto profiles = getSupportedVrProfiles(VRObject);
446 if (!profiles)
447 {
448 return std::nullopt;
449 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800450
451 // interpret IPMI cmd bits into profiles' index
452 long unsigned int index = 0;
453 // only one bit should be set and the highest bit should not be used.
454 if (assertOffset == 0 || assertOffset == (1u << 15) ||
455 (assertOffset & (assertOffset - 1)))
456 {
457 phosphor::logging::log<phosphor::logging::level::ERR>(
458 "IPMI cmd format incorrect",
459
460 phosphor::logging::entry("BYTES=%#02x",
461 static_cast<uint16_t>(assertOffset)));
462 return std::nullopt;
463 }
464
465 while (assertOffset != 1)
466 {
467 assertOffset >>= 1;
468 index++;
469 }
470
Hao Jiangd48c9212021-02-03 15:45:06 -0800471 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800472 {
473 phosphor::logging::log<phosphor::logging::level::ERR>(
474 "profile index out of boundary");
475 return std::nullopt;
476 }
477
Hao Jiangd48c9212021-02-03 15:45:06 -0800478 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800479}
480
481// Calculate sensor value from IPMI reading byte
482static std::optional<double>
483 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
484 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
485{
486 if (valueObject.find("Value") == valueObject.end())
487 {
488 phosphor::logging::log<phosphor::logging::level::ERR>(
489 "Missing the required Value property");
490 return std::nullopt;
491 }
492
493 double max = 0;
494 double min = 0;
495 getSensorMaxMin(sensorMap, max, min);
496
497 int16_t mValue = 0;
498 int16_t bValue = 0;
499 int8_t rExp = 0;
500 int8_t bExp = 0;
501 bool bSigned = false;
502
503 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
504 {
505 return std::nullopt;
506 }
507
508 double value = bSigned ? ((int8_t)reading) : reading;
509
510 value *= ((double)mValue);
511 value += ((double)bValue) * std::pow(10.0, bExp);
512 value *= std::pow(10.0, rExp);
513
514 return value;
515}
516
Willy Tu38e7a2b2021-03-29 15:09:56 -0700517// Extract file name from sensor path as the sensors SDR ID. Simplify the name
518// if it is too long.
519std::string parseSdrIdFromPath(const std::string& path)
520{
521 std::string name;
522 size_t nameStart = path.rfind("/");
523 if (nameStart != std::string::npos)
524 {
525 name = path.substr(nameStart + 1, std::string::npos - nameStart);
526 }
527
Willy Tu38e7a2b2021-03-29 15:09:56 -0700528 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
529 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200530#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800531 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700532 {
JeffLind950f412021-10-20 18:49:34 +0800533 if (boost::ends_with(name, suffix))
534 {
535 boost::replace_all(name, suffix, "");
536 break;
537 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700538 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200539#endif
540#ifdef SHORTNAME_REPLACE_WORDS
541 constexpr std::array<std::pair<const char*, const char*>, 2>
542 replaceWords = {std::make_pair("Output", "Out"),
543 std::make_pair("Input", "In")};
544 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800545 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200546 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800547 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200548#endif
549
550 // as a backup and if nothing else is configured
551 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700552 }
553 return name;
554}
555
Hao Jiangd48c9212021-02-03 15:45:06 -0800556bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
557 const std::string& path,
558 const ipmi::DbusInterfaceMap::mapped_type& object,
559 std::bitset<16>& assertions)
560{
561 auto profiles = sensor::getSupportedVrProfiles(object);
562 if (!profiles)
563 {
564 return false;
565 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700566 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800567
568 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700569 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800570 if (ec)
571 {
572 log<level::ERR>("Failed to get property",
573 entry("PROPERTY=%s", "Selected"),
574 entry("PATH=%s", path.c_str()),
575 entry("INTERFACE=%s", sensor::sensorInterface),
576 entry("WHAT=%s", ec.message().c_str()));
577 return false;
578 }
579
Willy Tu8366f0b2022-04-29 05:00:17 -0700580 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800581 if (itr == profiles->end())
582 {
583 using namespace phosphor::logging;
584 log<level::ERR>("VR mode doesn't match any of its profiles",
585 entry("PATH=%s", path.c_str()));
586 return false;
587 }
588 std::size_t index =
589 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
590
Willy Tubef102a2022-06-09 15:36:09 -0700591 // map index to response event assertion bit.
592 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800593 {
Willy Tubef102a2022-06-09 15:36:09 -0700594 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800595 }
596 else
597 {
598 log<level::ERR>("VR profile index reaches max assertion bit",
599 entry("PATH=%s", path.c_str()),
600 entry("INDEX=%uz", index));
601 return false;
602 }
603 if constexpr (debug)
604 {
605 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700606 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800607 }
608 return true;
609}
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700610
611/*
612 * Handle every Sensor Data Record besides Type 01
613 *
614 * The D-Bus sensors work well for generating Type 01 SDRs.
615 * After the Type 01 sensors are processed the remaining sensor types require
616 * special handling. Each BMC vendor is going to have their own requirements for
617 * insertion of non-Type 01 records.
618 * Manage non-Type 01 records:
619 *
620 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
621 * Populate it with the two weakly linked functions below, without adding the
622 * 'weak' attribute definition prior to the function definition.
623 * getOtherSensorsCount(...)
624 * getOtherSensorsDataRecord(...)
625 * Example contents are provided in the weak definitions below
626 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
627 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
628 * The contents of the sensorcommands_oem.cpp file will then override the code
629 * provided below.
630 */
631
632size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
633size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
634{
635 size_t fruCount = 0;
636
637 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
638 if (ret != ipmi::ccSuccess)
639 {
640 phosphor::logging::log<phosphor::logging::level::ERR>(
641 "getOtherSensorsCount: getFruSdrCount error");
642 return std::numeric_limits<size_t>::max();
643 }
644
645 const auto& entityRecords =
646 ipmi::sensor::EntityInfoMapContainer::getContainer()
647 ->getIpmiEntityRecords();
648 size_t entityCount = entityRecords.size();
649
650 return fruCount + ipmi::storage::type12Count + entityCount;
651}
652
653int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
654 std::vector<uint8_t>& recordData)
655 __attribute__((weak));
656int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
657 std::vector<uint8_t>& recordData)
658{
659 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
660 if (otherCount == std::numeric_limits<size_t>::max())
661 {
662 return GENERAL_ERROR;
663 }
664 const auto& entityRecords =
665 ipmi::sensor::EntityInfoMapContainer::getContainer()
666 ->getIpmiEntityRecords();
667
668 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
669 size_t entityCount{entityRecords.size()};
670 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
671
672 if (sdrIndex > otherCount)
673 {
674 return std::numeric_limits<int>::min();
675 }
676 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
677 {
678 // handle type 8 entity map records
679 ipmi::sensor::EntityInfoMap::const_iterator entity =
680 entityRecords.find(static_cast<uint8_t>(
681 sdrIndex - fruCount - ipmi::storage::type12Count));
682
683 if (entity == entityRecords.end())
684 {
685 return GENERAL_ERROR;
686 }
687 recordData = ipmi::storage::getType8SDRs(entity, recordID);
688 }
689 else if (sdrIndex >= fruCount)
690 {
691 // handle type 12 hardcoded records
692 size_t type12Index = sdrIndex - fruCount;
693 if (type12Index >= ipmi::storage::type12Count)
694 {
695 phosphor::logging::log<phosphor::logging::level::ERR>(
696 "getSensorDataRecord: type12Index error");
697 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);
706 ret != IPMI_CC_OK)
707 {
708 return GENERAL_ERROR;
709 }
710 data.header.record_id_msb = recordID >> 8;
711 data.header.record_id_lsb = recordID & 0xFF;
712 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
713 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
714 }
715
716 return 0;
717}
718
Hao Jiangd2afd052020-12-10 15:09:32 -0800719} // namespace sensor
720
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000721ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
722 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800723{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000724 constexpr const uint8_t validEnvmRev = 0x04;
725 constexpr const uint8_t lastSensorType = 0x2C;
726 constexpr const uint8_t oemReserved = 0xC0;
727
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700728 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000729 uint8_t evmRev = 0;
730 uint8_t sensorType = 0;
731 uint8_t sensorNum = 0;
732 uint8_t eventType = 0;
733 uint8_t eventData1 = 0;
734 std::optional<uint8_t> eventData2 = 0;
735 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700736 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000737 ipmi::ChannelInfo chInfo;
738
739 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
740 {
741 phosphor::logging::log<phosphor::logging::level::ERR>(
742 "Failed to get Channel Info",
743 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
744 return ipmi::responseUnspecifiedError();
745 }
746
747 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
748 ipmi::EChannelMediumType::systemInterface)
749 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700750 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000751 eventData1, eventData2, eventData3);
Johnathan Manteya440cd42024-06-20 13:21:48 -0700752 constexpr const uint8_t isSoftwareID = 0x01;
753 if (!(sysgeneratorID & isSoftwareID))
754 {
755 return ipmi::responseInvalidFieldRequest();
756 }
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700757 // Refer to IPMI Spec Table 32: SEL Event Records
758 generatorID = (ctx->channel << 12) // Channel
759 | (0x0 << 10) // Reserved
760 | (0x0 << 8) // 0x0 for sys-soft ID
Johnathan Manteya440cd42024-06-20 13:21:48 -0700761 | sysgeneratorID;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000762 }
763 else
764 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000765 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
766 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700767 // Refer to IPMI Spec Table 32: SEL Event Records
768 generatorID = (ctx->channel << 12) // Channel
769 | (0x0 << 10) // Reserved
770 | ((ctx->lun & 0x3) << 8) // Lun
771 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000772 }
773
774 if (!p.fullyUnpacked())
775 {
776 return ipmi::responseReqDataLenInvalid();
777 }
778
779 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
780 if (evmRev != validEnvmRev)
781 {
782 return ipmi::responseInvalidFieldRequest();
783 }
784 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
785 {
786 return ipmi::responseInvalidFieldRequest();
787 }
788
Willy Tude54f482021-01-26 15:59:09 -0800789 return ipmi::responseSuccess();
790}
791
Willy Tudbafbce2021-03-29 00:37:05 -0700792ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800793 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700794 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800795 bool, uint15_t, bool, uint8_t, uint8_t,
796 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700797{
798 std::string connection;
799 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700800 std::vector<std::string> interfaces;
801
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500802 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
803 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700804 if (status)
805 {
806 return ipmi::response(status);
807 }
808
Hao Jiangd2afd052020-12-10 15:09:32 -0800809 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700810 if (std::find(interfaces.begin(), interfaces.end(),
811 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700812 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700813 DbusInterfaceMap sensorMap;
814 if (!getSensorMap(ctx, connection, path, sensorMap))
815 {
816 return ipmi::responseResponseError();
817 }
818 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800819 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700820 {
821 return ipmi::responseResponseError();
822 }
823
Jie Yangf0a89942021-07-29 15:30:25 -0700824 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800825 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
826 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700827 {
828 return ipmi::responseResponseError();
829 }
830
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500831 auto value = sensor::calculateValue(reading, sensorMap,
832 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800833 if (!value)
834 {
835 return ipmi::responseResponseError();
836 }
837
838 if constexpr (debug)
839 {
840 phosphor::logging::log<phosphor::logging::level::INFO>(
841 "IPMI SET_SENSOR",
842 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
843 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
844 phosphor::logging::entry("VALUE=%f", *value));
845 }
846
847 boost::system::error_code ec =
848 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
849 "Value", ipmi::Value(*value));
850
851 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500852 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800853 // callback functions for now (e.g. ipmiSetSensorReading).
854 if (ec)
855 {
856 using namespace phosphor::logging;
857 log<level::ERR>("Failed to set property",
858 entry("PROPERTY=%s", "Value"),
859 entry("PATH=%s", path.c_str()),
860 entry("INTERFACE=%s", sensor::sensorInterface),
861 entry("WHAT=%s", ec.message().c_str()));
862 return ipmi::responseResponseError();
863 }
864 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700865 }
866
Hao Jiange39d4d82021-04-16 17:02:40 -0700867 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
868 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700869 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700870 DbusInterfaceMap sensorMap;
871 if (!getSensorMap(ctx, connection, path, sensorMap))
872 {
873 return ipmi::responseResponseError();
874 }
875 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800876 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700877 {
878 return ipmi::responseResponseError();
879 }
880
Hao Jiangd2afd052020-12-10 15:09:32 -0800881 // VR sensors are treated as a special case and we will not check the
882 // write permission for VR sensors, since they always deemed writable
883 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500884 auto vrMode = sensor::calculateVRMode(assertOffset,
885 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800886 if (!vrMode)
887 {
888 return ipmi::responseResponseError();
889 }
890 boost::system::error_code ec = setDbusProperty(
891 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
892 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500893 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800894 // callback functions for now (e.g. ipmiSetSensorReading).
895 if (ec)
896 {
897 using namespace phosphor::logging;
898 log<level::ERR>("Failed to set property",
899 entry("PROPERTY=%s", "Selected"),
900 entry("PATH=%s", path.c_str()),
901 entry("INTERFACE=%s", sensor::sensorInterface),
902 entry("WHAT=%s", ec.message().c_str()));
903 return ipmi::responseResponseError();
904 }
905 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700906 }
907
Hao Jiangd2afd052020-12-10 15:09:32 -0800908 phosphor::logging::log<phosphor::logging::level::ERR>(
909 "unknown sensor type",
910 phosphor::logging::entry("PATH=%s", path.c_str()));
911 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700912}
913
Willy Tude54f482021-01-26 15:59:09 -0800914ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
915 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
916{
917 std::string connection;
918 std::string path;
919
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000920 if (sensnum == reservedSensorNumber)
921 {
922 return ipmi::responseInvalidFieldRequest();
923 }
924
Willy Tude54f482021-01-26 15:59:09 -0800925 auto status = getSensorConnection(ctx, sensnum, connection, path);
926 if (status)
927 {
928 return ipmi::response(status);
929 }
930
Scron Chang2703b022021-07-06 15:47:45 +0800931#ifdef FEATURE_HYBRID_SENSORS
932 if (auto sensor = findStaticSensor(path);
933 sensor != ipmi::sensor::sensors.end() &&
934 getSensorEventTypeFromPath(path) !=
935 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
936 {
937 if (ipmi::sensor::Mutability::Read !=
938 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
939 {
940 return ipmi::responseIllegalCommand();
941 }
942
943 uint8_t operation;
944 try
945 {
946 ipmi::sensor::GetSensorResponse getResponse =
947 sensor->second.getFunc(sensor->second);
948
949 if (getResponse.readingOrStateUnavailable)
950 {
951 operation |= static_cast<uint8_t>(
952 IPMISensorReadingByte2::readingStateUnavailable);
953 }
954 if (getResponse.scanningEnabled)
955 {
956 operation |= static_cast<uint8_t>(
957 IPMISensorReadingByte2::sensorScanningEnable);
958 }
959 if (getResponse.allEventMessagesEnabled)
960 {
961 operation |= static_cast<uint8_t>(
962 IPMISensorReadingByte2::eventMessagesEnable);
963 }
964 return ipmi::responseSuccess(
965 getResponse.reading, operation,
966 getResponse.thresholdLevelsStates,
967 getResponse.discreteReadingSensorStates);
968 }
969 catch (const std::exception& e)
970 {
971 operation |= static_cast<uint8_t>(
972 IPMISensorReadingByte2::readingStateUnavailable);
973 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
974 }
975 }
976#endif
977
Willy Tude54f482021-01-26 15:59:09 -0800978 DbusInterfaceMap sensorMap;
979 if (!getSensorMap(ctx, connection, path, sensorMap))
980 {
981 return ipmi::responseResponseError();
982 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800983 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800984
985 if (sensorObject == sensorMap.end() ||
986 sensorObject->second.find("Value") == sensorObject->second.end())
987 {
988 return ipmi::responseResponseError();
989 }
990 auto& valueVariant = sensorObject->second["Value"];
991 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
992
993 double max = 0;
994 double min = 0;
995 getSensorMaxMin(sensorMap, max, min);
996
997 int16_t mValue = 0;
998 int16_t bValue = 0;
999 int8_t rExp = 0;
1000 int8_t bExp = 0;
1001 bool bSigned = false;
1002
1003 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1004 {
1005 return ipmi::responseResponseError();
1006 }
1007
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001008 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
1009 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -08001010 uint8_t operation =
1011 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
1012 operation |=
1013 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
1014 bool notReading = std::isnan(reading);
1015
1016 if (!notReading)
1017 {
1018 auto availableObject =
1019 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1020 if (availableObject != sensorMap.end())
1021 {
1022 auto findAvailable = availableObject->second.find("Available");
1023 if (findAvailable != availableObject->second.end())
1024 {
1025 bool* available = std::get_if<bool>(&(findAvailable->second));
1026 if (available && !(*available))
1027 {
1028 notReading = true;
1029 }
1030 }
1031 }
1032 }
1033
1034 if (notReading)
1035 {
1036 operation |= static_cast<uint8_t>(
1037 IPMISensorReadingByte2::readingStateUnavailable);
1038 }
1039
Josh Lehana55c9532020-10-28 21:59:06 -07001040 if constexpr (details::enableInstrumentation)
1041 {
1042 int byteValue;
1043 if (bSigned)
1044 {
1045 byteValue = static_cast<int>(static_cast<int8_t>(value));
1046 }
1047 else
1048 {
1049 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1050 }
1051
1052 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001053 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1054 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001055 {
1056 // This is the first reading, show the coefficients
1057 double step = (max - min) / 255.0;
1058 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001059 << details::sdrStatsTable.getName((ctx->lun << 8) |
1060 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -07001061 << ": Range min=" << min << " max=" << max
1062 << ", step=" << step
1063 << ", Coefficients mValue=" << static_cast<int>(mValue)
1064 << " rExp=" << static_cast<int>(rExp)
1065 << " bValue=" << static_cast<int>(bValue)
1066 << " bExp=" << static_cast<int>(bExp)
1067 << " bSigned=" << static_cast<int>(bSigned) << "\n";
1068 }
1069 }
1070
Willy Tude54f482021-01-26 15:59:09 -08001071 uint8_t thresholds = 0;
1072
1073 auto warningObject =
1074 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1075 if (warningObject != sensorMap.end())
1076 {
1077 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1078 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1079 if (alarmHigh != warningObject->second.end())
1080 {
1081 if (std::get<bool>(alarmHigh->second))
1082 {
1083 thresholds |= static_cast<uint8_t>(
1084 IPMISensorReadingByte3::upperNonCritical);
1085 }
1086 }
1087 if (alarmLow != warningObject->second.end())
1088 {
1089 if (std::get<bool>(alarmLow->second))
1090 {
1091 thresholds |= static_cast<uint8_t>(
1092 IPMISensorReadingByte3::lowerNonCritical);
1093 }
1094 }
1095 }
1096
1097 auto criticalObject =
1098 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1099 if (criticalObject != sensorMap.end())
1100 {
1101 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1102 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1103 if (alarmHigh != criticalObject->second.end())
1104 {
1105 if (std::get<bool>(alarmHigh->second))
1106 {
1107 thresholds |=
1108 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1109 }
1110 }
1111 if (alarmLow != criticalObject->second.end())
1112 {
1113 if (std::get<bool>(alarmLow->second))
1114 {
1115 thresholds |=
1116 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1117 }
1118 }
1119 }
1120
1121 // no discrete as of today so optional byte is never returned
1122 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1123}
1124
1125/** @brief implements the Set Sensor threshold command
1126 * @param sensorNumber - sensor number
1127 * @param lowerNonCriticalThreshMask
1128 * @param lowerCriticalThreshMask
1129 * @param lowerNonRecovThreshMask
1130 * @param upperNonCriticalThreshMask
1131 * @param upperCriticalThreshMask
1132 * @param upperNonRecovThreshMask
1133 * @param reserved
1134 * @param lowerNonCritical - lower non-critical threshold
1135 * @param lowerCritical - Lower critical threshold
1136 * @param lowerNonRecoverable - Lower non recovarable threshold
1137 * @param upperNonCritical - Upper non-critical threshold
1138 * @param upperCritical - Upper critical
1139 * @param upperNonRecoverable - Upper Non-recoverable
1140 *
1141 * @returns IPMI completion code
1142 */
1143ipmi::RspType<> ipmiSenSetSensorThresholds(
1144 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1145 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1146 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1147 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001148 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001149 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001150 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001151{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001152 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001153 {
1154 return ipmi::responseInvalidFieldRequest();
1155 }
1156
1157 // lower nc and upper nc not suppported on any sensor
1158 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1159 {
1160 return ipmi::responseInvalidFieldRequest();
1161 }
1162
1163 // if none of the threshold mask are set, nothing to do
1164 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1165 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1166 upperCriticalThreshMask | upperNonRecovThreshMask))
1167 {
1168 return ipmi::responseSuccess();
1169 }
1170
1171 std::string connection;
1172 std::string path;
1173
1174 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1175 if (status)
1176 {
1177 return ipmi::response(status);
1178 }
1179 DbusInterfaceMap sensorMap;
1180 if (!getSensorMap(ctx, connection, path, sensorMap))
1181 {
1182 return ipmi::responseResponseError();
1183 }
1184
1185 double max = 0;
1186 double min = 0;
1187 getSensorMaxMin(sensorMap, max, min);
1188
1189 int16_t mValue = 0;
1190 int16_t bValue = 0;
1191 int8_t rExp = 0;
1192 int8_t bExp = 0;
1193 bool bSigned = false;
1194
1195 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1196 {
1197 return ipmi::responseResponseError();
1198 }
1199
1200 // store a vector of property name, value to set, and interface
1201 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1202
1203 // define the indexes of the tuple
1204 constexpr uint8_t propertyName = 0;
1205 constexpr uint8_t thresholdValue = 1;
1206 constexpr uint8_t interface = 2;
1207 // verifiy all needed fields are present
1208 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1209 {
1210 auto findThreshold =
1211 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1212 if (findThreshold == sensorMap.end())
1213 {
1214 return ipmi::responseInvalidFieldRequest();
1215 }
1216 if (lowerCriticalThreshMask)
1217 {
1218 auto findLower = findThreshold->second.find("CriticalLow");
1219 if (findLower == findThreshold->second.end())
1220 {
1221 return ipmi::responseInvalidFieldRequest();
1222 }
1223 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1224 findThreshold->first);
1225 }
1226 if (upperCriticalThreshMask)
1227 {
1228 auto findUpper = findThreshold->second.find("CriticalHigh");
1229 if (findUpper == findThreshold->second.end())
1230 {
1231 return ipmi::responseInvalidFieldRequest();
1232 }
1233 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1234 findThreshold->first);
1235 }
1236 }
1237 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1238 {
1239 auto findThreshold =
1240 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1241 if (findThreshold == sensorMap.end())
1242 {
1243 return ipmi::responseInvalidFieldRequest();
1244 }
1245 if (lowerNonCriticalThreshMask)
1246 {
1247 auto findLower = findThreshold->second.find("WarningLow");
1248 if (findLower == findThreshold->second.end())
1249 {
1250 return ipmi::responseInvalidFieldRequest();
1251 }
1252 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1253 findThreshold->first);
1254 }
1255 if (upperNonCriticalThreshMask)
1256 {
1257 auto findUpper = findThreshold->second.find("WarningHigh");
1258 if (findUpper == findThreshold->second.end())
1259 {
1260 return ipmi::responseInvalidFieldRequest();
1261 }
1262 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1263 findThreshold->first);
1264 }
1265 }
1266 for (const auto& property : thresholdsToSet)
1267 {
1268 // from section 36.3 in the IPMI Spec, assume all linear
1269 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1270 (bValue * std::pow(10.0, bExp))) *
1271 std::pow(10.0, rExp);
1272 setDbusProperty(
1273 *getSdBus(), connection, path, std::get<interface>(property),
1274 std::get<propertyName>(property), ipmi::Value(valueToSet));
1275 }
1276 return ipmi::responseSuccess();
1277}
1278
1279IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1280{
1281 IPMIThresholds resp;
1282 auto warningInterface =
1283 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1284 auto criticalInterface =
1285 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1286
1287 if ((warningInterface != sensorMap.end()) ||
1288 (criticalInterface != sensorMap.end()))
1289 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001290 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001291
1292 if (sensorPair == sensorMap.end())
1293 {
1294 // should not have been able to find a sensor not implementing
1295 // the sensor object
1296 throw std::runtime_error("Invalid sensor map");
1297 }
1298
1299 double max = 0;
1300 double min = 0;
1301 getSensorMaxMin(sensorMap, max, min);
1302
1303 int16_t mValue = 0;
1304 int16_t bValue = 0;
1305 int8_t rExp = 0;
1306 int8_t bExp = 0;
1307 bool bSigned = false;
1308
1309 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1310 {
1311 throw std::runtime_error("Invalid sensor atrributes");
1312 }
1313 if (warningInterface != sensorMap.end())
1314 {
1315 auto& warningMap = warningInterface->second;
1316
1317 auto warningHigh = warningMap.find("WarningHigh");
1318 auto warningLow = warningMap.find("WarningLow");
1319
1320 if (warningHigh != warningMap.end())
1321 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001322 double value = std::visit(VariantToDoubleVisitor(),
1323 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001324 if (std::isfinite(value))
1325 {
1326 resp.warningHigh = scaleIPMIValueFromDouble(
1327 value, mValue, rExp, bValue, bExp, bSigned);
1328 }
Willy Tude54f482021-01-26 15:59:09 -08001329 }
1330 if (warningLow != warningMap.end())
1331 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001332 double value = std::visit(VariantToDoubleVisitor(),
1333 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001334 if (std::isfinite(value))
1335 {
1336 resp.warningLow = scaleIPMIValueFromDouble(
1337 value, mValue, rExp, bValue, bExp, bSigned);
1338 }
Willy Tude54f482021-01-26 15:59:09 -08001339 }
1340 }
1341 if (criticalInterface != sensorMap.end())
1342 {
1343 auto& criticalMap = criticalInterface->second;
1344
1345 auto criticalHigh = criticalMap.find("CriticalHigh");
1346 auto criticalLow = criticalMap.find("CriticalLow");
1347
1348 if (criticalHigh != criticalMap.end())
1349 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001350 double value = std::visit(VariantToDoubleVisitor(),
1351 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001352 if (std::isfinite(value))
1353 {
1354 resp.criticalHigh = scaleIPMIValueFromDouble(
1355 value, mValue, rExp, bValue, bExp, bSigned);
1356 }
Willy Tude54f482021-01-26 15:59:09 -08001357 }
1358 if (criticalLow != criticalMap.end())
1359 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001360 double value = std::visit(VariantToDoubleVisitor(),
1361 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001362 if (std::isfinite(value))
1363 {
1364 resp.criticalLow = scaleIPMIValueFromDouble(
1365 value, mValue, rExp, bValue, bExp, bSigned);
1366 }
Willy Tude54f482021-01-26 15:59:09 -08001367 }
1368 }
1369 }
1370 return resp;
1371}
1372
1373ipmi::RspType<uint8_t, // readable
1374 uint8_t, // lowerNCrit
1375 uint8_t, // lowerCrit
1376 uint8_t, // lowerNrecoverable
1377 uint8_t, // upperNC
1378 uint8_t, // upperCrit
1379 uint8_t> // upperNRecoverable
1380 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1381{
1382 std::string connection;
1383 std::string path;
1384
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001385 if (sensorNumber == reservedSensorNumber)
1386 {
1387 return ipmi::responseInvalidFieldRequest();
1388 }
1389
Willy Tude54f482021-01-26 15:59:09 -08001390 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1391 if (status)
1392 {
1393 return ipmi::response(status);
1394 }
1395
1396 DbusInterfaceMap sensorMap;
1397 if (!getSensorMap(ctx, connection, path, sensorMap))
1398 {
1399 return ipmi::responseResponseError();
1400 }
1401
1402 IPMIThresholds thresholdData;
1403 try
1404 {
1405 thresholdData = getIPMIThresholds(sensorMap);
1406 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001407 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001408 {
1409 return ipmi::responseResponseError();
1410 }
1411
1412 uint8_t readable = 0;
1413 uint8_t lowerNC = 0;
1414 uint8_t lowerCritical = 0;
1415 uint8_t lowerNonRecoverable = 0;
1416 uint8_t upperNC = 0;
1417 uint8_t upperCritical = 0;
1418 uint8_t upperNonRecoverable = 0;
1419
1420 if (thresholdData.warningHigh)
1421 {
1422 readable |=
1423 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1424 upperNC = *thresholdData.warningHigh;
1425 }
1426 if (thresholdData.warningLow)
1427 {
1428 readable |=
1429 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1430 lowerNC = *thresholdData.warningLow;
1431 }
1432
1433 if (thresholdData.criticalHigh)
1434 {
1435 readable |=
1436 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1437 upperCritical = *thresholdData.criticalHigh;
1438 }
1439 if (thresholdData.criticalLow)
1440 {
1441 readable |=
1442 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1443 lowerCritical = *thresholdData.criticalLow;
1444 }
1445
1446 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1447 lowerNonRecoverable, upperNC, upperCritical,
1448 upperNonRecoverable);
1449}
1450
1451/** @brief implements the get Sensor event enable command
1452 * @param sensorNumber - sensor number
1453 *
1454 * @returns IPMI completion code plus response data
1455 * - enabled - Sensor Event messages
1456 * - assertionEnabledLsb - Assertion event messages
1457 * - assertionEnabledMsb - Assertion event messages
1458 * - deassertionEnabledLsb - Deassertion event messages
1459 * - deassertionEnabledMsb - Deassertion event messages
1460 */
1461
1462ipmi::RspType<uint8_t, // enabled
1463 uint8_t, // assertionEnabledLsb
1464 uint8_t, // assertionEnabledMsb
1465 uint8_t, // deassertionEnabledLsb
1466 uint8_t> // deassertionEnabledMsb
1467 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1468{
1469 std::string connection;
1470 std::string path;
1471
1472 uint8_t enabled = 0;
1473 uint8_t assertionEnabledLsb = 0;
1474 uint8_t assertionEnabledMsb = 0;
1475 uint8_t deassertionEnabledLsb = 0;
1476 uint8_t deassertionEnabledMsb = 0;
1477
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001478 if (sensorNum == reservedSensorNumber)
1479 {
1480 return ipmi::responseInvalidFieldRequest();
1481 }
1482
Willy Tude54f482021-01-26 15:59:09 -08001483 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1484 if (status)
1485 {
1486 return ipmi::response(status);
1487 }
1488
Scron Chang2703b022021-07-06 15:47:45 +08001489#ifdef FEATURE_HYBRID_SENSORS
1490 if (auto sensor = findStaticSensor(path);
1491 sensor != ipmi::sensor::sensors.end() &&
1492 getSensorEventTypeFromPath(path) !=
1493 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1494 {
1495 enabled = static_cast<uint8_t>(
1496 IPMISensorEventEnableByte2::sensorScanningEnable);
1497 uint16_t assertionEnabled = 0;
1498 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1499 ->second.begin()
1500 ->second.second)
1501 {
1502 assertionEnabled |= (1 << offsetValMap.first);
1503 }
1504 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1505 assertionEnabledMsb =
1506 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1507
1508 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1509 assertionEnabledMsb, deassertionEnabledLsb,
1510 deassertionEnabledMsb);
1511 }
1512#endif
1513
Willy Tude54f482021-01-26 15:59:09 -08001514 DbusInterfaceMap sensorMap;
1515 if (!getSensorMap(ctx, connection, path, sensorMap))
1516 {
1517 return ipmi::responseResponseError();
1518 }
1519
1520 auto warningInterface =
1521 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1522 auto criticalInterface =
1523 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1524 if ((warningInterface != sensorMap.end()) ||
1525 (criticalInterface != sensorMap.end()))
1526 {
1527 enabled = static_cast<uint8_t>(
1528 IPMISensorEventEnableByte2::sensorScanningEnable);
1529 if (warningInterface != sensorMap.end())
1530 {
1531 auto& warningMap = warningInterface->second;
1532
1533 auto warningHigh = warningMap.find("WarningHigh");
1534 auto warningLow = warningMap.find("WarningLow");
1535 if (warningHigh != warningMap.end())
1536 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001537 double value = std::visit(VariantToDoubleVisitor(),
1538 warningHigh->second);
1539 if (std::isfinite(value))
1540 {
1541 assertionEnabledLsb |= static_cast<uint8_t>(
1542 IPMISensorEventEnableThresholds::
1543 upperNonCriticalGoingHigh);
1544 deassertionEnabledLsb |= static_cast<uint8_t>(
1545 IPMISensorEventEnableThresholds::
1546 upperNonCriticalGoingLow);
1547 }
Willy Tude54f482021-01-26 15:59:09 -08001548 }
1549 if (warningLow != warningMap.end())
1550 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001551 double value = std::visit(VariantToDoubleVisitor(),
1552 warningLow->second);
1553 if (std::isfinite(value))
1554 {
1555 assertionEnabledLsb |= static_cast<uint8_t>(
1556 IPMISensorEventEnableThresholds::
1557 lowerNonCriticalGoingLow);
1558 deassertionEnabledLsb |= static_cast<uint8_t>(
1559 IPMISensorEventEnableThresholds::
1560 lowerNonCriticalGoingHigh);
1561 }
Willy Tude54f482021-01-26 15:59:09 -08001562 }
1563 }
1564 if (criticalInterface != sensorMap.end())
1565 {
1566 auto& criticalMap = criticalInterface->second;
1567
1568 auto criticalHigh = criticalMap.find("CriticalHigh");
1569 auto criticalLow = criticalMap.find("CriticalLow");
1570
1571 if (criticalHigh != criticalMap.end())
1572 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001573 double value = std::visit(VariantToDoubleVisitor(),
1574 criticalHigh->second);
1575 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 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001586 double value = std::visit(VariantToDoubleVisitor(),
1587 criticalLow->second);
1588 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 {
1629 phosphor::logging::log<phosphor::logging::level::ERR>(
1630 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1631 phosphor::logging::entry("SENSOR=%d", sensorNum));
1632 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;
1646 uint8_t sensorEventStatus;
1647 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 {
1672 phosphor::logging::log<phosphor::logging::level::ERR>(
1673 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1674 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1675 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
Willy Tude54f482021-01-26 15:59:09 -08001699 auto warningInterface =
1700 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1701 auto criticalInterface =
1702 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1703
Willy Tude54f482021-01-26 15:59:09 -08001704 std::optional<bool> criticalDeassertHigh =
1705 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1706 std::optional<bool> criticalDeassertLow =
1707 thresholdDeassertMap[path]["CriticalAlarmLow"];
1708 std::optional<bool> warningDeassertHigh =
1709 thresholdDeassertMap[path]["WarningAlarmHigh"];
1710 std::optional<bool> warningDeassertLow =
1711 thresholdDeassertMap[path]["WarningAlarmLow"];
1712
Willy Tude54f482021-01-26 15:59:09 -08001713 if (criticalDeassertHigh && !*criticalDeassertHigh)
1714 {
1715 deassertions.set(static_cast<size_t>(
1716 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1717 }
1718 if (criticalDeassertLow && !*criticalDeassertLow)
1719 {
1720 deassertions.set(static_cast<size_t>(
1721 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1722 }
1723 if (warningDeassertHigh && !*warningDeassertHigh)
1724 {
1725 deassertions.set(static_cast<size_t>(
1726 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1727 }
1728 if (warningDeassertLow && !*warningDeassertLow)
1729 {
1730 deassertions.set(static_cast<size_t>(
1731 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1732 }
1733 if ((warningInterface != sensorMap.end()) ||
1734 (criticalInterface != sensorMap.end()))
1735 {
1736 sensorEventStatus = static_cast<size_t>(
1737 IPMISensorEventEnableByte2::eventMessagesEnable);
1738 if (warningInterface != sensorMap.end())
1739 {
1740 auto& warningMap = warningInterface->second;
1741
1742 auto warningHigh = warningMap.find("WarningAlarmHigh");
1743 auto warningLow = warningMap.find("WarningAlarmLow");
1744 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 {
1757 assertions.set(
1758 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1759 upperNonCriticalGoingHigh));
1760 }
1761 if (warningLowAlarm)
1762 {
1763 assertions.set(
1764 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1765 lowerNonCriticalGoingLow));
1766 }
1767 }
1768 if (criticalInterface != sensorMap.end())
1769 {
1770 auto& criticalMap = criticalInterface->second;
1771
1772 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1773 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1774 auto criticalHighAlarm = false;
1775 auto criticalLowAlarm = false;
1776
1777 if (criticalHigh != criticalMap.end())
1778 {
1779 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1780 }
1781 if (criticalLow != criticalMap.end())
1782 {
1783 criticalLowAlarm = std::get<bool>(criticalLow->second);
1784 }
1785 if (criticalHighAlarm)
1786 {
1787 assertions.set(
1788 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1789 upperCriticalGoingHigh));
1790 }
1791 if (criticalLowAlarm)
1792 {
1793 assertions.set(static_cast<size_t>(
1794 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1795 }
1796 }
1797 }
1798
1799 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1800}
1801
Willy Tu38e7a2b2021-03-29 15:09:56 -07001802// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001803void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1804 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001805{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001806 get_sdr::header::set_record_id(
1807 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1808
Willy Tu38e7a2b2021-03-29 15:09:56 -07001809 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1810 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1811
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001812 record.header.sdr_version = ipmiSdrVersion;
1813 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1814 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1815 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001816 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001817 record.key.owner_lun = lun;
1818 record.key.sensor_number = 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 {
1831 phosphor::logging::log<phosphor::logging::level::ERR>(
1832 "Failed to update sensor map for threshold sensor",
1833 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1834 phosphor::logging::entry("PATH=%s", path.c_str()));
1835 return false;
1836 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001837
1838 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1839 record.body.sensor_type = getSensorTypeFromPath(path);
1840 std::string type = getSensorTypeStringFromPath(path);
1841 auto typeCstr = type.c_str();
1842 auto findUnits = sensorUnits.find(typeCstr);
1843 if (findUnits != sensorUnits.end())
1844 {
1845 record.body.sensor_units_2_base =
1846 static_cast<uint8_t>(findUnits->second);
1847 } // else default 0x0 unspecified
1848
1849 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1850
Hao Jiangd2afd052020-12-10 15:09:32 -08001851 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001852 if (sensorObject == sensorMap.end())
1853 {
1854 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001855 "constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001856 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001857 }
1858
1859 uint8_t entityId = 0;
1860 uint8_t entityInstance = 0x01;
1861
1862 // follow the association chain to get the parent board's entityid and
1863 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001864 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1865 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001866
1867 record.body.entity_id = entityId;
1868 record.body.entity_instance = entityInstance;
1869
Shakeeb Pasha93889722021-10-14 10:20:13 +05301870 double max = 0;
1871 double min = 0;
1872 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001873
1874 int16_t mValue = 0;
1875 int8_t rExp = 0;
1876 int16_t bValue = 0;
1877 int8_t bExp = 0;
1878 bool bSigned = false;
1879
1880 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1881 {
1882 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001883 "constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001884 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001885 }
1886
1887 // The record.body is a struct SensorDataFullRecordBody
1888 // from sensorhandler.hpp in phosphor-ipmi-host.
1889 // The meaning of these bits appears to come from
1890 // table 43.1 of the IPMI spec.
1891 // The above 5 sensor attributes are stuffed in as follows:
1892 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1893 // Byte 22-24 are for other purposes
1894 // Byte 25 = MMMMMMMM = LSB of M
1895 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1896 // Byte 27 = BBBBBBBB = LSB of B
1897 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1898 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1899 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1900
1901 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1902 record.body.m_lsb = mValue & 0xFF;
1903
1904 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1905 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1906
1907 // move the smallest bit of the MSB into place (bit 9)
1908 // the MSbs are bits 7:8 in m_msb_and_tolerance
1909 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1910
1911 record.body.b_lsb = bValue & 0xFF;
1912
1913 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1914 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1915
1916 // move the smallest bit of the MSB into place (bit 9)
1917 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1918 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1919
1920 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1921 uint8_t rExpBits = rExp & 0x07;
1922
1923 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1924 uint8_t bExpBits = bExp & 0x07;
1925
1926 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001927 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1928 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001929
1930 // Set the analog reading byte interpretation accordingly
1931 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1932
1933 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1934 // These seem redundant, but derivable from the above 5 attributes
1935 // Original comment said "todo fill out rest of units"
1936
1937 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001938 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001939 get_sdr::body::set_id_strlen(name.size(), &record.body);
1940 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001941 std::memcpy(record.body.id_string, name.c_str(),
1942 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001943
Josh Lehana55c9532020-10-28 21:59:06 -07001944 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001945 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001946
Jie Yangf0a89942021-07-29 15:30:25 -07001947 bool sensorSettable = false;
1948 auto mutability =
1949 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1950 if (mutability != sensorMap.end())
1951 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001952 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1953 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001954 }
1955 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1956
1957 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001958 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001959
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001960 IPMIThresholds thresholdData;
1961 try
1962 {
1963 thresholdData = getIPMIThresholds(sensorMap);
1964 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001965 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001966 {
1967 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001968 "constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001969 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001970 }
1971
1972 if (thresholdData.criticalHigh)
1973 {
1974 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1975 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1976 IPMISensorEventEnableThresholds::criticalThreshold);
1977 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1978 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1979 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1980 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1981 record.body.discrete_reading_setting_mask[0] |=
1982 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1983 }
1984 if (thresholdData.warningHigh)
1985 {
1986 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1987 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1988 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1989 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1990 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1991 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1992 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1993 record.body.discrete_reading_setting_mask[0] |=
1994 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1995 }
1996 if (thresholdData.criticalLow)
1997 {
1998 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1999 record.body.supported_assertions[1] |= static_cast<uint8_t>(
2000 IPMISensorEventEnableThresholds::criticalThreshold);
2001 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
2002 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
2003 record.body.supported_assertions[0] |= static_cast<uint8_t>(
2004 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
2005 record.body.discrete_reading_setting_mask[0] |=
2006 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
2007 }
2008 if (thresholdData.warningLow)
2009 {
2010 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
2011 record.body.supported_assertions[1] |= static_cast<uint8_t>(
2012 IPMISensorEventEnableThresholds::nonCriticalThreshold);
2013 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
2014 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2015 record.body.supported_assertions[0] |= static_cast<uint8_t>(
2016 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2017 record.body.discrete_reading_setting_mask[0] |=
2018 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
2019 }
2020
2021 // everything that is readable is setable
2022 record.body.discrete_reading_setting_mask[1] =
2023 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07002024 return true;
2025}
2026
Scron Chang2703b022021-07-06 15:47:45 +08002027#ifdef FEATURE_HYBRID_SENSORS
2028// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08002029void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08002030 uint16_t recordID,
2031 ipmi::sensor::IdInfoMap::const_iterator sensor,
2032 get_sdr::SensorDataFullRecord& record)
2033{
2034 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2035
2036 record.body.entity_id = sensor->second.entityType;
2037 record.body.sensor_type = sensor->second.sensorType;
2038 record.body.event_reading_type = sensor->second.sensorReadingType;
2039 record.body.entity_instance = sensor->second.instance;
2040 if (ipmi::sensor::Mutability::Write ==
2041 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2042 {
2043 get_sdr::body::init_settable_state(true, &(record.body));
2044 }
2045
2046 auto id_string = sensor->second.sensorName;
2047
2048 if (id_string.empty())
2049 {
2050 id_string = sensor->second.sensorNameFunc(sensor->second);
2051 }
2052
2053 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2054 {
2055 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2056 &(record.body));
2057 }
2058 else
2059 {
2060 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2061 }
Paul Fertser51136982022-08-18 12:36:41 +00002062 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002063 std::strncpy(record.body.id_string, id_string.c_str(),
2064 get_sdr::body::get_id_strlen(&(record.body)));
2065}
2066#endif
2067
Hao Jiange39d4d82021-04-16 17:02:40 -07002068// Construct type 3 SDR header and key (for VR and other discrete sensors)
2069void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2070 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002071{
2072 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2073 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2074
2075 get_sdr::header::set_record_id(
2076 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2077
2078 record.header.sdr_version = ipmiSdrVersion;
2079 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2080 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2081 sizeof(get_sdr::SensorDataRecordHeader);
2082 record.key.owner_id = bmcI2CAddr;
2083 record.key.owner_lun = lun;
2084 record.key.sensor_number = sensornumber;
2085
2086 record.body.entity_id = 0x00;
2087 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002088}
Willy Tu61992ad2021-03-29 15:33:20 -07002089
Hao Jiange39d4d82021-04-16 17:02:40 -07002090// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002091bool constructVrSdr(ipmi::Context::ptr ctx,
2092 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2093 uint16_t sensorNum, uint16_t recordID,
2094 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002095 get_sdr::SensorDataEventRecord& record)
2096{
Hao Jiange39d4d82021-04-16 17:02:40 -07002097 constructEventSdrHeaderKey(sensorNum, recordID, record);
2098
2099 DbusInterfaceMap sensorMap;
2100 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2101 {
2102 phosphor::logging::log<phosphor::logging::level::ERR>(
2103 "Failed to update sensor map for VR sensor",
2104 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2105 phosphor::logging::entry("PATH=%s", path.c_str()));
2106 return false;
2107 }
Willy Tu61992ad2021-03-29 15:33:20 -07002108 // follow the association chain to get the parent board's entityid and
2109 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002110 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2111 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002112 record.body.entity_instance);
2113
2114 // Sensor type is hardcoded as a module/board type instead of parsing from
2115 // sensor path. This is because VR control is allocated in an independent
2116 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2117 // types.
2118 static constexpr const uint8_t module_board_type = 0x15;
2119 record.body.sensor_type = module_board_type;
2120 record.body.event_reading_type = 0x00;
2121
2122 record.body.sensor_record_sharing_1 = 0x00;
2123 record.body.sensor_record_sharing_2 = 0x00;
2124
2125 // populate sensor name from path
2126 auto name = sensor::parseSdrIdFromPath(path);
2127 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002128 get_sdr::body::set_id_strlen(nameSize, &record.body);
2129 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002130 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2131 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2132
2133 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002134 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002135
2136 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002137}
2138
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002139uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002140{
2141 return std::min(getSensorTree().size(), maxIPMISensors);
2142}
2143
Willy Tu4eca2512022-06-20 21:14:51 -07002144static int getSensorDataRecord(
2145 ipmi::Context::ptr ctx,
2146 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2147 std::vector<uint8_t>& recordData, uint16_t recordID,
2148 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002149{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302150 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002151 size_t lastRecord = ipmi::getNumberOfSensors() +
2152 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2153 uint16_t nextRecord(recordID + 1);
2154
Willy Tu38e7a2b2021-03-29 15:09:56 -07002155 if (recordID == lastRecordIndex)
2156 {
2157 recordID = lastRecord;
2158 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002159 if (recordID == lastRecord)
2160 {
2161 nextRecord = lastRecordIndex;
2162 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002163 if (recordID > lastRecord)
2164 {
2165 phosphor::logging::log<phosphor::logging::level::ERR>(
2166 "getSensorDataRecord: recordID > lastRecord error");
2167 return GENERAL_ERROR;
2168 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002169 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002170 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002171 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2172 recordData);
2173 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002174 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002175 // phosphor::logging::log<phosphor::logging::level::ERR>(
2176 // "getSensorDataRecord: Error getting custom record");
2177 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002178 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002179 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002180 }
2181
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002182 // Perform a incremental scan of the SDR Record ID's and translate the
2183 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2184 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2185 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2186 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002187 std::string connection;
2188 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002189 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002190 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002191 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002192 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002193 // LUN 0 has one reserved sensor number. Compensate here by adding one
2194 // to the record ID
2195 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002196 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002197 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002198 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002199 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002200 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2201 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2202 // rules governing its use.
2203 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002204 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002205 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002206
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002207 auto status = getSensorConnection(ctx,
2208 static_cast<uint8_t>(sensNumFromRecID),
2209 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002210 if (status)
2211 {
2212 phosphor::logging::log<phosphor::logging::level::ERR>(
2213 "getSensorDataRecord: getSensorConnection error");
2214 return GENERAL_ERROR;
2215 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002216 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002217 // Return an error on LUN 2 assingments, and any sensor number beyond the
2218 // range of LUN 3
2219 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2220 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002221 {
2222 phosphor::logging::log<phosphor::logging::level::ERR>(
2223 "getSensorDataRecord: invalidSensorNumber");
2224 return GENERAL_ERROR;
2225 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002226 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2227 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2228
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002229 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2230 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002231 {
2232 phosphor::logging::log<phosphor::logging::level::ERR>(
2233 "getSensorDataRecord: sensor record mismatch");
2234 return GENERAL_ERROR;
2235 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002236
Willy Tu38e7a2b2021-03-29 15:09:56 -07002237 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002238 if (std::find(interfaces.begin(), interfaces.end(),
2239 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002240 {
Willy Tu11d68892022-01-20 10:37:34 -08002241 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002242
Hao Jiange39d4d82021-04-16 17:02:40 -07002243 // If the request doesn't read SDR body, construct only header and key
2244 // part to avoid additional DBus transaction.
2245 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2246 {
2247 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2248 }
Willy Tu4eca2512022-06-20 21:14:51 -07002249 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2250 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002251 {
2252 return GENERAL_ERROR;
2253 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002254
selvaganapathi7b2e5502023-02-14 07:10:47 +05302255 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2256 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002257
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002258 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002259 }
Willy Tu61992ad2021-03-29 15:33:20 -07002260
Scron Chang2703b022021-07-06 15:47:45 +08002261#ifdef FEATURE_HYBRID_SENSORS
2262 if (auto sensor = findStaticSensor(path);
2263 sensor != ipmi::sensor::sensors.end() &&
2264 getSensorEventTypeFromPath(path) !=
2265 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2266 {
Willy Tu11d68892022-01-20 10:37:34 -08002267 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002268
2269 // If the request doesn't read SDR body, construct only header and key
2270 // part to avoid additional DBus transaction.
2271 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2272 {
2273 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2274 }
2275 else
2276 {
2277 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2278 }
2279
selvaganapathi7b2e5502023-02-14 07:10:47 +05302280 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2281 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002282
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002283 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002284 }
2285#endif
2286
Willy Tu61992ad2021-03-29 15:33:20 -07002287 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002288 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2289 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002290 {
Willy Tu11d68892022-01-20 10:37:34 -08002291 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002292
Hao Jiange39d4d82021-04-16 17:02:40 -07002293 // If the request doesn't read SDR body, construct only header and key
2294 // part to avoid additional DBus transaction.
2295 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2296 {
2297 constructEventSdrHeaderKey(sensorNum, recordID, record);
2298 }
Willy Tu4eca2512022-06-20 21:14:51 -07002299 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2300 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002301 {
2302 return GENERAL_ERROR;
2303 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302304 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2305 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002306 }
2307
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002308 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002309}
2310
2311/** @brief implements the get SDR Info command
2312 * @param count - Operation
2313 *
2314 * @returns IPMI completion code plus response data
2315 * - sdrCount - sensor/SDR count
2316 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2317 */
2318static ipmi::RspType<uint8_t, // respcount
2319 uint8_t, // dynamic population flags
2320 uint32_t // last time a sensor was added
2321 >
2322 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2323 std::optional<uint8_t> count)
2324{
2325 auto& sensorTree = getSensorTree();
2326 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002327 uint16_t recordID = 0;
2328 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002329 // Sensors are dynamically allocated, and there is at least one LUN
2330 uint8_t lunsAndDynamicPopulation = 0x80;
2331 constexpr uint8_t getSdrCount = 0x01;
2332 constexpr uint8_t getSensorCount = 0x00;
2333
2334 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2335 {
2336 return ipmi::responseResponseError();
2337 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002338 uint16_t numSensors = ipmi::getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002339 if (count.value_or(0) == getSdrCount)
2340 {
Willy Tu4eca2512022-06-20 21:14:51 -07002341 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2342
Harvey Wu75893062023-03-22 17:17:31 +08002343 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002344 {
2345 recordID += maxSensorsPerLUN;
2346 }
Harvey Wu75893062023-03-22 17:17:31 +08002347 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002348 {
2349 recordID += maxSensorsPerLUN * 2;
2350 }
2351
Harvey Wu75893062023-03-22 17:17:31 +08002352 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2353 // assigned to the LUN
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002354 while (getSensorDataRecord(ctx,
2355 ipmiDecoratorPaths.value_or(
2356 std::unordered_set<std::string>()),
2357 record, recordID++) >= 0)
Willy Tude54f482021-01-26 15:59:09 -08002358 {
2359 get_sdr::SensorDataRecordHeader* hdr =
2360 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002361 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302362 if (!hdr)
2363 {
2364 continue;
2365 }
2366
2367 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002368 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002369 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002370 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002371 record.data());
2372 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002373 {
2374 sdrCount++;
2375 }
2376 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302377 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2378 {
2379 get_sdr::SensorDataCompactRecord* recordData =
2380 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2381 record.data());
2382 if (ctx->lun == recordData->key.owner_lun)
2383 {
2384 sdrCount++;
2385 }
2386 }
Harvey Wua3476272023-03-22 10:09:38 +08002387 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2388 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302389 {
2390 sdrCount++;
2391 }
Harvey Wua3476272023-03-22 10:09:38 +08002392
2393 // Because response count data is 1 byte, so sdrCount need to avoid
2394 // overflow.
2395 if (sdrCount == maxSensorsPerLUN)
2396 {
2397 break;
2398 }
Willy Tude54f482021-01-26 15:59:09 -08002399 }
2400 }
2401 else if (count.value_or(0) == getSensorCount)
2402 {
2403 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002404 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002405 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002406 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2407 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002408 }
Harvey Wu75893062023-03-22 17:17:31 +08002409 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002410 {
2411 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2412 ? maxSensorsPerLUN
2413 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2414 }
Harvey Wu75893062023-03-22 17:17:31 +08002415 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002416 {
2417 if (numSensors <= maxIPMISensors)
2418 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002419 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2420 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002421 }
2422 else
2423 {
2424 // error
2425 throw std::out_of_range(
2426 "Maximum number of IPMI sensors exceeded.");
2427 }
2428 }
2429 }
2430 else
2431 {
2432 return ipmi::responseInvalidFieldRequest();
2433 }
2434
2435 // Get Sensor count. This returns the number of sensors
2436 if (numSensors > 0)
2437 {
2438 lunsAndDynamicPopulation |= 1;
2439 }
2440 if (numSensors > maxSensorsPerLUN)
2441 {
2442 lunsAndDynamicPopulation |= 2;
2443 }
2444 if (numSensors >= (maxSensorsPerLUN * 2))
2445 {
2446 lunsAndDynamicPopulation |= 8;
2447 }
2448 if (numSensors > maxIPMISensors)
2449 {
2450 // error
2451 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2452 }
2453
2454 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2455 sdrLastAdd);
2456}
2457
2458/* end sensor commands */
2459
2460/* storage commands */
2461
2462ipmi::RspType<uint8_t, // sdr version
2463 uint16_t, // record count
2464 uint16_t, // free space
2465 uint32_t, // most recent addition
2466 uint32_t, // most recent erase
2467 uint8_t // operationSupport
2468 >
2469 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2470{
Willy Tude54f482021-01-26 15:59:09 -08002471 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Johnathan Mantey31c1ecd2024-06-20 11:10:41 -07002472 uint16_t recordCount = ipmi::getNumberOfSensors() +
2473 ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002474
2475 uint8_t operationSupport = static_cast<uint8_t>(
2476 SdrRepositoryInfoOps::overflow); // write not supported
2477
2478 operationSupport |=
2479 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2480 operationSupport |= static_cast<uint8_t>(
2481 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2482 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2483 unspecifiedFreeSpace, sdrLastAdd,
2484 sdrLastRemove, operationSupport);
2485}
2486
2487/** @brief implements the get SDR allocation info command
2488 *
2489 * @returns IPMI completion code plus response data
2490 * - allocUnits - Number of possible allocation units
2491 * - allocUnitSize - Allocation unit size in bytes.
2492 * - allocUnitFree - Number of free allocation units
2493 * - allocUnitLargestFree - Largest free block in allocation units
2494 * - maxRecordSize - Maximum record size in allocation units.
2495 */
2496ipmi::RspType<uint16_t, // allocUnits
2497 uint16_t, // allocUnitSize
2498 uint16_t, // allocUnitFree
2499 uint16_t, // allocUnitLargestFree
2500 uint8_t // maxRecordSize
2501 >
2502 ipmiStorageGetSDRAllocationInfo()
2503{
2504 // 0000h unspecified number of alloc units
2505 constexpr uint16_t allocUnits = 0;
2506
2507 constexpr uint16_t allocUnitFree = 0;
2508 constexpr uint16_t allocUnitLargestFree = 0;
2509 // only allow one block at a time
2510 constexpr uint8_t maxRecordSize = 1;
2511
2512 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2513 allocUnitLargestFree, maxRecordSize);
2514}
2515
2516/** @brief implements the reserve SDR command
2517 * @returns IPMI completion code plus response data
2518 * - sdrReservationID
2519 */
2520ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2521{
2522 sdrReservationID++;
2523 if (sdrReservationID == 0)
2524 {
2525 sdrReservationID++;
2526 }
2527
2528 return ipmi::responseSuccess(sdrReservationID);
2529}
2530
2531ipmi::RspType<uint16_t, // next record ID
2532 std::vector<uint8_t> // payload
2533 >
2534 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2535 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2536{
2537 // reservation required for partial reads with non zero offset into
2538 // record
2539 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2540 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002541 phosphor::logging::log<phosphor::logging::level::ERR>(
2542 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002543 return ipmi::responseInvalidReservationId();
2544 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002545
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002546 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002547 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002548 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002549 phosphor::logging::log<phosphor::logging::level::ERR>(
2550 "ipmiStorageGetSDR: getSensorSubtree error");
2551 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002552 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002553
Willy Tu4eca2512022-06-20 21:14:51 -07002554 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2555
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002556 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002557 int nextRecordId = getSensorDataRecord(
2558 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2559 record, recordID, offset + bytesToRead);
2560
2561 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002562 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002563 phosphor::logging::log<phosphor::logging::level::ERR>(
2564 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002565 return ipmi::responseInvalidFieldRequest();
2566 }
Willy Tude54f482021-01-26 15:59:09 -08002567 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002568 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002569 if (!hdr)
2570 {
2571 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002572 "ipmiStorageGetSDR: record header is null");
2573 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002574 }
2575
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002576 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2577 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002578 if (offset >= sdrLength)
2579 {
2580 phosphor::logging::log<phosphor::logging::level::ERR>(
2581 "ipmiStorageGetSDR: offset is outside the record");
2582 return ipmi::responseParmOutOfRange();
2583 }
Willy Tude54f482021-01-26 15:59:09 -08002584 if (sdrLength < (offset + bytesToRead))
2585 {
2586 bytesToRead = sdrLength - offset;
2587 }
2588
2589 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2590 if (!respStart)
2591 {
2592 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002593 "ipmiStorageGetSDR: record is null");
2594 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002595 }
2596
2597 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002598
Willy Tude54f482021-01-26 15:59:09 -08002599 return ipmi::responseSuccess(nextRecordId, recordData);
2600}
adarshgrami042e9db2022-09-15 10:34:34 +05302601namespace dcmi
2602{
2603
Thang Tranb1416ef2023-08-02 13:57:09 +07002604std::tuple<uint8_t, // Total of instance sensors
2605 std::vector<sensorInfo> // The list of sensors
2606 >
2607 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2608 uint8_t entityInstance, uint8_t instanceStart)
2609{
2610 std::vector<sensorInfo> sensorList;
2611 uint8_t totalInstSensor = 0;
2612 auto match = ipmi::dcmi::validEntityId.find(entityId);
2613
2614 if (match == ipmi::dcmi::validEntityId.end())
2615 {
2616 return std::make_tuple(totalInstSensor, sensorList);
2617 }
2618
2619 auto& sensorTree = getSensorTree();
2620 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2621 {
2622 return std::make_tuple(totalInstSensor, sensorList);
2623 }
2624
2625 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2626
Willy Tu62e3ca82024-01-31 17:28:34 +00002627 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002628 for (const auto& sensor : sensorTree)
2629 {
2630 const std::string& sensorObjPath = sensor.first;
2631 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2632
2633 /*
2634 * In the DCMI specification, it only supports the sensor type is 0x01
2635 * (temperature type) for both Get Sensor Info and Get Temperature
2636 * Readings commands.
2637 */
2638 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2639 {
2640 continue;
2641 }
2642
2643 const auto& connection = sensor.second.begin()->first;
2644 DbusInterfaceMap sensorMap;
2645
2646 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2647 sensorMapSdrUpdatePeriod))
2648 {
2649 phosphor::logging::log<phosphor::logging::level::ERR>(
2650 "Failed to update sensor map for threshold sensor",
2651 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2652 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2653 continue;
2654 }
2655
2656 uint8_t entityIdValue = 0;
2657 uint8_t entityInstanceValue = 0;
2658
2659 /*
2660 * Get the Entity ID, Entity Instance information which are configured
2661 * in the Entity-Manger.
2662 */
2663 updateIpmiFromAssociation(
2664 sensorObjPath,
2665 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2666 sensorMap, entityIdValue, entityInstanceValue);
2667
2668 if (entityIdValue == match->first || entityIdValue == match->second)
2669 {
2670 totalInstSensor++;
2671
2672 /*
2673 * When Entity Instance parameter is not 0, we only get the first
2674 * sensor whose Entity Instance number is equal input Entity
2675 * Instance parameter.
2676 */
2677 if (entityInstance)
2678 {
2679 if (!sensorList.empty())
2680 {
2681 continue;
2682 }
2683
2684 if (entityInstanceValue == entityInstance)
2685 {
2686 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002687 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002688 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002689 ++invalidSensorNumberErrCount;
2690 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002691 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002692 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2693 recordId, entityIdValue,
2694 entityInstanceValue);
2695 }
2696 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002697 else if (entityInstanceValue >= instanceStart)
2698 {
2699 auto recordId = getSensorNumberFromPath(sensorObjPath);
2700 if (recordId == invalidSensorNumber)
2701 {
2702 ++invalidSensorNumberErrCount;
2703 continue;
2704 }
2705 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2706 recordId, entityIdValue,
2707 entityInstanceValue);
2708 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002709 }
2710 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002711 if (invalidSensorNumberErrCount != 0)
2712 {
2713 phosphor::logging::log<phosphor::logging::level::ERR>(
2714 std::format(
2715 "getSensorNumberFromPath returned invalidSensorNumber {} times",
2716 invalidSensorNumberErrCount)
2717 .data());
2718 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002719
2720 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2721 return first.entityInstance <= second.entityInstance;
2722 };
2723
2724 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2725
2726 return std::make_tuple(totalInstSensor, sensorList);
2727}
2728
Thang Tran3dad8262023-08-17 15:20:56 +07002729std::tuple<bool, // Reading result
2730 uint7_t, // Temp value
2731 bool> // Sign bit
2732 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2733{
2734 std::string service{};
2735 boost::system::error_code ec =
2736 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2737 if (ec.value())
2738 {
2739 return std::make_tuple(false, 0, false);
2740 }
2741
2742 ipmi::PropertyMap properties{};
2743 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2744 sensor::sensorInterface, properties);
2745 if (ec.value())
2746 {
2747 return std::make_tuple(false, 0, false);
2748 }
2749
2750 auto scaleIt = properties.find("Scale");
2751 double scaleVal = 0.0;
2752 if (scaleIt != properties.end())
2753 {
2754 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2755 }
2756
2757 auto tempValIt = properties.find("Value");
2758 double tempVal = 0.0;
2759 if (tempValIt == properties.end())
2760 {
2761 return std::make_tuple(false, 0, false);
2762 }
2763
2764 const double maxTemp = 127;
2765 double absTempVal = 0.0;
2766 bool signBit = false;
2767
2768 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2769 tempVal = std::pow(10, scaleVal) * tempVal;
2770 absTempVal = std::abs(tempVal);
2771 absTempVal = std::min(absTempVal, maxTemp);
2772 signBit = (tempVal < 0) ? true : false;
2773
2774 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2775}
2776
adarshgrami042e9db2022-09-15 10:34:34 +05302777ipmi::RspType<uint8_t, // No of instances for requested id
2778 uint8_t, // No of record ids in the response
2779 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2780 // IDs
2781 >
2782 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002783 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302784{
2785 auto match = ipmi::dcmi::validEntityId.find(entityId);
2786 if (match == ipmi::dcmi::validEntityId.end())
2787 {
2788 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2789
2790 return ipmi::responseInvalidFieldRequest();
2791 }
2792
2793 if (sensorType != ipmi::dcmi::temperatureSensorType)
2794 {
2795 log<level::ERR>("Invalid sensor type",
2796 entry("SENSOR_TYPE=%d", sensorType));
2797
2798 return ipmi::responseInvalidFieldRequest();
2799 }
adarshgrami042e9db2022-09-15 10:34:34 +05302800
2801 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002802 const auto& [totalSensorInst, sensorList] =
2803 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302804
Thang Tranb1416ef2023-08-02 13:57:09 +07002805 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302806 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002807 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2808 }
adarshgrami042e9db2022-09-15 10:34:34 +05302809
Thang Tranb1416ef2023-08-02 13:57:09 +07002810 /*
2811 * As DCMI specification, the maximum number of Record Ids of response data
2812 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2813 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2814 * in response data.
2815 */
2816 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2817
2818 for (const auto& sensor : sensorList)
2819 {
2820 sensorRec.emplace_back(sensor.recordId);
2821 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302822 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002823 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302824 }
2825 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002826
2827 return ipmi::responseSuccess(
2828 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302829}
Thang Tran3dad8262023-08-17 15:20:56 +07002830
2831ipmi::RspType<uint8_t, // No of instances for requested id
2832 uint8_t, // No of record ids in the response
2833 std::vector< // Temperature Data
2834 std::tuple<uint7_t, // Temperature value
2835 bool, // Sign bit
2836 uint8_t // Entity Instance of sensor
2837 >>>
2838 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2839 uint8_t entityId, uint8_t entityInstance,
2840 uint8_t instanceStart)
2841{
2842 auto match = ipmi::dcmi::validEntityId.find(entityId);
2843 if (match == ipmi::dcmi::validEntityId.end())
2844 {
2845 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2846
2847 return ipmi::responseInvalidFieldRequest();
2848 }
2849
2850 if (sensorType != ipmi::dcmi::temperatureSensorType)
2851 {
2852 log<level::ERR>("Invalid sensor type",
2853 entry("SENSOR_TYPE=%d", sensorType));
2854
2855 return ipmi::responseInvalidFieldRequest();
2856 }
2857
2858 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2859 const auto& [totalSensorInst, sensorList] =
2860 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2861
2862 if (sensorList.empty())
2863 {
2864 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2865 }
2866
2867 /*
2868 * As DCMI specification, the maximum number of Record Ids of response data
2869 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2870 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2871 * in response data.
2872 */
2873 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2874
2875 for (const auto& sensor : sensorList)
2876 {
2877 const auto& [readResult, tempVal,
2878 signBit] = readTemp(ctx, sensor.objectPath);
2879
2880 if (readResult)
2881 {
2882 tempReadingVal.emplace_back(
2883 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2884
2885 if (tempReadingVal.size() >= numOfRec)
2886 {
2887 break;
2888 }
2889 }
2890 }
2891
2892 return ipmi::responseSuccess(totalSensorInst,
2893 static_cast<uint8_t>(tempReadingVal.size()),
2894 tempReadingVal);
2895}
2896
adarshgrami042e9db2022-09-15 10:34:34 +05302897} // namespace dcmi
2898
Willy Tude54f482021-01-26 15:59:09 -08002899/* end storage commands */
2900
2901void registerSensorFunctions()
2902{
2903 // <Platform Event>
2904 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2905 ipmi::sensor_event::cmdPlatformEvent,
2906 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2907
Willy Tudbafbce2021-03-29 00:37:05 -07002908 // <Set Sensor Reading and Event Status>
2909 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2910 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2911 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002912
Willy Tude54f482021-01-26 15:59:09 -08002913 // <Get Sensor Reading>
2914 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2915 ipmi::sensor_event::cmdGetSensorReading,
2916 ipmi::Privilege::User, ipmiSenGetSensorReading);
2917
2918 // <Get Sensor Threshold>
2919 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2920 ipmi::sensor_event::cmdGetSensorThreshold,
2921 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2922
2923 // <Set Sensor Threshold>
2924 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2925 ipmi::sensor_event::cmdSetSensorThreshold,
2926 ipmi::Privilege::Operator,
2927 ipmiSenSetSensorThresholds);
2928
2929 // <Get Sensor Event Enable>
2930 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2931 ipmi::sensor_event::cmdGetSensorEventEnable,
2932 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2933
2934 // <Get Sensor Event Status>
2935 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2936 ipmi::sensor_event::cmdGetSensorEventStatus,
2937 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2938
2939 // register all storage commands for both Sensor and Storage command
2940 // versions
2941
2942 // <Get SDR Repository Info>
2943 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2944 ipmi::storage::cmdGetSdrRepositoryInfo,
2945 ipmi::Privilege::User,
2946 ipmiStorageGetSDRRepositoryInfo);
2947
2948 // <Get Device SDR Info>
2949 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2950 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2951 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2952
2953 // <Get SDR Allocation Info>
2954 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2955 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2956 ipmi::Privilege::User,
2957 ipmiStorageGetSDRAllocationInfo);
2958
2959 // <Reserve SDR Repo>
2960 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2961 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2962 ipmi::Privilege::User, ipmiStorageReserveSDR);
2963
2964 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2965 ipmi::storage::cmdReserveSdrRepository,
2966 ipmi::Privilege::User, ipmiStorageReserveSDR);
2967
2968 // <Get Sdr>
2969 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2970 ipmi::sensor_event::cmdGetDeviceSdr,
2971 ipmi::Privilege::User, ipmiStorageGetSDR);
2972
2973 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2974 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2975 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302976 // <Get DCMI Sensor Info>
2977 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2978 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002979 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302980 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002981 // <Get Temperature Readings>
2982 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2983 ipmi::dcmi::cmdGetTemperatureReadings,
2984 ipmi::Privilege::User,
2985 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002986}
2987} // namespace ipmi