blob: 2c5e3e2cf3f1980ef53f13286e0896675d317153 [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);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700752 // Refer to IPMI Spec Table 32: SEL Event Records
753 generatorID = (ctx->channel << 12) // Channel
754 | (0x0 << 10) // Reserved
755 | (0x0 << 8) // 0x0 for sys-soft ID
756 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000757 }
758 else
759 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000760 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
761 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700762 // Refer to IPMI Spec Table 32: SEL Event Records
763 generatorID = (ctx->channel << 12) // Channel
764 | (0x0 << 10) // Reserved
765 | ((ctx->lun & 0x3) << 8) // Lun
766 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000767 }
768
769 if (!p.fullyUnpacked())
770 {
771 return ipmi::responseReqDataLenInvalid();
772 }
773
774 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
775 if (evmRev != validEnvmRev)
776 {
777 return ipmi::responseInvalidFieldRequest();
778 }
779 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
780 {
781 return ipmi::responseInvalidFieldRequest();
782 }
783
Willy Tude54f482021-01-26 15:59:09 -0800784 return ipmi::responseSuccess();
785}
786
Willy Tudbafbce2021-03-29 00:37:05 -0700787ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800788 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700789 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800790 bool, uint15_t, bool, uint8_t, uint8_t,
791 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700792{
793 std::string connection;
794 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700795 std::vector<std::string> interfaces;
796
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500797 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
798 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700799 if (status)
800 {
801 return ipmi::response(status);
802 }
803
Hao Jiangd2afd052020-12-10 15:09:32 -0800804 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700805 if (std::find(interfaces.begin(), interfaces.end(),
806 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700807 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700808 DbusInterfaceMap sensorMap;
809 if (!getSensorMap(ctx, connection, path, sensorMap))
810 {
811 return ipmi::responseResponseError();
812 }
813 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800814 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700815 {
816 return ipmi::responseResponseError();
817 }
818
Jie Yangf0a89942021-07-29 15:30:25 -0700819 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800820 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
821 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700822 {
823 return ipmi::responseResponseError();
824 }
825
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500826 auto value = sensor::calculateValue(reading, sensorMap,
827 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800828 if (!value)
829 {
830 return ipmi::responseResponseError();
831 }
832
833 if constexpr (debug)
834 {
835 phosphor::logging::log<phosphor::logging::level::INFO>(
836 "IPMI SET_SENSOR",
837 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
838 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
839 phosphor::logging::entry("VALUE=%f", *value));
840 }
841
842 boost::system::error_code ec =
843 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
844 "Value", ipmi::Value(*value));
845
846 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500847 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800848 // callback functions for now (e.g. ipmiSetSensorReading).
849 if (ec)
850 {
851 using namespace phosphor::logging;
852 log<level::ERR>("Failed to set property",
853 entry("PROPERTY=%s", "Value"),
854 entry("PATH=%s", path.c_str()),
855 entry("INTERFACE=%s", sensor::sensorInterface),
856 entry("WHAT=%s", ec.message().c_str()));
857 return ipmi::responseResponseError();
858 }
859 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700860 }
861
Hao Jiange39d4d82021-04-16 17:02:40 -0700862 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
863 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700864 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700865 DbusInterfaceMap sensorMap;
866 if (!getSensorMap(ctx, connection, path, sensorMap))
867 {
868 return ipmi::responseResponseError();
869 }
870 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800871 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700872 {
873 return ipmi::responseResponseError();
874 }
875
Hao Jiangd2afd052020-12-10 15:09:32 -0800876 // VR sensors are treated as a special case and we will not check the
877 // write permission for VR sensors, since they always deemed writable
878 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500879 auto vrMode = sensor::calculateVRMode(assertOffset,
880 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800881 if (!vrMode)
882 {
883 return ipmi::responseResponseError();
884 }
885 boost::system::error_code ec = setDbusProperty(
886 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
887 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500888 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800889 // callback functions for now (e.g. ipmiSetSensorReading).
890 if (ec)
891 {
892 using namespace phosphor::logging;
893 log<level::ERR>("Failed to set property",
894 entry("PROPERTY=%s", "Selected"),
895 entry("PATH=%s", path.c_str()),
896 entry("INTERFACE=%s", sensor::sensorInterface),
897 entry("WHAT=%s", ec.message().c_str()));
898 return ipmi::responseResponseError();
899 }
900 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700901 }
902
Hao Jiangd2afd052020-12-10 15:09:32 -0800903 phosphor::logging::log<phosphor::logging::level::ERR>(
904 "unknown sensor type",
905 phosphor::logging::entry("PATH=%s", path.c_str()));
906 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700907}
908
Willy Tude54f482021-01-26 15:59:09 -0800909ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
910 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
911{
912 std::string connection;
913 std::string path;
914
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000915 if (sensnum == reservedSensorNumber)
916 {
917 return ipmi::responseInvalidFieldRequest();
918 }
919
Willy Tude54f482021-01-26 15:59:09 -0800920 auto status = getSensorConnection(ctx, sensnum, connection, path);
921 if (status)
922 {
923 return ipmi::response(status);
924 }
925
Scron Chang2703b022021-07-06 15:47:45 +0800926#ifdef FEATURE_HYBRID_SENSORS
927 if (auto sensor = findStaticSensor(path);
928 sensor != ipmi::sensor::sensors.end() &&
929 getSensorEventTypeFromPath(path) !=
930 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
931 {
932 if (ipmi::sensor::Mutability::Read !=
933 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
934 {
935 return ipmi::responseIllegalCommand();
936 }
937
938 uint8_t operation;
939 try
940 {
941 ipmi::sensor::GetSensorResponse getResponse =
942 sensor->second.getFunc(sensor->second);
943
944 if (getResponse.readingOrStateUnavailable)
945 {
946 operation |= static_cast<uint8_t>(
947 IPMISensorReadingByte2::readingStateUnavailable);
948 }
949 if (getResponse.scanningEnabled)
950 {
951 operation |= static_cast<uint8_t>(
952 IPMISensorReadingByte2::sensorScanningEnable);
953 }
954 if (getResponse.allEventMessagesEnabled)
955 {
956 operation |= static_cast<uint8_t>(
957 IPMISensorReadingByte2::eventMessagesEnable);
958 }
959 return ipmi::responseSuccess(
960 getResponse.reading, operation,
961 getResponse.thresholdLevelsStates,
962 getResponse.discreteReadingSensorStates);
963 }
964 catch (const std::exception& e)
965 {
966 operation |= static_cast<uint8_t>(
967 IPMISensorReadingByte2::readingStateUnavailable);
968 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
969 }
970 }
971#endif
972
Willy Tude54f482021-01-26 15:59:09 -0800973 DbusInterfaceMap sensorMap;
974 if (!getSensorMap(ctx, connection, path, sensorMap))
975 {
976 return ipmi::responseResponseError();
977 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800978 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800979
980 if (sensorObject == sensorMap.end() ||
981 sensorObject->second.find("Value") == sensorObject->second.end())
982 {
983 return ipmi::responseResponseError();
984 }
985 auto& valueVariant = sensorObject->second["Value"];
986 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
987
988 double max = 0;
989 double min = 0;
990 getSensorMaxMin(sensorMap, max, min);
991
992 int16_t mValue = 0;
993 int16_t bValue = 0;
994 int8_t rExp = 0;
995 int8_t bExp = 0;
996 bool bSigned = false;
997
998 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
999 {
1000 return ipmi::responseResponseError();
1001 }
1002
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001003 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
1004 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -08001005 uint8_t operation =
1006 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
1007 operation |=
1008 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
1009 bool notReading = std::isnan(reading);
1010
1011 if (!notReading)
1012 {
1013 auto availableObject =
1014 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1015 if (availableObject != sensorMap.end())
1016 {
1017 auto findAvailable = availableObject->second.find("Available");
1018 if (findAvailable != availableObject->second.end())
1019 {
1020 bool* available = std::get_if<bool>(&(findAvailable->second));
1021 if (available && !(*available))
1022 {
1023 notReading = true;
1024 }
1025 }
1026 }
1027 }
1028
1029 if (notReading)
1030 {
1031 operation |= static_cast<uint8_t>(
1032 IPMISensorReadingByte2::readingStateUnavailable);
1033 }
1034
Josh Lehana55c9532020-10-28 21:59:06 -07001035 if constexpr (details::enableInstrumentation)
1036 {
1037 int byteValue;
1038 if (bSigned)
1039 {
1040 byteValue = static_cast<int>(static_cast<int8_t>(value));
1041 }
1042 else
1043 {
1044 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1045 }
1046
1047 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001048 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1049 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001050 {
1051 // This is the first reading, show the coefficients
1052 double step = (max - min) / 255.0;
1053 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001054 << details::sdrStatsTable.getName((ctx->lun << 8) |
1055 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -07001056 << ": Range min=" << min << " max=" << max
1057 << ", step=" << step
1058 << ", Coefficients mValue=" << static_cast<int>(mValue)
1059 << " rExp=" << static_cast<int>(rExp)
1060 << " bValue=" << static_cast<int>(bValue)
1061 << " bExp=" << static_cast<int>(bExp)
1062 << " bSigned=" << static_cast<int>(bSigned) << "\n";
1063 }
1064 }
1065
Willy Tude54f482021-01-26 15:59:09 -08001066 uint8_t thresholds = 0;
1067
1068 auto warningObject =
1069 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1070 if (warningObject != sensorMap.end())
1071 {
1072 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1073 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1074 if (alarmHigh != warningObject->second.end())
1075 {
1076 if (std::get<bool>(alarmHigh->second))
1077 {
1078 thresholds |= static_cast<uint8_t>(
1079 IPMISensorReadingByte3::upperNonCritical);
1080 }
1081 }
1082 if (alarmLow != warningObject->second.end())
1083 {
1084 if (std::get<bool>(alarmLow->second))
1085 {
1086 thresholds |= static_cast<uint8_t>(
1087 IPMISensorReadingByte3::lowerNonCritical);
1088 }
1089 }
1090 }
1091
1092 auto criticalObject =
1093 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1094 if (criticalObject != sensorMap.end())
1095 {
1096 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1097 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1098 if (alarmHigh != criticalObject->second.end())
1099 {
1100 if (std::get<bool>(alarmHigh->second))
1101 {
1102 thresholds |=
1103 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1104 }
1105 }
1106 if (alarmLow != criticalObject->second.end())
1107 {
1108 if (std::get<bool>(alarmLow->second))
1109 {
1110 thresholds |=
1111 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1112 }
1113 }
1114 }
1115
1116 // no discrete as of today so optional byte is never returned
1117 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1118}
1119
1120/** @brief implements the Set Sensor threshold command
1121 * @param sensorNumber - sensor number
1122 * @param lowerNonCriticalThreshMask
1123 * @param lowerCriticalThreshMask
1124 * @param lowerNonRecovThreshMask
1125 * @param upperNonCriticalThreshMask
1126 * @param upperCriticalThreshMask
1127 * @param upperNonRecovThreshMask
1128 * @param reserved
1129 * @param lowerNonCritical - lower non-critical threshold
1130 * @param lowerCritical - Lower critical threshold
1131 * @param lowerNonRecoverable - Lower non recovarable threshold
1132 * @param upperNonCritical - Upper non-critical threshold
1133 * @param upperCritical - Upper critical
1134 * @param upperNonRecoverable - Upper Non-recoverable
1135 *
1136 * @returns IPMI completion code
1137 */
1138ipmi::RspType<> ipmiSenSetSensorThresholds(
1139 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1140 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1141 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1142 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001143 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001144 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001145 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001146{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001147 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001148 {
1149 return ipmi::responseInvalidFieldRequest();
1150 }
1151
1152 // lower nc and upper nc not suppported on any sensor
1153 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1154 {
1155 return ipmi::responseInvalidFieldRequest();
1156 }
1157
1158 // if none of the threshold mask are set, nothing to do
1159 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1160 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1161 upperCriticalThreshMask | upperNonRecovThreshMask))
1162 {
1163 return ipmi::responseSuccess();
1164 }
1165
1166 std::string connection;
1167 std::string path;
1168
1169 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1170 if (status)
1171 {
1172 return ipmi::response(status);
1173 }
1174 DbusInterfaceMap sensorMap;
1175 if (!getSensorMap(ctx, connection, path, sensorMap))
1176 {
1177 return ipmi::responseResponseError();
1178 }
1179
1180 double max = 0;
1181 double min = 0;
1182 getSensorMaxMin(sensorMap, max, min);
1183
1184 int16_t mValue = 0;
1185 int16_t bValue = 0;
1186 int8_t rExp = 0;
1187 int8_t bExp = 0;
1188 bool bSigned = false;
1189
1190 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1191 {
1192 return ipmi::responseResponseError();
1193 }
1194
1195 // store a vector of property name, value to set, and interface
1196 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1197
1198 // define the indexes of the tuple
1199 constexpr uint8_t propertyName = 0;
1200 constexpr uint8_t thresholdValue = 1;
1201 constexpr uint8_t interface = 2;
1202 // verifiy all needed fields are present
1203 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1204 {
1205 auto findThreshold =
1206 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1207 if (findThreshold == sensorMap.end())
1208 {
1209 return ipmi::responseInvalidFieldRequest();
1210 }
1211 if (lowerCriticalThreshMask)
1212 {
1213 auto findLower = findThreshold->second.find("CriticalLow");
1214 if (findLower == findThreshold->second.end())
1215 {
1216 return ipmi::responseInvalidFieldRequest();
1217 }
1218 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1219 findThreshold->first);
1220 }
1221 if (upperCriticalThreshMask)
1222 {
1223 auto findUpper = findThreshold->second.find("CriticalHigh");
1224 if (findUpper == findThreshold->second.end())
1225 {
1226 return ipmi::responseInvalidFieldRequest();
1227 }
1228 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1229 findThreshold->first);
1230 }
1231 }
1232 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1233 {
1234 auto findThreshold =
1235 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1236 if (findThreshold == sensorMap.end())
1237 {
1238 return ipmi::responseInvalidFieldRequest();
1239 }
1240 if (lowerNonCriticalThreshMask)
1241 {
1242 auto findLower = findThreshold->second.find("WarningLow");
1243 if (findLower == findThreshold->second.end())
1244 {
1245 return ipmi::responseInvalidFieldRequest();
1246 }
1247 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1248 findThreshold->first);
1249 }
1250 if (upperNonCriticalThreshMask)
1251 {
1252 auto findUpper = findThreshold->second.find("WarningHigh");
1253 if (findUpper == findThreshold->second.end())
1254 {
1255 return ipmi::responseInvalidFieldRequest();
1256 }
1257 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1258 findThreshold->first);
1259 }
1260 }
1261 for (const auto& property : thresholdsToSet)
1262 {
1263 // from section 36.3 in the IPMI Spec, assume all linear
1264 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1265 (bValue * std::pow(10.0, bExp))) *
1266 std::pow(10.0, rExp);
1267 setDbusProperty(
1268 *getSdBus(), connection, path, std::get<interface>(property),
1269 std::get<propertyName>(property), ipmi::Value(valueToSet));
1270 }
1271 return ipmi::responseSuccess();
1272}
1273
1274IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1275{
1276 IPMIThresholds resp;
1277 auto warningInterface =
1278 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1279 auto criticalInterface =
1280 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1281
1282 if ((warningInterface != sensorMap.end()) ||
1283 (criticalInterface != sensorMap.end()))
1284 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001285 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001286
1287 if (sensorPair == sensorMap.end())
1288 {
1289 // should not have been able to find a sensor not implementing
1290 // the sensor object
1291 throw std::runtime_error("Invalid sensor map");
1292 }
1293
1294 double max = 0;
1295 double min = 0;
1296 getSensorMaxMin(sensorMap, max, min);
1297
1298 int16_t mValue = 0;
1299 int16_t bValue = 0;
1300 int8_t rExp = 0;
1301 int8_t bExp = 0;
1302 bool bSigned = false;
1303
1304 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1305 {
1306 throw std::runtime_error("Invalid sensor atrributes");
1307 }
1308 if (warningInterface != sensorMap.end())
1309 {
1310 auto& warningMap = warningInterface->second;
1311
1312 auto warningHigh = warningMap.find("WarningHigh");
1313 auto warningLow = warningMap.find("WarningLow");
1314
1315 if (warningHigh != warningMap.end())
1316 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001317 double value = std::visit(VariantToDoubleVisitor(),
1318 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001319 if (std::isfinite(value))
1320 {
1321 resp.warningHigh = scaleIPMIValueFromDouble(
1322 value, mValue, rExp, bValue, bExp, bSigned);
1323 }
Willy Tude54f482021-01-26 15:59:09 -08001324 }
1325 if (warningLow != warningMap.end())
1326 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001327 double value = std::visit(VariantToDoubleVisitor(),
1328 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001329 if (std::isfinite(value))
1330 {
1331 resp.warningLow = scaleIPMIValueFromDouble(
1332 value, mValue, rExp, bValue, bExp, bSigned);
1333 }
Willy Tude54f482021-01-26 15:59:09 -08001334 }
1335 }
1336 if (criticalInterface != sensorMap.end())
1337 {
1338 auto& criticalMap = criticalInterface->second;
1339
1340 auto criticalHigh = criticalMap.find("CriticalHigh");
1341 auto criticalLow = criticalMap.find("CriticalLow");
1342
1343 if (criticalHigh != criticalMap.end())
1344 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001345 double value = std::visit(VariantToDoubleVisitor(),
1346 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001347 if (std::isfinite(value))
1348 {
1349 resp.criticalHigh = scaleIPMIValueFromDouble(
1350 value, mValue, rExp, bValue, bExp, bSigned);
1351 }
Willy Tude54f482021-01-26 15:59:09 -08001352 }
1353 if (criticalLow != criticalMap.end())
1354 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001355 double value = std::visit(VariantToDoubleVisitor(),
1356 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001357 if (std::isfinite(value))
1358 {
1359 resp.criticalLow = scaleIPMIValueFromDouble(
1360 value, mValue, rExp, bValue, bExp, bSigned);
1361 }
Willy Tude54f482021-01-26 15:59:09 -08001362 }
1363 }
1364 }
1365 return resp;
1366}
1367
1368ipmi::RspType<uint8_t, // readable
1369 uint8_t, // lowerNCrit
1370 uint8_t, // lowerCrit
1371 uint8_t, // lowerNrecoverable
1372 uint8_t, // upperNC
1373 uint8_t, // upperCrit
1374 uint8_t> // upperNRecoverable
1375 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1376{
1377 std::string connection;
1378 std::string path;
1379
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001380 if (sensorNumber == reservedSensorNumber)
1381 {
1382 return ipmi::responseInvalidFieldRequest();
1383 }
1384
Willy Tude54f482021-01-26 15:59:09 -08001385 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1386 if (status)
1387 {
1388 return ipmi::response(status);
1389 }
1390
1391 DbusInterfaceMap sensorMap;
1392 if (!getSensorMap(ctx, connection, path, sensorMap))
1393 {
1394 return ipmi::responseResponseError();
1395 }
1396
1397 IPMIThresholds thresholdData;
1398 try
1399 {
1400 thresholdData = getIPMIThresholds(sensorMap);
1401 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001402 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001403 {
1404 return ipmi::responseResponseError();
1405 }
1406
1407 uint8_t readable = 0;
1408 uint8_t lowerNC = 0;
1409 uint8_t lowerCritical = 0;
1410 uint8_t lowerNonRecoverable = 0;
1411 uint8_t upperNC = 0;
1412 uint8_t upperCritical = 0;
1413 uint8_t upperNonRecoverable = 0;
1414
1415 if (thresholdData.warningHigh)
1416 {
1417 readable |=
1418 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1419 upperNC = *thresholdData.warningHigh;
1420 }
1421 if (thresholdData.warningLow)
1422 {
1423 readable |=
1424 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1425 lowerNC = *thresholdData.warningLow;
1426 }
1427
1428 if (thresholdData.criticalHigh)
1429 {
1430 readable |=
1431 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1432 upperCritical = *thresholdData.criticalHigh;
1433 }
1434 if (thresholdData.criticalLow)
1435 {
1436 readable |=
1437 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1438 lowerCritical = *thresholdData.criticalLow;
1439 }
1440
1441 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1442 lowerNonRecoverable, upperNC, upperCritical,
1443 upperNonRecoverable);
1444}
1445
1446/** @brief implements the get Sensor event enable command
1447 * @param sensorNumber - sensor number
1448 *
1449 * @returns IPMI completion code plus response data
1450 * - enabled - Sensor Event messages
1451 * - assertionEnabledLsb - Assertion event messages
1452 * - assertionEnabledMsb - Assertion event messages
1453 * - deassertionEnabledLsb - Deassertion event messages
1454 * - deassertionEnabledMsb - Deassertion event messages
1455 */
1456
1457ipmi::RspType<uint8_t, // enabled
1458 uint8_t, // assertionEnabledLsb
1459 uint8_t, // assertionEnabledMsb
1460 uint8_t, // deassertionEnabledLsb
1461 uint8_t> // deassertionEnabledMsb
1462 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1463{
1464 std::string connection;
1465 std::string path;
1466
1467 uint8_t enabled = 0;
1468 uint8_t assertionEnabledLsb = 0;
1469 uint8_t assertionEnabledMsb = 0;
1470 uint8_t deassertionEnabledLsb = 0;
1471 uint8_t deassertionEnabledMsb = 0;
1472
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001473 if (sensorNum == reservedSensorNumber)
1474 {
1475 return ipmi::responseInvalidFieldRequest();
1476 }
1477
Willy Tude54f482021-01-26 15:59:09 -08001478 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1479 if (status)
1480 {
1481 return ipmi::response(status);
1482 }
1483
Scron Chang2703b022021-07-06 15:47:45 +08001484#ifdef FEATURE_HYBRID_SENSORS
1485 if (auto sensor = findStaticSensor(path);
1486 sensor != ipmi::sensor::sensors.end() &&
1487 getSensorEventTypeFromPath(path) !=
1488 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1489 {
1490 enabled = static_cast<uint8_t>(
1491 IPMISensorEventEnableByte2::sensorScanningEnable);
1492 uint16_t assertionEnabled = 0;
1493 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1494 ->second.begin()
1495 ->second.second)
1496 {
1497 assertionEnabled |= (1 << offsetValMap.first);
1498 }
1499 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1500 assertionEnabledMsb =
1501 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1502
1503 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1504 assertionEnabledMsb, deassertionEnabledLsb,
1505 deassertionEnabledMsb);
1506 }
1507#endif
1508
Willy Tude54f482021-01-26 15:59:09 -08001509 DbusInterfaceMap sensorMap;
1510 if (!getSensorMap(ctx, connection, path, sensorMap))
1511 {
1512 return ipmi::responseResponseError();
1513 }
1514
1515 auto warningInterface =
1516 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1517 auto criticalInterface =
1518 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1519 if ((warningInterface != sensorMap.end()) ||
1520 (criticalInterface != sensorMap.end()))
1521 {
1522 enabled = static_cast<uint8_t>(
1523 IPMISensorEventEnableByte2::sensorScanningEnable);
1524 if (warningInterface != sensorMap.end())
1525 {
1526 auto& warningMap = warningInterface->second;
1527
1528 auto warningHigh = warningMap.find("WarningHigh");
1529 auto warningLow = warningMap.find("WarningLow");
1530 if (warningHigh != warningMap.end())
1531 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001532 double value = std::visit(VariantToDoubleVisitor(),
1533 warningHigh->second);
1534 if (std::isfinite(value))
1535 {
1536 assertionEnabledLsb |= static_cast<uint8_t>(
1537 IPMISensorEventEnableThresholds::
1538 upperNonCriticalGoingHigh);
1539 deassertionEnabledLsb |= static_cast<uint8_t>(
1540 IPMISensorEventEnableThresholds::
1541 upperNonCriticalGoingLow);
1542 }
Willy Tude54f482021-01-26 15:59:09 -08001543 }
1544 if (warningLow != warningMap.end())
1545 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001546 double value = std::visit(VariantToDoubleVisitor(),
1547 warningLow->second);
1548 if (std::isfinite(value))
1549 {
1550 assertionEnabledLsb |= static_cast<uint8_t>(
1551 IPMISensorEventEnableThresholds::
1552 lowerNonCriticalGoingLow);
1553 deassertionEnabledLsb |= static_cast<uint8_t>(
1554 IPMISensorEventEnableThresholds::
1555 lowerNonCriticalGoingHigh);
1556 }
Willy Tude54f482021-01-26 15:59:09 -08001557 }
1558 }
1559 if (criticalInterface != sensorMap.end())
1560 {
1561 auto& criticalMap = criticalInterface->second;
1562
1563 auto criticalHigh = criticalMap.find("CriticalHigh");
1564 auto criticalLow = criticalMap.find("CriticalLow");
1565
1566 if (criticalHigh != criticalMap.end())
1567 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001568 double value = std::visit(VariantToDoubleVisitor(),
1569 criticalHigh->second);
1570 if (std::isfinite(value))
1571 {
1572 assertionEnabledMsb |= static_cast<uint8_t>(
1573 IPMISensorEventEnableThresholds::
1574 upperCriticalGoingHigh);
1575 deassertionEnabledMsb |= static_cast<uint8_t>(
1576 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1577 }
Willy Tude54f482021-01-26 15:59:09 -08001578 }
1579 if (criticalLow != criticalMap.end())
1580 {
Johnathan Mantey92217f02024-06-20 12:43:01 -07001581 double value = std::visit(VariantToDoubleVisitor(),
1582 criticalLow->second);
1583 if (std::isfinite(value))
1584 {
1585 assertionEnabledLsb |= static_cast<uint8_t>(
1586 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1587 deassertionEnabledLsb |= static_cast<uint8_t>(
1588 IPMISensorEventEnableThresholds::
1589 lowerCriticalGoingHigh);
1590 }
Willy Tude54f482021-01-26 15:59:09 -08001591 }
1592 }
1593 }
1594
1595 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1596 assertionEnabledMsb, deassertionEnabledLsb,
1597 deassertionEnabledMsb);
1598}
1599
1600/** @brief implements the get Sensor event status command
1601 * @param sensorNumber - sensor number, FFh = reserved
1602 *
1603 * @returns IPMI completion code plus response data
1604 * - sensorEventStatus - Sensor Event messages state
1605 * - assertions - Assertion event messages
1606 * - deassertions - Deassertion event messages
1607 */
1608ipmi::RspType<uint8_t, // sensorEventStatus
1609 std::bitset<16>, // assertions
1610 std::bitset<16> // deassertion
1611 >
1612 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1613{
1614 if (sensorNum == reservedSensorNumber)
1615 {
1616 return ipmi::responseInvalidFieldRequest();
1617 }
1618
1619 std::string connection;
1620 std::string path;
1621 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1622 if (status)
1623 {
1624 phosphor::logging::log<phosphor::logging::level::ERR>(
1625 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1626 phosphor::logging::entry("SENSOR=%d", sensorNum));
1627 return ipmi::response(status);
1628 }
1629
Scron Chang2703b022021-07-06 15:47:45 +08001630#ifdef FEATURE_HYBRID_SENSORS
1631 if (auto sensor = findStaticSensor(path);
1632 sensor != ipmi::sensor::sensors.end() &&
1633 getSensorEventTypeFromPath(path) !=
1634 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1635 {
1636 auto response = ipmi::sensor::get::mapDbusToAssertion(
1637 sensor->second, path, sensor->second.sensorInterface);
1638 std::bitset<16> assertions;
1639 // deassertions are not used.
1640 std::bitset<16> deassertions = 0;
1641 uint8_t sensorEventStatus;
1642 if (response.readingOrStateUnavailable)
1643 {
1644 sensorEventStatus |= static_cast<uint8_t>(
1645 IPMISensorReadingByte2::readingStateUnavailable);
1646 }
1647 if (response.scanningEnabled)
1648 {
1649 sensorEventStatus |= static_cast<uint8_t>(
1650 IPMISensorReadingByte2::sensorScanningEnable);
1651 }
1652 if (response.allEventMessagesEnabled)
1653 {
1654 sensorEventStatus |= static_cast<uint8_t>(
1655 IPMISensorReadingByte2::eventMessagesEnable);
1656 }
1657 assertions |= response.discreteReadingSensorStates << 8;
1658 assertions |= response.thresholdLevelsStates;
1659 return ipmi::responseSuccess(sensorEventStatus, assertions,
1660 deassertions);
1661 }
1662#endif
1663
Willy Tude54f482021-01-26 15:59:09 -08001664 DbusInterfaceMap sensorMap;
1665 if (!getSensorMap(ctx, connection, path, sensorMap))
1666 {
1667 phosphor::logging::log<phosphor::logging::level::ERR>(
1668 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1669 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1670 return ipmi::responseResponseError();
1671 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001672
1673 uint8_t sensorEventStatus =
1674 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1675 std::bitset<16> assertions = 0;
1676 std::bitset<16> deassertions = 0;
1677
1678 // handle VR typed sensor
1679 auto vrInterface = sensorMap.find(sensor::vrInterface);
1680 if (vrInterface != sensorMap.end())
1681 {
1682 if (!sensor::getVrEventStatus(ctx, connection, path,
1683 vrInterface->second, assertions))
1684 {
1685 return ipmi::responseResponseError();
1686 }
1687
1688 // both Event Message and Sensor Scanning are disable for VR.
1689 sensorEventStatus = 0;
1690 return ipmi::responseSuccess(sensorEventStatus, assertions,
1691 deassertions);
1692 }
1693
Willy Tude54f482021-01-26 15:59:09 -08001694 auto warningInterface =
1695 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1696 auto criticalInterface =
1697 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1698
Willy Tude54f482021-01-26 15:59:09 -08001699 std::optional<bool> criticalDeassertHigh =
1700 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1701 std::optional<bool> criticalDeassertLow =
1702 thresholdDeassertMap[path]["CriticalAlarmLow"];
1703 std::optional<bool> warningDeassertHigh =
1704 thresholdDeassertMap[path]["WarningAlarmHigh"];
1705 std::optional<bool> warningDeassertLow =
1706 thresholdDeassertMap[path]["WarningAlarmLow"];
1707
Willy Tude54f482021-01-26 15:59:09 -08001708 if (criticalDeassertHigh && !*criticalDeassertHigh)
1709 {
1710 deassertions.set(static_cast<size_t>(
1711 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1712 }
1713 if (criticalDeassertLow && !*criticalDeassertLow)
1714 {
1715 deassertions.set(static_cast<size_t>(
1716 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1717 }
1718 if (warningDeassertHigh && !*warningDeassertHigh)
1719 {
1720 deassertions.set(static_cast<size_t>(
1721 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1722 }
1723 if (warningDeassertLow && !*warningDeassertLow)
1724 {
1725 deassertions.set(static_cast<size_t>(
1726 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1727 }
1728 if ((warningInterface != sensorMap.end()) ||
1729 (criticalInterface != sensorMap.end()))
1730 {
1731 sensorEventStatus = static_cast<size_t>(
1732 IPMISensorEventEnableByte2::eventMessagesEnable);
1733 if (warningInterface != sensorMap.end())
1734 {
1735 auto& warningMap = warningInterface->second;
1736
1737 auto warningHigh = warningMap.find("WarningAlarmHigh");
1738 auto warningLow = warningMap.find("WarningAlarmLow");
1739 auto warningHighAlarm = false;
1740 auto warningLowAlarm = false;
1741
1742 if (warningHigh != warningMap.end())
1743 {
1744 warningHighAlarm = std::get<bool>(warningHigh->second);
1745 }
1746 if (warningLow != warningMap.end())
1747 {
1748 warningLowAlarm = std::get<bool>(warningLow->second);
1749 }
1750 if (warningHighAlarm)
1751 {
1752 assertions.set(
1753 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1754 upperNonCriticalGoingHigh));
1755 }
1756 if (warningLowAlarm)
1757 {
1758 assertions.set(
1759 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1760 lowerNonCriticalGoingLow));
1761 }
1762 }
1763 if (criticalInterface != sensorMap.end())
1764 {
1765 auto& criticalMap = criticalInterface->second;
1766
1767 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1768 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1769 auto criticalHighAlarm = false;
1770 auto criticalLowAlarm = false;
1771
1772 if (criticalHigh != criticalMap.end())
1773 {
1774 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1775 }
1776 if (criticalLow != criticalMap.end())
1777 {
1778 criticalLowAlarm = std::get<bool>(criticalLow->second);
1779 }
1780 if (criticalHighAlarm)
1781 {
1782 assertions.set(
1783 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1784 upperCriticalGoingHigh));
1785 }
1786 if (criticalLowAlarm)
1787 {
1788 assertions.set(static_cast<size_t>(
1789 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1790 }
1791 }
1792 }
1793
1794 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1795}
1796
Willy Tu38e7a2b2021-03-29 15:09:56 -07001797// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001798void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1799 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001800{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001801 get_sdr::header::set_record_id(
1802 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1803
Willy Tu38e7a2b2021-03-29 15:09:56 -07001804 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1805 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1806
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001807 record.header.sdr_version = ipmiSdrVersion;
1808 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1809 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1810 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001811 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001812 record.key.owner_lun = lun;
1813 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001814}
Willy Tu4eca2512022-06-20 21:14:51 -07001815bool constructSensorSdr(
1816 ipmi::Context::ptr ctx,
1817 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1818 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1819 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001820{
Hao Jiange39d4d82021-04-16 17:02:40 -07001821 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1822
1823 DbusInterfaceMap sensorMap;
1824 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1825 {
1826 phosphor::logging::log<phosphor::logging::level::ERR>(
1827 "Failed to update sensor map for threshold sensor",
1828 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1829 phosphor::logging::entry("PATH=%s", path.c_str()));
1830 return false;
1831 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001832
1833 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1834 record.body.sensor_type = getSensorTypeFromPath(path);
1835 std::string type = getSensorTypeStringFromPath(path);
1836 auto typeCstr = type.c_str();
1837 auto findUnits = sensorUnits.find(typeCstr);
1838 if (findUnits != sensorUnits.end())
1839 {
1840 record.body.sensor_units_2_base =
1841 static_cast<uint8_t>(findUnits->second);
1842 } // else default 0x0 unspecified
1843
1844 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1845
Hao Jiangd2afd052020-12-10 15:09:32 -08001846 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001847 if (sensorObject == sensorMap.end())
1848 {
1849 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001850 "constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001851 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001852 }
1853
1854 uint8_t entityId = 0;
1855 uint8_t entityInstance = 0x01;
1856
1857 // follow the association chain to get the parent board's entityid and
1858 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001859 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1860 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001861
1862 record.body.entity_id = entityId;
1863 record.body.entity_instance = entityInstance;
1864
Shakeeb Pasha93889722021-10-14 10:20:13 +05301865 double max = 0;
1866 double min = 0;
1867 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001868
1869 int16_t mValue = 0;
1870 int8_t rExp = 0;
1871 int16_t bValue = 0;
1872 int8_t bExp = 0;
1873 bool bSigned = false;
1874
1875 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1876 {
1877 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001878 "constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001879 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001880 }
1881
1882 // The record.body is a struct SensorDataFullRecordBody
1883 // from sensorhandler.hpp in phosphor-ipmi-host.
1884 // The meaning of these bits appears to come from
1885 // table 43.1 of the IPMI spec.
1886 // The above 5 sensor attributes are stuffed in as follows:
1887 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1888 // Byte 22-24 are for other purposes
1889 // Byte 25 = MMMMMMMM = LSB of M
1890 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1891 // Byte 27 = BBBBBBBB = LSB of B
1892 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1893 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1894 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1895
1896 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1897 record.body.m_lsb = mValue & 0xFF;
1898
1899 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1900 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1901
1902 // move the smallest bit of the MSB into place (bit 9)
1903 // the MSbs are bits 7:8 in m_msb_and_tolerance
1904 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1905
1906 record.body.b_lsb = bValue & 0xFF;
1907
1908 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1909 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1910
1911 // move the smallest bit of the MSB into place (bit 9)
1912 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1913 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1914
1915 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1916 uint8_t rExpBits = rExp & 0x07;
1917
1918 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1919 uint8_t bExpBits = bExp & 0x07;
1920
1921 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001922 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1923 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001924
1925 // Set the analog reading byte interpretation accordingly
1926 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1927
1928 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1929 // These seem redundant, but derivable from the above 5 attributes
1930 // Original comment said "todo fill out rest of units"
1931
1932 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001933 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001934 get_sdr::body::set_id_strlen(name.size(), &record.body);
1935 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001936 std::memcpy(record.body.id_string, name.c_str(),
1937 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001938
Josh Lehana55c9532020-10-28 21:59:06 -07001939 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001940 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001941
Jie Yangf0a89942021-07-29 15:30:25 -07001942 bool sensorSettable = false;
1943 auto mutability =
1944 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1945 if (mutability != sensorMap.end())
1946 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001947 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1948 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001949 }
1950 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1951
1952 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001953 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001954
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001955 IPMIThresholds thresholdData;
1956 try
1957 {
1958 thresholdData = getIPMIThresholds(sensorMap);
1959 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001960 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001961 {
1962 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001963 "constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001964 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001965 }
1966
1967 if (thresholdData.criticalHigh)
1968 {
1969 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1970 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1971 IPMISensorEventEnableThresholds::criticalThreshold);
1972 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1973 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1974 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1975 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1976 record.body.discrete_reading_setting_mask[0] |=
1977 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1978 }
1979 if (thresholdData.warningHigh)
1980 {
1981 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1982 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1983 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1984 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1985 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1986 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1987 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1988 record.body.discrete_reading_setting_mask[0] |=
1989 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1990 }
1991 if (thresholdData.criticalLow)
1992 {
1993 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1994 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1995 IPMISensorEventEnableThresholds::criticalThreshold);
1996 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1997 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1998 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1999 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
2000 record.body.discrete_reading_setting_mask[0] |=
2001 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
2002 }
2003 if (thresholdData.warningLow)
2004 {
2005 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
2006 record.body.supported_assertions[1] |= static_cast<uint8_t>(
2007 IPMISensorEventEnableThresholds::nonCriticalThreshold);
2008 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
2009 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2010 record.body.supported_assertions[0] |= static_cast<uint8_t>(
2011 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
2012 record.body.discrete_reading_setting_mask[0] |=
2013 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
2014 }
2015
2016 // everything that is readable is setable
2017 record.body.discrete_reading_setting_mask[1] =
2018 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07002019 return true;
2020}
2021
Scron Chang2703b022021-07-06 15:47:45 +08002022#ifdef FEATURE_HYBRID_SENSORS
2023// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08002024void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08002025 uint16_t recordID,
2026 ipmi::sensor::IdInfoMap::const_iterator sensor,
2027 get_sdr::SensorDataFullRecord& record)
2028{
2029 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2030
2031 record.body.entity_id = sensor->second.entityType;
2032 record.body.sensor_type = sensor->second.sensorType;
2033 record.body.event_reading_type = sensor->second.sensorReadingType;
2034 record.body.entity_instance = sensor->second.instance;
2035 if (ipmi::sensor::Mutability::Write ==
2036 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2037 {
2038 get_sdr::body::init_settable_state(true, &(record.body));
2039 }
2040
2041 auto id_string = sensor->second.sensorName;
2042
2043 if (id_string.empty())
2044 {
2045 id_string = sensor->second.sensorNameFunc(sensor->second);
2046 }
2047
2048 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2049 {
2050 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2051 &(record.body));
2052 }
2053 else
2054 {
2055 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2056 }
Paul Fertser51136982022-08-18 12:36:41 +00002057 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002058 std::strncpy(record.body.id_string, id_string.c_str(),
2059 get_sdr::body::get_id_strlen(&(record.body)));
2060}
2061#endif
2062
Hao Jiange39d4d82021-04-16 17:02:40 -07002063// Construct type 3 SDR header and key (for VR and other discrete sensors)
2064void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2065 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002066{
2067 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2068 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2069
2070 get_sdr::header::set_record_id(
2071 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2072
2073 record.header.sdr_version = ipmiSdrVersion;
2074 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2075 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2076 sizeof(get_sdr::SensorDataRecordHeader);
2077 record.key.owner_id = bmcI2CAddr;
2078 record.key.owner_lun = lun;
2079 record.key.sensor_number = sensornumber;
2080
2081 record.body.entity_id = 0x00;
2082 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002083}
Willy Tu61992ad2021-03-29 15:33:20 -07002084
Hao Jiange39d4d82021-04-16 17:02:40 -07002085// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002086bool constructVrSdr(ipmi::Context::ptr ctx,
2087 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2088 uint16_t sensorNum, uint16_t recordID,
2089 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002090 get_sdr::SensorDataEventRecord& record)
2091{
Hao Jiange39d4d82021-04-16 17:02:40 -07002092 constructEventSdrHeaderKey(sensorNum, recordID, record);
2093
2094 DbusInterfaceMap sensorMap;
2095 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2096 {
2097 phosphor::logging::log<phosphor::logging::level::ERR>(
2098 "Failed to update sensor map for VR sensor",
2099 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2100 phosphor::logging::entry("PATH=%s", path.c_str()));
2101 return false;
2102 }
Willy Tu61992ad2021-03-29 15:33:20 -07002103 // follow the association chain to get the parent board's entityid and
2104 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002105 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2106 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002107 record.body.entity_instance);
2108
2109 // Sensor type is hardcoded as a module/board type instead of parsing from
2110 // sensor path. This is because VR control is allocated in an independent
2111 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2112 // types.
2113 static constexpr const uint8_t module_board_type = 0x15;
2114 record.body.sensor_type = module_board_type;
2115 record.body.event_reading_type = 0x00;
2116
2117 record.body.sensor_record_sharing_1 = 0x00;
2118 record.body.sensor_record_sharing_2 = 0x00;
2119
2120 // populate sensor name from path
2121 auto name = sensor::parseSdrIdFromPath(path);
2122 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002123 get_sdr::body::set_id_strlen(nameSize, &record.body);
2124 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002125 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2126 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2127
2128 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002129 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002130
2131 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002132}
2133
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002134uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002135{
2136 return std::min(getSensorTree().size(), maxIPMISensors);
2137}
2138
Willy Tu4eca2512022-06-20 21:14:51 -07002139static int getSensorDataRecord(
2140 ipmi::Context::ptr ctx,
2141 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2142 std::vector<uint8_t>& recordData, uint16_t recordID,
2143 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002144{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302145 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002146 size_t lastRecord = ipmi::getNumberOfSensors() +
2147 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2148 uint16_t nextRecord(recordID + 1);
2149
Willy Tu38e7a2b2021-03-29 15:09:56 -07002150 if (recordID == lastRecordIndex)
2151 {
2152 recordID = lastRecord;
2153 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002154 if (recordID == lastRecord)
2155 {
2156 nextRecord = lastRecordIndex;
2157 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002158 if (recordID > lastRecord)
2159 {
2160 phosphor::logging::log<phosphor::logging::level::ERR>(
2161 "getSensorDataRecord: recordID > lastRecord error");
2162 return GENERAL_ERROR;
2163 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002164 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002165 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002166 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2167 recordData);
2168 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002169 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002170 // phosphor::logging::log<phosphor::logging::level::ERR>(
2171 // "getSensorDataRecord: Error getting custom record");
2172 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002173 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002174 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002175 }
2176
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002177 // Perform a incremental scan of the SDR Record ID's and translate the
2178 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2179 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2180 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2181 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002182 std::string connection;
2183 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002184 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002185 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002186 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002187 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002188 // LUN 0 has one reserved sensor number. Compensate here by adding one
2189 // to the record ID
2190 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002191 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002192 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002193 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002194 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002195 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2196 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2197 // rules governing its use.
2198 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002199 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002200 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002201
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002202 auto status = getSensorConnection(ctx,
2203 static_cast<uint8_t>(sensNumFromRecID),
2204 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002205 if (status)
2206 {
2207 phosphor::logging::log<phosphor::logging::level::ERR>(
2208 "getSensorDataRecord: getSensorConnection error");
2209 return GENERAL_ERROR;
2210 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002211 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002212 // Return an error on LUN 2 assingments, and any sensor number beyond the
2213 // range of LUN 3
2214 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2215 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002216 {
2217 phosphor::logging::log<phosphor::logging::level::ERR>(
2218 "getSensorDataRecord: invalidSensorNumber");
2219 return GENERAL_ERROR;
2220 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002221 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2222 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2223
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002224 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2225 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002226 {
2227 phosphor::logging::log<phosphor::logging::level::ERR>(
2228 "getSensorDataRecord: sensor record mismatch");
2229 return GENERAL_ERROR;
2230 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002231
Willy Tu38e7a2b2021-03-29 15:09:56 -07002232 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002233 if (std::find(interfaces.begin(), interfaces.end(),
2234 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002235 {
Willy Tu11d68892022-01-20 10:37:34 -08002236 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002237
Hao Jiange39d4d82021-04-16 17:02:40 -07002238 // If the request doesn't read SDR body, construct only header and key
2239 // part to avoid additional DBus transaction.
2240 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2241 {
2242 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2243 }
Willy Tu4eca2512022-06-20 21:14:51 -07002244 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2245 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002246 {
2247 return GENERAL_ERROR;
2248 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002249
selvaganapathi7b2e5502023-02-14 07:10:47 +05302250 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2251 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002252
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002253 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002254 }
Willy Tu61992ad2021-03-29 15:33:20 -07002255
Scron Chang2703b022021-07-06 15:47:45 +08002256#ifdef FEATURE_HYBRID_SENSORS
2257 if (auto sensor = findStaticSensor(path);
2258 sensor != ipmi::sensor::sensors.end() &&
2259 getSensorEventTypeFromPath(path) !=
2260 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2261 {
Willy Tu11d68892022-01-20 10:37:34 -08002262 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002263
2264 // If the request doesn't read SDR body, construct only header and key
2265 // part to avoid additional DBus transaction.
2266 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2267 {
2268 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2269 }
2270 else
2271 {
2272 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2273 }
2274
selvaganapathi7b2e5502023-02-14 07:10:47 +05302275 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2276 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002277
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002278 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002279 }
2280#endif
2281
Willy Tu61992ad2021-03-29 15:33:20 -07002282 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002283 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2284 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002285 {
Willy Tu11d68892022-01-20 10:37:34 -08002286 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002287
Hao Jiange39d4d82021-04-16 17:02:40 -07002288 // If the request doesn't read SDR body, construct only header and key
2289 // part to avoid additional DBus transaction.
2290 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2291 {
2292 constructEventSdrHeaderKey(sensorNum, recordID, record);
2293 }
Willy Tu4eca2512022-06-20 21:14:51 -07002294 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2295 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002296 {
2297 return GENERAL_ERROR;
2298 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302299 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2300 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002301 }
2302
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002303 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002304}
2305
2306/** @brief implements the get SDR Info command
2307 * @param count - Operation
2308 *
2309 * @returns IPMI completion code plus response data
2310 * - sdrCount - sensor/SDR count
2311 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2312 */
2313static ipmi::RspType<uint8_t, // respcount
2314 uint8_t, // dynamic population flags
2315 uint32_t // last time a sensor was added
2316 >
2317 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2318 std::optional<uint8_t> count)
2319{
2320 auto& sensorTree = getSensorTree();
2321 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002322 uint16_t recordID = 0;
2323 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002324 // Sensors are dynamically allocated, and there is at least one LUN
2325 uint8_t lunsAndDynamicPopulation = 0x80;
2326 constexpr uint8_t getSdrCount = 0x01;
2327 constexpr uint8_t getSensorCount = 0x00;
2328
2329 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2330 {
2331 return ipmi::responseResponseError();
2332 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002333 uint16_t numSensors = ipmi::getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002334 if (count.value_or(0) == getSdrCount)
2335 {
Willy Tu4eca2512022-06-20 21:14:51 -07002336 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2337
Harvey Wu75893062023-03-22 17:17:31 +08002338 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002339 {
2340 recordID += maxSensorsPerLUN;
2341 }
Harvey Wu75893062023-03-22 17:17:31 +08002342 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002343 {
2344 recordID += maxSensorsPerLUN * 2;
2345 }
2346
Harvey Wu75893062023-03-22 17:17:31 +08002347 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2348 // assigned to the LUN
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002349 while (getSensorDataRecord(ctx,
2350 ipmiDecoratorPaths.value_or(
2351 std::unordered_set<std::string>()),
2352 record, recordID++) >= 0)
Willy Tude54f482021-01-26 15:59:09 -08002353 {
2354 get_sdr::SensorDataRecordHeader* hdr =
2355 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002356 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302357 if (!hdr)
2358 {
2359 continue;
2360 }
2361
2362 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002363 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002364 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002365 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002366 record.data());
2367 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002368 {
2369 sdrCount++;
2370 }
2371 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302372 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2373 {
2374 get_sdr::SensorDataCompactRecord* recordData =
2375 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2376 record.data());
2377 if (ctx->lun == recordData->key.owner_lun)
2378 {
2379 sdrCount++;
2380 }
2381 }
Harvey Wua3476272023-03-22 10:09:38 +08002382 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2383 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302384 {
2385 sdrCount++;
2386 }
Harvey Wua3476272023-03-22 10:09:38 +08002387
2388 // Because response count data is 1 byte, so sdrCount need to avoid
2389 // overflow.
2390 if (sdrCount == maxSensorsPerLUN)
2391 {
2392 break;
2393 }
Willy Tude54f482021-01-26 15:59:09 -08002394 }
2395 }
2396 else if (count.value_or(0) == getSensorCount)
2397 {
2398 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002399 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002400 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002401 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2402 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002403 }
Harvey Wu75893062023-03-22 17:17:31 +08002404 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002405 {
2406 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2407 ? maxSensorsPerLUN
2408 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2409 }
Harvey Wu75893062023-03-22 17:17:31 +08002410 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002411 {
2412 if (numSensors <= maxIPMISensors)
2413 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002414 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2415 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002416 }
2417 else
2418 {
2419 // error
2420 throw std::out_of_range(
2421 "Maximum number of IPMI sensors exceeded.");
2422 }
2423 }
2424 }
2425 else
2426 {
2427 return ipmi::responseInvalidFieldRequest();
2428 }
2429
2430 // Get Sensor count. This returns the number of sensors
2431 if (numSensors > 0)
2432 {
2433 lunsAndDynamicPopulation |= 1;
2434 }
2435 if (numSensors > maxSensorsPerLUN)
2436 {
2437 lunsAndDynamicPopulation |= 2;
2438 }
2439 if (numSensors >= (maxSensorsPerLUN * 2))
2440 {
2441 lunsAndDynamicPopulation |= 8;
2442 }
2443 if (numSensors > maxIPMISensors)
2444 {
2445 // error
2446 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2447 }
2448
2449 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2450 sdrLastAdd);
2451}
2452
2453/* end sensor commands */
2454
2455/* storage commands */
2456
2457ipmi::RspType<uint8_t, // sdr version
2458 uint16_t, // record count
2459 uint16_t, // free space
2460 uint32_t, // most recent addition
2461 uint32_t, // most recent erase
2462 uint8_t // operationSupport
2463 >
2464 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2465{
Willy Tude54f482021-01-26 15:59:09 -08002466 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Johnathan Mantey31c1ecd2024-06-20 11:10:41 -07002467 uint16_t recordCount = ipmi::getNumberOfSensors() +
2468 ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002469
2470 uint8_t operationSupport = static_cast<uint8_t>(
2471 SdrRepositoryInfoOps::overflow); // write not supported
2472
2473 operationSupport |=
2474 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2475 operationSupport |= static_cast<uint8_t>(
2476 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2477 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2478 unspecifiedFreeSpace, sdrLastAdd,
2479 sdrLastRemove, operationSupport);
2480}
2481
2482/** @brief implements the get SDR allocation info command
2483 *
2484 * @returns IPMI completion code plus response data
2485 * - allocUnits - Number of possible allocation units
2486 * - allocUnitSize - Allocation unit size in bytes.
2487 * - allocUnitFree - Number of free allocation units
2488 * - allocUnitLargestFree - Largest free block in allocation units
2489 * - maxRecordSize - Maximum record size in allocation units.
2490 */
2491ipmi::RspType<uint16_t, // allocUnits
2492 uint16_t, // allocUnitSize
2493 uint16_t, // allocUnitFree
2494 uint16_t, // allocUnitLargestFree
2495 uint8_t // maxRecordSize
2496 >
2497 ipmiStorageGetSDRAllocationInfo()
2498{
2499 // 0000h unspecified number of alloc units
2500 constexpr uint16_t allocUnits = 0;
2501
2502 constexpr uint16_t allocUnitFree = 0;
2503 constexpr uint16_t allocUnitLargestFree = 0;
2504 // only allow one block at a time
2505 constexpr uint8_t maxRecordSize = 1;
2506
2507 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2508 allocUnitLargestFree, maxRecordSize);
2509}
2510
2511/** @brief implements the reserve SDR command
2512 * @returns IPMI completion code plus response data
2513 * - sdrReservationID
2514 */
2515ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2516{
2517 sdrReservationID++;
2518 if (sdrReservationID == 0)
2519 {
2520 sdrReservationID++;
2521 }
2522
2523 return ipmi::responseSuccess(sdrReservationID);
2524}
2525
2526ipmi::RspType<uint16_t, // next record ID
2527 std::vector<uint8_t> // payload
2528 >
2529 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2530 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2531{
2532 // reservation required for partial reads with non zero offset into
2533 // record
2534 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2535 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002536 phosphor::logging::log<phosphor::logging::level::ERR>(
2537 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002538 return ipmi::responseInvalidReservationId();
2539 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002540
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002541 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002542 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002543 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002544 phosphor::logging::log<phosphor::logging::level::ERR>(
2545 "ipmiStorageGetSDR: getSensorSubtree error");
2546 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002547 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002548
Willy Tu4eca2512022-06-20 21:14:51 -07002549 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2550
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002551 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002552 int nextRecordId = getSensorDataRecord(
2553 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2554 record, recordID, offset + bytesToRead);
2555
2556 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002557 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002558 phosphor::logging::log<phosphor::logging::level::ERR>(
2559 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002560 return ipmi::responseInvalidFieldRequest();
2561 }
Willy Tude54f482021-01-26 15:59:09 -08002562 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002563 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002564 if (!hdr)
2565 {
2566 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002567 "ipmiStorageGetSDR: record header is null");
2568 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002569 }
2570
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002571 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2572 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002573 if (offset >= sdrLength)
2574 {
2575 phosphor::logging::log<phosphor::logging::level::ERR>(
2576 "ipmiStorageGetSDR: offset is outside the record");
2577 return ipmi::responseParmOutOfRange();
2578 }
Willy Tude54f482021-01-26 15:59:09 -08002579 if (sdrLength < (offset + bytesToRead))
2580 {
2581 bytesToRead = sdrLength - offset;
2582 }
2583
2584 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2585 if (!respStart)
2586 {
2587 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002588 "ipmiStorageGetSDR: record is null");
2589 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002590 }
2591
2592 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002593
Willy Tude54f482021-01-26 15:59:09 -08002594 return ipmi::responseSuccess(nextRecordId, recordData);
2595}
adarshgrami042e9db2022-09-15 10:34:34 +05302596namespace dcmi
2597{
2598
Thang Tranb1416ef2023-08-02 13:57:09 +07002599std::tuple<uint8_t, // Total of instance sensors
2600 std::vector<sensorInfo> // The list of sensors
2601 >
2602 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2603 uint8_t entityInstance, uint8_t instanceStart)
2604{
2605 std::vector<sensorInfo> sensorList;
2606 uint8_t totalInstSensor = 0;
2607 auto match = ipmi::dcmi::validEntityId.find(entityId);
2608
2609 if (match == ipmi::dcmi::validEntityId.end())
2610 {
2611 return std::make_tuple(totalInstSensor, sensorList);
2612 }
2613
2614 auto& sensorTree = getSensorTree();
2615 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2616 {
2617 return std::make_tuple(totalInstSensor, sensorList);
2618 }
2619
2620 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2621
Willy Tu62e3ca82024-01-31 17:28:34 +00002622 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002623 for (const auto& sensor : sensorTree)
2624 {
2625 const std::string& sensorObjPath = sensor.first;
2626 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2627
2628 /*
2629 * In the DCMI specification, it only supports the sensor type is 0x01
2630 * (temperature type) for both Get Sensor Info and Get Temperature
2631 * Readings commands.
2632 */
2633 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2634 {
2635 continue;
2636 }
2637
2638 const auto& connection = sensor.second.begin()->first;
2639 DbusInterfaceMap sensorMap;
2640
2641 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2642 sensorMapSdrUpdatePeriod))
2643 {
2644 phosphor::logging::log<phosphor::logging::level::ERR>(
2645 "Failed to update sensor map for threshold sensor",
2646 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2647 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2648 continue;
2649 }
2650
2651 uint8_t entityIdValue = 0;
2652 uint8_t entityInstanceValue = 0;
2653
2654 /*
2655 * Get the Entity ID, Entity Instance information which are configured
2656 * in the Entity-Manger.
2657 */
2658 updateIpmiFromAssociation(
2659 sensorObjPath,
2660 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2661 sensorMap, entityIdValue, entityInstanceValue);
2662
2663 if (entityIdValue == match->first || entityIdValue == match->second)
2664 {
2665 totalInstSensor++;
2666
2667 /*
2668 * When Entity Instance parameter is not 0, we only get the first
2669 * sensor whose Entity Instance number is equal input Entity
2670 * Instance parameter.
2671 */
2672 if (entityInstance)
2673 {
2674 if (!sensorList.empty())
2675 {
2676 continue;
2677 }
2678
2679 if (entityInstanceValue == entityInstance)
2680 {
2681 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002682 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002683 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002684 ++invalidSensorNumberErrCount;
2685 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002686 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002687 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2688 recordId, entityIdValue,
2689 entityInstanceValue);
2690 }
2691 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002692 else if (entityInstanceValue >= instanceStart)
2693 {
2694 auto recordId = getSensorNumberFromPath(sensorObjPath);
2695 if (recordId == invalidSensorNumber)
2696 {
2697 ++invalidSensorNumberErrCount;
2698 continue;
2699 }
2700 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2701 recordId, entityIdValue,
2702 entityInstanceValue);
2703 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002704 }
2705 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002706 if (invalidSensorNumberErrCount != 0)
2707 {
2708 phosphor::logging::log<phosphor::logging::level::ERR>(
2709 std::format(
2710 "getSensorNumberFromPath returned invalidSensorNumber {} times",
2711 invalidSensorNumberErrCount)
2712 .data());
2713 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002714
2715 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2716 return first.entityInstance <= second.entityInstance;
2717 };
2718
2719 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2720
2721 return std::make_tuple(totalInstSensor, sensorList);
2722}
2723
Thang Tran3dad8262023-08-17 15:20:56 +07002724std::tuple<bool, // Reading result
2725 uint7_t, // Temp value
2726 bool> // Sign bit
2727 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2728{
2729 std::string service{};
2730 boost::system::error_code ec =
2731 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2732 if (ec.value())
2733 {
2734 return std::make_tuple(false, 0, false);
2735 }
2736
2737 ipmi::PropertyMap properties{};
2738 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2739 sensor::sensorInterface, properties);
2740 if (ec.value())
2741 {
2742 return std::make_tuple(false, 0, false);
2743 }
2744
2745 auto scaleIt = properties.find("Scale");
2746 double scaleVal = 0.0;
2747 if (scaleIt != properties.end())
2748 {
2749 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2750 }
2751
2752 auto tempValIt = properties.find("Value");
2753 double tempVal = 0.0;
2754 if (tempValIt == properties.end())
2755 {
2756 return std::make_tuple(false, 0, false);
2757 }
2758
2759 const double maxTemp = 127;
2760 double absTempVal = 0.0;
2761 bool signBit = false;
2762
2763 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2764 tempVal = std::pow(10, scaleVal) * tempVal;
2765 absTempVal = std::abs(tempVal);
2766 absTempVal = std::min(absTempVal, maxTemp);
2767 signBit = (tempVal < 0) ? true : false;
2768
2769 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2770}
2771
adarshgrami042e9db2022-09-15 10:34:34 +05302772ipmi::RspType<uint8_t, // No of instances for requested id
2773 uint8_t, // No of record ids in the response
2774 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2775 // IDs
2776 >
2777 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002778 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302779{
2780 auto match = ipmi::dcmi::validEntityId.find(entityId);
2781 if (match == ipmi::dcmi::validEntityId.end())
2782 {
2783 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2784
2785 return ipmi::responseInvalidFieldRequest();
2786 }
2787
2788 if (sensorType != ipmi::dcmi::temperatureSensorType)
2789 {
2790 log<level::ERR>("Invalid sensor type",
2791 entry("SENSOR_TYPE=%d", sensorType));
2792
2793 return ipmi::responseInvalidFieldRequest();
2794 }
adarshgrami042e9db2022-09-15 10:34:34 +05302795
2796 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002797 const auto& [totalSensorInst, sensorList] =
2798 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302799
Thang Tranb1416ef2023-08-02 13:57:09 +07002800 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302801 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002802 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2803 }
adarshgrami042e9db2022-09-15 10:34:34 +05302804
Thang Tranb1416ef2023-08-02 13:57:09 +07002805 /*
2806 * As DCMI specification, the maximum number of Record Ids of response data
2807 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2808 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2809 * in response data.
2810 */
2811 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2812
2813 for (const auto& sensor : sensorList)
2814 {
2815 sensorRec.emplace_back(sensor.recordId);
2816 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302817 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002818 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302819 }
2820 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002821
2822 return ipmi::responseSuccess(
2823 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302824}
Thang Tran3dad8262023-08-17 15:20:56 +07002825
2826ipmi::RspType<uint8_t, // No of instances for requested id
2827 uint8_t, // No of record ids in the response
2828 std::vector< // Temperature Data
2829 std::tuple<uint7_t, // Temperature value
2830 bool, // Sign bit
2831 uint8_t // Entity Instance of sensor
2832 >>>
2833 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2834 uint8_t entityId, uint8_t entityInstance,
2835 uint8_t instanceStart)
2836{
2837 auto match = ipmi::dcmi::validEntityId.find(entityId);
2838 if (match == ipmi::dcmi::validEntityId.end())
2839 {
2840 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2841
2842 return ipmi::responseInvalidFieldRequest();
2843 }
2844
2845 if (sensorType != ipmi::dcmi::temperatureSensorType)
2846 {
2847 log<level::ERR>("Invalid sensor type",
2848 entry("SENSOR_TYPE=%d", sensorType));
2849
2850 return ipmi::responseInvalidFieldRequest();
2851 }
2852
2853 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2854 const auto& [totalSensorInst, sensorList] =
2855 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2856
2857 if (sensorList.empty())
2858 {
2859 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2860 }
2861
2862 /*
2863 * As DCMI specification, the maximum number of Record Ids of response data
2864 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2865 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2866 * in response data.
2867 */
2868 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2869
2870 for (const auto& sensor : sensorList)
2871 {
2872 const auto& [readResult, tempVal,
2873 signBit] = readTemp(ctx, sensor.objectPath);
2874
2875 if (readResult)
2876 {
2877 tempReadingVal.emplace_back(
2878 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2879
2880 if (tempReadingVal.size() >= numOfRec)
2881 {
2882 break;
2883 }
2884 }
2885 }
2886
2887 return ipmi::responseSuccess(totalSensorInst,
2888 static_cast<uint8_t>(tempReadingVal.size()),
2889 tempReadingVal);
2890}
2891
adarshgrami042e9db2022-09-15 10:34:34 +05302892} // namespace dcmi
2893
Willy Tude54f482021-01-26 15:59:09 -08002894/* end storage commands */
2895
2896void registerSensorFunctions()
2897{
2898 // <Platform Event>
2899 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2900 ipmi::sensor_event::cmdPlatformEvent,
2901 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2902
Willy Tudbafbce2021-03-29 00:37:05 -07002903 // <Set Sensor Reading and Event Status>
2904 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2905 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2906 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002907
Willy Tude54f482021-01-26 15:59:09 -08002908 // <Get Sensor Reading>
2909 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2910 ipmi::sensor_event::cmdGetSensorReading,
2911 ipmi::Privilege::User, ipmiSenGetSensorReading);
2912
2913 // <Get Sensor Threshold>
2914 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2915 ipmi::sensor_event::cmdGetSensorThreshold,
2916 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2917
2918 // <Set Sensor Threshold>
2919 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2920 ipmi::sensor_event::cmdSetSensorThreshold,
2921 ipmi::Privilege::Operator,
2922 ipmiSenSetSensorThresholds);
2923
2924 // <Get Sensor Event Enable>
2925 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2926 ipmi::sensor_event::cmdGetSensorEventEnable,
2927 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2928
2929 // <Get Sensor Event Status>
2930 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2931 ipmi::sensor_event::cmdGetSensorEventStatus,
2932 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2933
2934 // register all storage commands for both Sensor and Storage command
2935 // versions
2936
2937 // <Get SDR Repository Info>
2938 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2939 ipmi::storage::cmdGetSdrRepositoryInfo,
2940 ipmi::Privilege::User,
2941 ipmiStorageGetSDRRepositoryInfo);
2942
2943 // <Get Device SDR Info>
2944 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2945 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2946 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2947
2948 // <Get SDR Allocation Info>
2949 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2950 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2951 ipmi::Privilege::User,
2952 ipmiStorageGetSDRAllocationInfo);
2953
2954 // <Reserve SDR Repo>
2955 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2956 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2957 ipmi::Privilege::User, ipmiStorageReserveSDR);
2958
2959 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2960 ipmi::storage::cmdReserveSdrRepository,
2961 ipmi::Privilege::User, ipmiStorageReserveSDR);
2962
2963 // <Get Sdr>
2964 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2965 ipmi::sensor_event::cmdGetDeviceSdr,
2966 ipmi::Privilege::User, ipmiStorageGetSDR);
2967
2968 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2969 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2970 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302971 // <Get DCMI Sensor Info>
2972 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2973 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002974 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302975 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002976 // <Get Temperature Readings>
2977 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2978 ipmi::dcmi::cmdGetTemperatureReadings,
2979 ipmi::Privilege::User,
2980 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002981}
2982} // namespace ipmi