blob: f3cd385e207534fcc752816cbdd600b336f2f431 [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 {
289 min = std::min(value, min);
290 }
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 {
297 max = std::max(value, max);
298 }
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 {
310 min = std::min(value, min);
311 }
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 {
318 max = std::max(value, max);
319 }
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 {
1532 assertionEnabledLsb |= static_cast<uint8_t>(
1533 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1534 deassertionEnabledLsb |= static_cast<uint8_t>(
1535 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1536 }
1537 if (warningLow != warningMap.end())
1538 {
1539 assertionEnabledLsb |= static_cast<uint8_t>(
1540 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1541 deassertionEnabledLsb |= static_cast<uint8_t>(
1542 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1543 }
1544 }
1545 if (criticalInterface != sensorMap.end())
1546 {
1547 auto& criticalMap = criticalInterface->second;
1548
1549 auto criticalHigh = criticalMap.find("CriticalHigh");
1550 auto criticalLow = criticalMap.find("CriticalLow");
1551
1552 if (criticalHigh != criticalMap.end())
1553 {
1554 assertionEnabledMsb |= static_cast<uint8_t>(
1555 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1556 deassertionEnabledMsb |= static_cast<uint8_t>(
1557 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1558 }
1559 if (criticalLow != criticalMap.end())
1560 {
1561 assertionEnabledLsb |= static_cast<uint8_t>(
1562 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1563 deassertionEnabledLsb |= static_cast<uint8_t>(
1564 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1565 }
1566 }
1567 }
1568
1569 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1570 assertionEnabledMsb, deassertionEnabledLsb,
1571 deassertionEnabledMsb);
1572}
1573
1574/** @brief implements the get Sensor event status command
1575 * @param sensorNumber - sensor number, FFh = reserved
1576 *
1577 * @returns IPMI completion code plus response data
1578 * - sensorEventStatus - Sensor Event messages state
1579 * - assertions - Assertion event messages
1580 * - deassertions - Deassertion event messages
1581 */
1582ipmi::RspType<uint8_t, // sensorEventStatus
1583 std::bitset<16>, // assertions
1584 std::bitset<16> // deassertion
1585 >
1586 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1587{
1588 if (sensorNum == reservedSensorNumber)
1589 {
1590 return ipmi::responseInvalidFieldRequest();
1591 }
1592
1593 std::string connection;
1594 std::string path;
1595 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1596 if (status)
1597 {
1598 phosphor::logging::log<phosphor::logging::level::ERR>(
1599 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1600 phosphor::logging::entry("SENSOR=%d", sensorNum));
1601 return ipmi::response(status);
1602 }
1603
Scron Chang2703b022021-07-06 15:47:45 +08001604#ifdef FEATURE_HYBRID_SENSORS
1605 if (auto sensor = findStaticSensor(path);
1606 sensor != ipmi::sensor::sensors.end() &&
1607 getSensorEventTypeFromPath(path) !=
1608 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1609 {
1610 auto response = ipmi::sensor::get::mapDbusToAssertion(
1611 sensor->second, path, sensor->second.sensorInterface);
1612 std::bitset<16> assertions;
1613 // deassertions are not used.
1614 std::bitset<16> deassertions = 0;
1615 uint8_t sensorEventStatus;
1616 if (response.readingOrStateUnavailable)
1617 {
1618 sensorEventStatus |= static_cast<uint8_t>(
1619 IPMISensorReadingByte2::readingStateUnavailable);
1620 }
1621 if (response.scanningEnabled)
1622 {
1623 sensorEventStatus |= static_cast<uint8_t>(
1624 IPMISensorReadingByte2::sensorScanningEnable);
1625 }
1626 if (response.allEventMessagesEnabled)
1627 {
1628 sensorEventStatus |= static_cast<uint8_t>(
1629 IPMISensorReadingByte2::eventMessagesEnable);
1630 }
1631 assertions |= response.discreteReadingSensorStates << 8;
1632 assertions |= response.thresholdLevelsStates;
1633 return ipmi::responseSuccess(sensorEventStatus, assertions,
1634 deassertions);
1635 }
1636#endif
1637
Willy Tude54f482021-01-26 15:59:09 -08001638 DbusInterfaceMap sensorMap;
1639 if (!getSensorMap(ctx, connection, path, sensorMap))
1640 {
1641 phosphor::logging::log<phosphor::logging::level::ERR>(
1642 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1643 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1644 return ipmi::responseResponseError();
1645 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001646
1647 uint8_t sensorEventStatus =
1648 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1649 std::bitset<16> assertions = 0;
1650 std::bitset<16> deassertions = 0;
1651
1652 // handle VR typed sensor
1653 auto vrInterface = sensorMap.find(sensor::vrInterface);
1654 if (vrInterface != sensorMap.end())
1655 {
1656 if (!sensor::getVrEventStatus(ctx, connection, path,
1657 vrInterface->second, assertions))
1658 {
1659 return ipmi::responseResponseError();
1660 }
1661
1662 // both Event Message and Sensor Scanning are disable for VR.
1663 sensorEventStatus = 0;
1664 return ipmi::responseSuccess(sensorEventStatus, assertions,
1665 deassertions);
1666 }
1667
Willy Tude54f482021-01-26 15:59:09 -08001668 auto warningInterface =
1669 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1670 auto criticalInterface =
1671 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1672
Willy Tude54f482021-01-26 15:59:09 -08001673 std::optional<bool> criticalDeassertHigh =
1674 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1675 std::optional<bool> criticalDeassertLow =
1676 thresholdDeassertMap[path]["CriticalAlarmLow"];
1677 std::optional<bool> warningDeassertHigh =
1678 thresholdDeassertMap[path]["WarningAlarmHigh"];
1679 std::optional<bool> warningDeassertLow =
1680 thresholdDeassertMap[path]["WarningAlarmLow"];
1681
Willy Tude54f482021-01-26 15:59:09 -08001682 if (criticalDeassertHigh && !*criticalDeassertHigh)
1683 {
1684 deassertions.set(static_cast<size_t>(
1685 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1686 }
1687 if (criticalDeassertLow && !*criticalDeassertLow)
1688 {
1689 deassertions.set(static_cast<size_t>(
1690 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1691 }
1692 if (warningDeassertHigh && !*warningDeassertHigh)
1693 {
1694 deassertions.set(static_cast<size_t>(
1695 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1696 }
1697 if (warningDeassertLow && !*warningDeassertLow)
1698 {
1699 deassertions.set(static_cast<size_t>(
1700 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1701 }
1702 if ((warningInterface != sensorMap.end()) ||
1703 (criticalInterface != sensorMap.end()))
1704 {
1705 sensorEventStatus = static_cast<size_t>(
1706 IPMISensorEventEnableByte2::eventMessagesEnable);
1707 if (warningInterface != sensorMap.end())
1708 {
1709 auto& warningMap = warningInterface->second;
1710
1711 auto warningHigh = warningMap.find("WarningAlarmHigh");
1712 auto warningLow = warningMap.find("WarningAlarmLow");
1713 auto warningHighAlarm = false;
1714 auto warningLowAlarm = false;
1715
1716 if (warningHigh != warningMap.end())
1717 {
1718 warningHighAlarm = std::get<bool>(warningHigh->second);
1719 }
1720 if (warningLow != warningMap.end())
1721 {
1722 warningLowAlarm = std::get<bool>(warningLow->second);
1723 }
1724 if (warningHighAlarm)
1725 {
1726 assertions.set(
1727 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1728 upperNonCriticalGoingHigh));
1729 }
1730 if (warningLowAlarm)
1731 {
1732 assertions.set(
1733 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1734 lowerNonCriticalGoingLow));
1735 }
1736 }
1737 if (criticalInterface != sensorMap.end())
1738 {
1739 auto& criticalMap = criticalInterface->second;
1740
1741 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1742 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1743 auto criticalHighAlarm = false;
1744 auto criticalLowAlarm = false;
1745
1746 if (criticalHigh != criticalMap.end())
1747 {
1748 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1749 }
1750 if (criticalLow != criticalMap.end())
1751 {
1752 criticalLowAlarm = std::get<bool>(criticalLow->second);
1753 }
1754 if (criticalHighAlarm)
1755 {
1756 assertions.set(
1757 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1758 upperCriticalGoingHigh));
1759 }
1760 if (criticalLowAlarm)
1761 {
1762 assertions.set(static_cast<size_t>(
1763 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1764 }
1765 }
1766 }
1767
1768 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1769}
1770
Willy Tu38e7a2b2021-03-29 15:09:56 -07001771// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001772void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1773 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001774{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001775 get_sdr::header::set_record_id(
1776 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1777
Willy Tu38e7a2b2021-03-29 15:09:56 -07001778 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1779 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1780
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001781 record.header.sdr_version = ipmiSdrVersion;
1782 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1783 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1784 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001785 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001786 record.key.owner_lun = lun;
1787 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001788}
Willy Tu4eca2512022-06-20 21:14:51 -07001789bool constructSensorSdr(
1790 ipmi::Context::ptr ctx,
1791 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1792 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1793 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001794{
Hao Jiange39d4d82021-04-16 17:02:40 -07001795 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1796
1797 DbusInterfaceMap sensorMap;
1798 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1799 {
1800 phosphor::logging::log<phosphor::logging::level::ERR>(
1801 "Failed to update sensor map for threshold sensor",
1802 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1803 phosphor::logging::entry("PATH=%s", path.c_str()));
1804 return false;
1805 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001806
1807 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1808 record.body.sensor_type = getSensorTypeFromPath(path);
1809 std::string type = getSensorTypeStringFromPath(path);
1810 auto typeCstr = type.c_str();
1811 auto findUnits = sensorUnits.find(typeCstr);
1812 if (findUnits != sensorUnits.end())
1813 {
1814 record.body.sensor_units_2_base =
1815 static_cast<uint8_t>(findUnits->second);
1816 } // else default 0x0 unspecified
1817
1818 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1819
Hao Jiangd2afd052020-12-10 15:09:32 -08001820 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001821 if (sensorObject == sensorMap.end())
1822 {
1823 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001824 "constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001825 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001826 }
1827
1828 uint8_t entityId = 0;
1829 uint8_t entityInstance = 0x01;
1830
1831 // follow the association chain to get the parent board's entityid and
1832 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001833 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1834 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001835
1836 record.body.entity_id = entityId;
1837 record.body.entity_instance = entityInstance;
1838
Shakeeb Pasha93889722021-10-14 10:20:13 +05301839 double max = 0;
1840 double min = 0;
1841 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001842
1843 int16_t mValue = 0;
1844 int8_t rExp = 0;
1845 int16_t bValue = 0;
1846 int8_t bExp = 0;
1847 bool bSigned = false;
1848
1849 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1850 {
1851 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001852 "constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001853 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001854 }
1855
1856 // The record.body is a struct SensorDataFullRecordBody
1857 // from sensorhandler.hpp in phosphor-ipmi-host.
1858 // The meaning of these bits appears to come from
1859 // table 43.1 of the IPMI spec.
1860 // The above 5 sensor attributes are stuffed in as follows:
1861 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1862 // Byte 22-24 are for other purposes
1863 // Byte 25 = MMMMMMMM = LSB of M
1864 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1865 // Byte 27 = BBBBBBBB = LSB of B
1866 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1867 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1868 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1869
1870 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1871 record.body.m_lsb = mValue & 0xFF;
1872
1873 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1874 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1875
1876 // move the smallest bit of the MSB into place (bit 9)
1877 // the MSbs are bits 7:8 in m_msb_and_tolerance
1878 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1879
1880 record.body.b_lsb = bValue & 0xFF;
1881
1882 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1883 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1884
1885 // move the smallest bit of the MSB into place (bit 9)
1886 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1887 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1888
1889 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1890 uint8_t rExpBits = rExp & 0x07;
1891
1892 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1893 uint8_t bExpBits = bExp & 0x07;
1894
1895 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001896 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1897 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001898
1899 // Set the analog reading byte interpretation accordingly
1900 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1901
1902 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1903 // These seem redundant, but derivable from the above 5 attributes
1904 // Original comment said "todo fill out rest of units"
1905
1906 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001907 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001908 get_sdr::body::set_id_strlen(name.size(), &record.body);
1909 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001910 std::memcpy(record.body.id_string, name.c_str(),
1911 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001912
Josh Lehana55c9532020-10-28 21:59:06 -07001913 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001914 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001915
Jie Yangf0a89942021-07-29 15:30:25 -07001916 bool sensorSettable = false;
1917 auto mutability =
1918 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1919 if (mutability != sensorMap.end())
1920 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001921 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1922 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001923 }
1924 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1925
1926 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001927 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001928
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001929 IPMIThresholds thresholdData;
1930 try
1931 {
1932 thresholdData = getIPMIThresholds(sensorMap);
1933 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001934 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001935 {
1936 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001937 "constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001938 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001939 }
1940
1941 if (thresholdData.criticalHigh)
1942 {
1943 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1944 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1945 IPMISensorEventEnableThresholds::criticalThreshold);
1946 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1947 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1948 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1949 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1950 record.body.discrete_reading_setting_mask[0] |=
1951 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1952 }
1953 if (thresholdData.warningHigh)
1954 {
1955 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1956 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1957 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1958 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1959 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1960 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1961 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1962 record.body.discrete_reading_setting_mask[0] |=
1963 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1964 }
1965 if (thresholdData.criticalLow)
1966 {
1967 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1968 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1969 IPMISensorEventEnableThresholds::criticalThreshold);
1970 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1971 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1972 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1973 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1974 record.body.discrete_reading_setting_mask[0] |=
1975 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1976 }
1977 if (thresholdData.warningLow)
1978 {
1979 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1980 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1981 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1982 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1983 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1984 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1985 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1986 record.body.discrete_reading_setting_mask[0] |=
1987 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1988 }
1989
1990 // everything that is readable is setable
1991 record.body.discrete_reading_setting_mask[1] =
1992 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001993 return true;
1994}
1995
Scron Chang2703b022021-07-06 15:47:45 +08001996#ifdef FEATURE_HYBRID_SENSORS
1997// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001998void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001999 uint16_t recordID,
2000 ipmi::sensor::IdInfoMap::const_iterator sensor,
2001 get_sdr::SensorDataFullRecord& record)
2002{
2003 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2004
2005 record.body.entity_id = sensor->second.entityType;
2006 record.body.sensor_type = sensor->second.sensorType;
2007 record.body.event_reading_type = sensor->second.sensorReadingType;
2008 record.body.entity_instance = sensor->second.instance;
2009 if (ipmi::sensor::Mutability::Write ==
2010 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2011 {
2012 get_sdr::body::init_settable_state(true, &(record.body));
2013 }
2014
2015 auto id_string = sensor->second.sensorName;
2016
2017 if (id_string.empty())
2018 {
2019 id_string = sensor->second.sensorNameFunc(sensor->second);
2020 }
2021
2022 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2023 {
2024 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2025 &(record.body));
2026 }
2027 else
2028 {
2029 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2030 }
Paul Fertser51136982022-08-18 12:36:41 +00002031 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002032 std::strncpy(record.body.id_string, id_string.c_str(),
2033 get_sdr::body::get_id_strlen(&(record.body)));
2034}
2035#endif
2036
Hao Jiange39d4d82021-04-16 17:02:40 -07002037// Construct type 3 SDR header and key (for VR and other discrete sensors)
2038void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2039 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002040{
2041 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2042 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2043
2044 get_sdr::header::set_record_id(
2045 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2046
2047 record.header.sdr_version = ipmiSdrVersion;
2048 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2049 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2050 sizeof(get_sdr::SensorDataRecordHeader);
2051 record.key.owner_id = bmcI2CAddr;
2052 record.key.owner_lun = lun;
2053 record.key.sensor_number = sensornumber;
2054
2055 record.body.entity_id = 0x00;
2056 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002057}
Willy Tu61992ad2021-03-29 15:33:20 -07002058
Hao Jiange39d4d82021-04-16 17:02:40 -07002059// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002060bool constructVrSdr(ipmi::Context::ptr ctx,
2061 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2062 uint16_t sensorNum, uint16_t recordID,
2063 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002064 get_sdr::SensorDataEventRecord& record)
2065{
Hao Jiange39d4d82021-04-16 17:02:40 -07002066 constructEventSdrHeaderKey(sensorNum, recordID, record);
2067
2068 DbusInterfaceMap sensorMap;
2069 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2070 {
2071 phosphor::logging::log<phosphor::logging::level::ERR>(
2072 "Failed to update sensor map for VR sensor",
2073 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2074 phosphor::logging::entry("PATH=%s", path.c_str()));
2075 return false;
2076 }
Willy Tu61992ad2021-03-29 15:33:20 -07002077 // follow the association chain to get the parent board's entityid and
2078 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002079 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2080 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002081 record.body.entity_instance);
2082
2083 // Sensor type is hardcoded as a module/board type instead of parsing from
2084 // sensor path. This is because VR control is allocated in an independent
2085 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2086 // types.
2087 static constexpr const uint8_t module_board_type = 0x15;
2088 record.body.sensor_type = module_board_type;
2089 record.body.event_reading_type = 0x00;
2090
2091 record.body.sensor_record_sharing_1 = 0x00;
2092 record.body.sensor_record_sharing_2 = 0x00;
2093
2094 // populate sensor name from path
2095 auto name = sensor::parseSdrIdFromPath(path);
2096 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002097 get_sdr::body::set_id_strlen(nameSize, &record.body);
2098 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002099 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2100 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2101
2102 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002103 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002104
2105 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002106}
2107
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002108uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002109{
2110 return std::min(getSensorTree().size(), maxIPMISensors);
2111}
2112
Willy Tu4eca2512022-06-20 21:14:51 -07002113static int getSensorDataRecord(
2114 ipmi::Context::ptr ctx,
2115 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2116 std::vector<uint8_t>& recordData, uint16_t recordID,
2117 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002118{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302119 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002120 size_t lastRecord = ipmi::getNumberOfSensors() +
2121 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2122 uint16_t nextRecord(recordID + 1);
2123
Willy Tu38e7a2b2021-03-29 15:09:56 -07002124 if (recordID == lastRecordIndex)
2125 {
2126 recordID = lastRecord;
2127 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002128 if (recordID == lastRecord)
2129 {
2130 nextRecord = lastRecordIndex;
2131 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002132 if (recordID > lastRecord)
2133 {
2134 phosphor::logging::log<phosphor::logging::level::ERR>(
2135 "getSensorDataRecord: recordID > lastRecord error");
2136 return GENERAL_ERROR;
2137 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002138 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002139 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002140 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2141 recordData);
2142 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002143 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002144 // phosphor::logging::log<phosphor::logging::level::ERR>(
2145 // "getSensorDataRecord: Error getting custom record");
2146 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002147 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002148 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002149 }
2150
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002151 // Perform a incremental scan of the SDR Record ID's and translate the
2152 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2153 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2154 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2155 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002156 std::string connection;
2157 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002158 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002159 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002160 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002161 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002162 // LUN 0 has one reserved sensor number. Compensate here by adding one
2163 // to the record ID
2164 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002165 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002166 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002167 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002168 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002169 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2170 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2171 // rules governing its use.
2172 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002173 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002174 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002175
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002176 auto status = getSensorConnection(ctx,
2177 static_cast<uint8_t>(sensNumFromRecID),
2178 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002179 if (status)
2180 {
2181 phosphor::logging::log<phosphor::logging::level::ERR>(
2182 "getSensorDataRecord: getSensorConnection error");
2183 return GENERAL_ERROR;
2184 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002185 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002186 // Return an error on LUN 2 assingments, and any sensor number beyond the
2187 // range of LUN 3
2188 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2189 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002190 {
2191 phosphor::logging::log<phosphor::logging::level::ERR>(
2192 "getSensorDataRecord: invalidSensorNumber");
2193 return GENERAL_ERROR;
2194 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002195 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2196 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2197
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002198 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2199 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002200 {
2201 phosphor::logging::log<phosphor::logging::level::ERR>(
2202 "getSensorDataRecord: sensor record mismatch");
2203 return GENERAL_ERROR;
2204 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002205
Willy Tu38e7a2b2021-03-29 15:09:56 -07002206 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002207 if (std::find(interfaces.begin(), interfaces.end(),
2208 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002209 {
Willy Tu11d68892022-01-20 10:37:34 -08002210 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002211
Hao Jiange39d4d82021-04-16 17:02:40 -07002212 // If the request doesn't read SDR body, construct only header and key
2213 // part to avoid additional DBus transaction.
2214 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2215 {
2216 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2217 }
Willy Tu4eca2512022-06-20 21:14:51 -07002218 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2219 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002220 {
2221 return GENERAL_ERROR;
2222 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002223
selvaganapathi7b2e5502023-02-14 07:10:47 +05302224 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2225 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002226
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002227 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002228 }
Willy Tu61992ad2021-03-29 15:33:20 -07002229
Scron Chang2703b022021-07-06 15:47:45 +08002230#ifdef FEATURE_HYBRID_SENSORS
2231 if (auto sensor = findStaticSensor(path);
2232 sensor != ipmi::sensor::sensors.end() &&
2233 getSensorEventTypeFromPath(path) !=
2234 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2235 {
Willy Tu11d68892022-01-20 10:37:34 -08002236 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002237
2238 // 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 }
2244 else
2245 {
2246 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2247 }
2248
selvaganapathi7b2e5502023-02-14 07:10:47 +05302249 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2250 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002251
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002252 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002253 }
2254#endif
2255
Willy Tu61992ad2021-03-29 15:33:20 -07002256 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002257 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2258 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002259 {
Willy Tu11d68892022-01-20 10:37:34 -08002260 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002261
Hao Jiange39d4d82021-04-16 17:02:40 -07002262 // If the request doesn't read SDR body, construct only header and key
2263 // part to avoid additional DBus transaction.
2264 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2265 {
2266 constructEventSdrHeaderKey(sensorNum, recordID, record);
2267 }
Willy Tu4eca2512022-06-20 21:14:51 -07002268 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2269 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002270 {
2271 return GENERAL_ERROR;
2272 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302273 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2274 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002275 }
2276
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002277 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002278}
2279
2280/** @brief implements the get SDR Info command
2281 * @param count - Operation
2282 *
2283 * @returns IPMI completion code plus response data
2284 * - sdrCount - sensor/SDR count
2285 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2286 */
2287static ipmi::RspType<uint8_t, // respcount
2288 uint8_t, // dynamic population flags
2289 uint32_t // last time a sensor was added
2290 >
2291 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2292 std::optional<uint8_t> count)
2293{
2294 auto& sensorTree = getSensorTree();
2295 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002296 uint16_t recordID = 0;
2297 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002298 // Sensors are dynamically allocated, and there is at least one LUN
2299 uint8_t lunsAndDynamicPopulation = 0x80;
2300 constexpr uint8_t getSdrCount = 0x01;
2301 constexpr uint8_t getSensorCount = 0x00;
2302
2303 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2304 {
2305 return ipmi::responseResponseError();
2306 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002307 uint16_t numSensors = ipmi::getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002308 if (count.value_or(0) == getSdrCount)
2309 {
Willy Tu4eca2512022-06-20 21:14:51 -07002310 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2311
Harvey Wu75893062023-03-22 17:17:31 +08002312 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002313 {
2314 recordID += maxSensorsPerLUN;
2315 }
Harvey Wu75893062023-03-22 17:17:31 +08002316 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002317 {
2318 recordID += maxSensorsPerLUN * 2;
2319 }
2320
Harvey Wu75893062023-03-22 17:17:31 +08002321 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2322 // assigned to the LUN
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002323 while (getSensorDataRecord(ctx,
2324 ipmiDecoratorPaths.value_or(
2325 std::unordered_set<std::string>()),
2326 record, recordID++) >= 0)
Willy Tude54f482021-01-26 15:59:09 -08002327 {
2328 get_sdr::SensorDataRecordHeader* hdr =
2329 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002330 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302331 if (!hdr)
2332 {
2333 continue;
2334 }
2335
2336 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002337 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002338 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002339 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002340 record.data());
2341 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002342 {
2343 sdrCount++;
2344 }
2345 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302346 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2347 {
2348 get_sdr::SensorDataCompactRecord* recordData =
2349 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2350 record.data());
2351 if (ctx->lun == recordData->key.owner_lun)
2352 {
2353 sdrCount++;
2354 }
2355 }
Harvey Wua3476272023-03-22 10:09:38 +08002356 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2357 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302358 {
2359 sdrCount++;
2360 }
Harvey Wua3476272023-03-22 10:09:38 +08002361
2362 // Because response count data is 1 byte, so sdrCount need to avoid
2363 // overflow.
2364 if (sdrCount == maxSensorsPerLUN)
2365 {
2366 break;
2367 }
Willy Tude54f482021-01-26 15:59:09 -08002368 }
2369 }
2370 else if (count.value_or(0) == getSensorCount)
2371 {
2372 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002373 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002374 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002375 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2376 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002377 }
Harvey Wu75893062023-03-22 17:17:31 +08002378 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002379 {
2380 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2381 ? maxSensorsPerLUN
2382 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2383 }
Harvey Wu75893062023-03-22 17:17:31 +08002384 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002385 {
2386 if (numSensors <= maxIPMISensors)
2387 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002388 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2389 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002390 }
2391 else
2392 {
2393 // error
2394 throw std::out_of_range(
2395 "Maximum number of IPMI sensors exceeded.");
2396 }
2397 }
2398 }
2399 else
2400 {
2401 return ipmi::responseInvalidFieldRequest();
2402 }
2403
2404 // Get Sensor count. This returns the number of sensors
2405 if (numSensors > 0)
2406 {
2407 lunsAndDynamicPopulation |= 1;
2408 }
2409 if (numSensors > maxSensorsPerLUN)
2410 {
2411 lunsAndDynamicPopulation |= 2;
2412 }
2413 if (numSensors >= (maxSensorsPerLUN * 2))
2414 {
2415 lunsAndDynamicPopulation |= 8;
2416 }
2417 if (numSensors > maxIPMISensors)
2418 {
2419 // error
2420 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2421 }
2422
2423 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2424 sdrLastAdd);
2425}
2426
2427/* end sensor commands */
2428
2429/* storage commands */
2430
2431ipmi::RspType<uint8_t, // sdr version
2432 uint16_t, // record count
2433 uint16_t, // free space
2434 uint32_t, // most recent addition
2435 uint32_t, // most recent erase
2436 uint8_t // operationSupport
2437 >
2438 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2439{
Willy Tude54f482021-01-26 15:59:09 -08002440 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Johnathan Mantey31c1ecd2024-06-20 11:10:41 -07002441 uint16_t recordCount = ipmi::getNumberOfSensors() +
2442 ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002443
2444 uint8_t operationSupport = static_cast<uint8_t>(
2445 SdrRepositoryInfoOps::overflow); // write not supported
2446
2447 operationSupport |=
2448 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2449 operationSupport |= static_cast<uint8_t>(
2450 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2451 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2452 unspecifiedFreeSpace, sdrLastAdd,
2453 sdrLastRemove, operationSupport);
2454}
2455
2456/** @brief implements the get SDR allocation info command
2457 *
2458 * @returns IPMI completion code plus response data
2459 * - allocUnits - Number of possible allocation units
2460 * - allocUnitSize - Allocation unit size in bytes.
2461 * - allocUnitFree - Number of free allocation units
2462 * - allocUnitLargestFree - Largest free block in allocation units
2463 * - maxRecordSize - Maximum record size in allocation units.
2464 */
2465ipmi::RspType<uint16_t, // allocUnits
2466 uint16_t, // allocUnitSize
2467 uint16_t, // allocUnitFree
2468 uint16_t, // allocUnitLargestFree
2469 uint8_t // maxRecordSize
2470 >
2471 ipmiStorageGetSDRAllocationInfo()
2472{
2473 // 0000h unspecified number of alloc units
2474 constexpr uint16_t allocUnits = 0;
2475
2476 constexpr uint16_t allocUnitFree = 0;
2477 constexpr uint16_t allocUnitLargestFree = 0;
2478 // only allow one block at a time
2479 constexpr uint8_t maxRecordSize = 1;
2480
2481 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2482 allocUnitLargestFree, maxRecordSize);
2483}
2484
2485/** @brief implements the reserve SDR command
2486 * @returns IPMI completion code plus response data
2487 * - sdrReservationID
2488 */
2489ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2490{
2491 sdrReservationID++;
2492 if (sdrReservationID == 0)
2493 {
2494 sdrReservationID++;
2495 }
2496
2497 return ipmi::responseSuccess(sdrReservationID);
2498}
2499
2500ipmi::RspType<uint16_t, // next record ID
2501 std::vector<uint8_t> // payload
2502 >
2503 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2504 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2505{
2506 // reservation required for partial reads with non zero offset into
2507 // record
2508 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2509 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002510 phosphor::logging::log<phosphor::logging::level::ERR>(
2511 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002512 return ipmi::responseInvalidReservationId();
2513 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002514
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002515 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002516 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002517 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002518 phosphor::logging::log<phosphor::logging::level::ERR>(
2519 "ipmiStorageGetSDR: getSensorSubtree error");
2520 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002521 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002522
Willy Tu4eca2512022-06-20 21:14:51 -07002523 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2524
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002525 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002526 int nextRecordId = getSensorDataRecord(
2527 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2528 record, recordID, offset + bytesToRead);
2529
2530 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002531 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002532 phosphor::logging::log<phosphor::logging::level::ERR>(
2533 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002534 return ipmi::responseInvalidFieldRequest();
2535 }
Willy Tude54f482021-01-26 15:59:09 -08002536 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002537 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002538 if (!hdr)
2539 {
2540 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002541 "ipmiStorageGetSDR: record header is null");
2542 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002543 }
2544
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002545 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2546 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002547 if (offset >= sdrLength)
2548 {
2549 phosphor::logging::log<phosphor::logging::level::ERR>(
2550 "ipmiStorageGetSDR: offset is outside the record");
2551 return ipmi::responseParmOutOfRange();
2552 }
Willy Tude54f482021-01-26 15:59:09 -08002553 if (sdrLength < (offset + bytesToRead))
2554 {
2555 bytesToRead = sdrLength - offset;
2556 }
2557
2558 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2559 if (!respStart)
2560 {
2561 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002562 "ipmiStorageGetSDR: record is null");
2563 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002564 }
2565
2566 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002567
Willy Tude54f482021-01-26 15:59:09 -08002568 return ipmi::responseSuccess(nextRecordId, recordData);
2569}
adarshgrami042e9db2022-09-15 10:34:34 +05302570namespace dcmi
2571{
2572
Thang Tranb1416ef2023-08-02 13:57:09 +07002573std::tuple<uint8_t, // Total of instance sensors
2574 std::vector<sensorInfo> // The list of sensors
2575 >
2576 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2577 uint8_t entityInstance, uint8_t instanceStart)
2578{
2579 std::vector<sensorInfo> sensorList;
2580 uint8_t totalInstSensor = 0;
2581 auto match = ipmi::dcmi::validEntityId.find(entityId);
2582
2583 if (match == ipmi::dcmi::validEntityId.end())
2584 {
2585 return std::make_tuple(totalInstSensor, sensorList);
2586 }
2587
2588 auto& sensorTree = getSensorTree();
2589 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2590 {
2591 return std::make_tuple(totalInstSensor, sensorList);
2592 }
2593
2594 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2595
Willy Tu62e3ca82024-01-31 17:28:34 +00002596 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002597 for (const auto& sensor : sensorTree)
2598 {
2599 const std::string& sensorObjPath = sensor.first;
2600 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2601
2602 /*
2603 * In the DCMI specification, it only supports the sensor type is 0x01
2604 * (temperature type) for both Get Sensor Info and Get Temperature
2605 * Readings commands.
2606 */
2607 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2608 {
2609 continue;
2610 }
2611
2612 const auto& connection = sensor.second.begin()->first;
2613 DbusInterfaceMap sensorMap;
2614
2615 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2616 sensorMapSdrUpdatePeriod))
2617 {
2618 phosphor::logging::log<phosphor::logging::level::ERR>(
2619 "Failed to update sensor map for threshold sensor",
2620 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2621 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2622 continue;
2623 }
2624
2625 uint8_t entityIdValue = 0;
2626 uint8_t entityInstanceValue = 0;
2627
2628 /*
2629 * Get the Entity ID, Entity Instance information which are configured
2630 * in the Entity-Manger.
2631 */
2632 updateIpmiFromAssociation(
2633 sensorObjPath,
2634 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2635 sensorMap, entityIdValue, entityInstanceValue);
2636
2637 if (entityIdValue == match->first || entityIdValue == match->second)
2638 {
2639 totalInstSensor++;
2640
2641 /*
2642 * When Entity Instance parameter is not 0, we only get the first
2643 * sensor whose Entity Instance number is equal input Entity
2644 * Instance parameter.
2645 */
2646 if (entityInstance)
2647 {
2648 if (!sensorList.empty())
2649 {
2650 continue;
2651 }
2652
2653 if (entityInstanceValue == entityInstance)
2654 {
2655 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002656 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002657 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002658 ++invalidSensorNumberErrCount;
2659 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002660 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002661 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2662 recordId, entityIdValue,
2663 entityInstanceValue);
2664 }
2665 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002666 else if (entityInstanceValue >= instanceStart)
2667 {
2668 auto recordId = getSensorNumberFromPath(sensorObjPath);
2669 if (recordId == invalidSensorNumber)
2670 {
2671 ++invalidSensorNumberErrCount;
2672 continue;
2673 }
2674 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2675 recordId, entityIdValue,
2676 entityInstanceValue);
2677 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002678 }
2679 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002680 if (invalidSensorNumberErrCount != 0)
2681 {
2682 phosphor::logging::log<phosphor::logging::level::ERR>(
2683 std::format(
2684 "getSensorNumberFromPath returned invalidSensorNumber {} times",
2685 invalidSensorNumberErrCount)
2686 .data());
2687 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002688
2689 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2690 return first.entityInstance <= second.entityInstance;
2691 };
2692
2693 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2694
2695 return std::make_tuple(totalInstSensor, sensorList);
2696}
2697
Thang Tran3dad8262023-08-17 15:20:56 +07002698std::tuple<bool, // Reading result
2699 uint7_t, // Temp value
2700 bool> // Sign bit
2701 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2702{
2703 std::string service{};
2704 boost::system::error_code ec =
2705 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2706 if (ec.value())
2707 {
2708 return std::make_tuple(false, 0, false);
2709 }
2710
2711 ipmi::PropertyMap properties{};
2712 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2713 sensor::sensorInterface, properties);
2714 if (ec.value())
2715 {
2716 return std::make_tuple(false, 0, false);
2717 }
2718
2719 auto scaleIt = properties.find("Scale");
2720 double scaleVal = 0.0;
2721 if (scaleIt != properties.end())
2722 {
2723 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2724 }
2725
2726 auto tempValIt = properties.find("Value");
2727 double tempVal = 0.0;
2728 if (tempValIt == properties.end())
2729 {
2730 return std::make_tuple(false, 0, false);
2731 }
2732
2733 const double maxTemp = 127;
2734 double absTempVal = 0.0;
2735 bool signBit = false;
2736
2737 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2738 tempVal = std::pow(10, scaleVal) * tempVal;
2739 absTempVal = std::abs(tempVal);
2740 absTempVal = std::min(absTempVal, maxTemp);
2741 signBit = (tempVal < 0) ? true : false;
2742
2743 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2744}
2745
adarshgrami042e9db2022-09-15 10:34:34 +05302746ipmi::RspType<uint8_t, // No of instances for requested id
2747 uint8_t, // No of record ids in the response
2748 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2749 // IDs
2750 >
2751 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002752 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302753{
2754 auto match = ipmi::dcmi::validEntityId.find(entityId);
2755 if (match == ipmi::dcmi::validEntityId.end())
2756 {
2757 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2758
2759 return ipmi::responseInvalidFieldRequest();
2760 }
2761
2762 if (sensorType != ipmi::dcmi::temperatureSensorType)
2763 {
2764 log<level::ERR>("Invalid sensor type",
2765 entry("SENSOR_TYPE=%d", sensorType));
2766
2767 return ipmi::responseInvalidFieldRequest();
2768 }
adarshgrami042e9db2022-09-15 10:34:34 +05302769
2770 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002771 const auto& [totalSensorInst, sensorList] =
2772 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302773
Thang Tranb1416ef2023-08-02 13:57:09 +07002774 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302775 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002776 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2777 }
adarshgrami042e9db2022-09-15 10:34:34 +05302778
Thang Tranb1416ef2023-08-02 13:57:09 +07002779 /*
2780 * As DCMI specification, the maximum number of Record Ids of response data
2781 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2782 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2783 * in response data.
2784 */
2785 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2786
2787 for (const auto& sensor : sensorList)
2788 {
2789 sensorRec.emplace_back(sensor.recordId);
2790 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302791 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002792 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302793 }
2794 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002795
2796 return ipmi::responseSuccess(
2797 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302798}
Thang Tran3dad8262023-08-17 15:20:56 +07002799
2800ipmi::RspType<uint8_t, // No of instances for requested id
2801 uint8_t, // No of record ids in the response
2802 std::vector< // Temperature Data
2803 std::tuple<uint7_t, // Temperature value
2804 bool, // Sign bit
2805 uint8_t // Entity Instance of sensor
2806 >>>
2807 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2808 uint8_t entityId, uint8_t entityInstance,
2809 uint8_t instanceStart)
2810{
2811 auto match = ipmi::dcmi::validEntityId.find(entityId);
2812 if (match == ipmi::dcmi::validEntityId.end())
2813 {
2814 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2815
2816 return ipmi::responseInvalidFieldRequest();
2817 }
2818
2819 if (sensorType != ipmi::dcmi::temperatureSensorType)
2820 {
2821 log<level::ERR>("Invalid sensor type",
2822 entry("SENSOR_TYPE=%d", sensorType));
2823
2824 return ipmi::responseInvalidFieldRequest();
2825 }
2826
2827 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2828 const auto& [totalSensorInst, sensorList] =
2829 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2830
2831 if (sensorList.empty())
2832 {
2833 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2834 }
2835
2836 /*
2837 * As DCMI specification, the maximum number of Record Ids of response data
2838 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2839 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2840 * in response data.
2841 */
2842 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2843
2844 for (const auto& sensor : sensorList)
2845 {
2846 const auto& [readResult, tempVal,
2847 signBit] = readTemp(ctx, sensor.objectPath);
2848
2849 if (readResult)
2850 {
2851 tempReadingVal.emplace_back(
2852 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2853
2854 if (tempReadingVal.size() >= numOfRec)
2855 {
2856 break;
2857 }
2858 }
2859 }
2860
2861 return ipmi::responseSuccess(totalSensorInst,
2862 static_cast<uint8_t>(tempReadingVal.size()),
2863 tempReadingVal);
2864}
2865
adarshgrami042e9db2022-09-15 10:34:34 +05302866} // namespace dcmi
2867
Willy Tude54f482021-01-26 15:59:09 -08002868/* end storage commands */
2869
2870void registerSensorFunctions()
2871{
2872 // <Platform Event>
2873 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2874 ipmi::sensor_event::cmdPlatformEvent,
2875 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2876
Willy Tudbafbce2021-03-29 00:37:05 -07002877 // <Set Sensor Reading and Event Status>
2878 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2879 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2880 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002881
Willy Tude54f482021-01-26 15:59:09 -08002882 // <Get Sensor Reading>
2883 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2884 ipmi::sensor_event::cmdGetSensorReading,
2885 ipmi::Privilege::User, ipmiSenGetSensorReading);
2886
2887 // <Get Sensor Threshold>
2888 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2889 ipmi::sensor_event::cmdGetSensorThreshold,
2890 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2891
2892 // <Set Sensor Threshold>
2893 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2894 ipmi::sensor_event::cmdSetSensorThreshold,
2895 ipmi::Privilege::Operator,
2896 ipmiSenSetSensorThresholds);
2897
2898 // <Get Sensor Event Enable>
2899 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2900 ipmi::sensor_event::cmdGetSensorEventEnable,
2901 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2902
2903 // <Get Sensor Event Status>
2904 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2905 ipmi::sensor_event::cmdGetSensorEventStatus,
2906 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2907
2908 // register all storage commands for both Sensor and Storage command
2909 // versions
2910
2911 // <Get SDR Repository Info>
2912 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2913 ipmi::storage::cmdGetSdrRepositoryInfo,
2914 ipmi::Privilege::User,
2915 ipmiStorageGetSDRRepositoryInfo);
2916
2917 // <Get Device SDR Info>
2918 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2919 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2920 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2921
2922 // <Get SDR Allocation Info>
2923 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2924 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2925 ipmi::Privilege::User,
2926 ipmiStorageGetSDRAllocationInfo);
2927
2928 // <Reserve SDR Repo>
2929 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2930 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2931 ipmi::Privilege::User, ipmiStorageReserveSDR);
2932
2933 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2934 ipmi::storage::cmdReserveSdrRepository,
2935 ipmi::Privilege::User, ipmiStorageReserveSDR);
2936
2937 // <Get Sdr>
2938 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2939 ipmi::sensor_event::cmdGetDeviceSdr,
2940 ipmi::Privilege::User, ipmiStorageGetSDR);
2941
2942 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2943 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2944 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302945 // <Get DCMI Sensor Info>
2946 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2947 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002948 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302949 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002950 // <Get Temperature Readings>
2951 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2952 ipmi::dcmi::cmdGetTemperatureReadings,
2953 ipmi::Privilege::User,
2954 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002955}
2956} // namespace ipmi