blob: 5d23a192702e118c62d3e1a0ecb0592ee502fc05 [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>
43#include <memory>
44#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080045#include <stdexcept>
46#include <string>
47#include <utility>
48#include <variant>
49
Scron Chang2703b022021-07-06 15:47:45 +080050#ifdef FEATURE_HYBRID_SENSORS
51
52#include "sensordatahandler.hpp"
53namespace ipmi
54{
55namespace sensor
56{
57extern const IdInfoMap sensors;
58} // namespace sensor
59} // namespace ipmi
60#endif
adarshgrami042e9db2022-09-15 10:34:34 +053061namespace ipmi
62{
63namespace dcmi
64{
65// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
66static const std::map<uint8_t, uint8_t> validEntityId{
67 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
68 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
69constexpr uint8_t temperatureSensorType = 0x01;
70constexpr uint8_t maxRecords = 8;
71} // namespace dcmi
72} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080073constexpr std::array<const char*, 7> suffixes = {
74 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
75 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080076namespace ipmi
77{
Hao Jiangd48c9212021-02-03 15:45:06 -080078
79using phosphor::logging::entry;
80using phosphor::logging::level;
81using phosphor::logging::log;
82
Willy Tude54f482021-01-26 15:59:09 -080083static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070084static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080085
Willy Tu38e7a2b2021-03-29 15:09:56 -070086// BMC I2C address is generally at 0x20
87static constexpr uint8_t bmcI2CAddr = 0x20;
88
Willy Tude54f482021-01-26 15:59:09 -080089constexpr size_t maxSDRTotalSize =
90 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
91constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
92
93static uint16_t sdrReservationID;
94static uint32_t sdrLastAdd = noTimestamp;
95static uint32_t sdrLastRemove = noTimestamp;
96static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070097
98// The IPMI spec defines four Logical Units (LUN), each capable of supporting
99// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
100// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800101// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700102// location.
103static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
104
Harvey Wu75893062023-03-22 17:17:31 +0800105static constexpr uint8_t lun0 = 0x0;
106static constexpr uint8_t lun1 = 0x1;
107static constexpr uint8_t lun3 = 0x3;
108
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700109static constexpr size_t lun0MaxSensorNum = 0xfe;
110static constexpr size_t lun1MaxSensorNum = 0x1fe;
111static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800112static constexpr int GENERAL_ERROR = -1;
113
Willy Tude54f482021-01-26 15:59:09 -0800114static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
115
116// Specify the comparison required to sort and find char* map objects
117struct CmpStr
118{
119 bool operator()(const char* a, const char* b) const
120 {
121 return std::strcmp(a, b) < 0;
122 }
123};
124const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
125 sensorUnits{{{"temperature", SensorUnits::degreesC},
126 {"voltage", SensorUnits::volts},
127 {"current", SensorUnits::amps},
128 {"fan_tach", SensorUnits::rpm},
129 {"power", SensorUnits::watts}}};
130
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
157// this keeps track of deassertions for sensor event status command. A
158// deasertion can only happen if an assertion was seen first.
159static boost::container::flat_map<
160 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
161 thresholdDeassertMap;
162
Patrick Williams5d82f472022-07-22 19:26:53 -0500163static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800164 *getSdBus(),
165 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
166 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500167 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500168 boost::container::flat_map<std::string, std::variant<bool, double>> values;
169 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800170
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500171 auto findAssert = std::find_if(values.begin(), values.end(),
172 [](const auto& pair) {
173 return pair.first.find("Alarm") != std::string::npos;
174 });
175 if (findAssert != values.end())
176 {
177 auto ptr = std::get_if<bool>(&(findAssert->second));
178 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800179 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500180 phosphor::logging::log<phosphor::logging::level::ERR>(
181 "thresholdChanged: Assert non bool");
182 return;
183 }
184 if (*ptr)
185 {
186 phosphor::logging::log<phosphor::logging::level::INFO>(
187 "thresholdChanged: Assert",
188 phosphor::logging::entry("SENSOR=%s", m.get_path()));
189 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
190 }
191 else
192 {
193 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
194 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800195 {
196 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500197 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800198 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500199 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800200 }
201 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500202 }
Patrick Williams369824e2023-10-20 11:18:23 -0500203});
Willy Tude54f482021-01-26 15:59:09 -0800204
Hao Jiangd2afd052020-12-10 15:09:32 -0800205namespace sensor
206{
207static constexpr const char* vrInterface =
208 "xyz.openbmc_project.Control.VoltageRegulatorMode";
209static constexpr const char* sensorInterface =
210 "xyz.openbmc_project.Sensor.Value";
211} // namespace sensor
212
Willy Tude54f482021-01-26 15:59:09 -0800213static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
214 double& min)
215{
216 max = 127;
217 min = -128;
218
Hao Jiangd2afd052020-12-10 15:09:32 -0800219 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800220 auto critical =
221 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
222 auto warning =
223 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
224
225 if (sensorObject != sensorMap.end())
226 {
227 auto maxMap = sensorObject->second.find("MaxValue");
228 auto minMap = sensorObject->second.find("MinValue");
229
230 if (maxMap != sensorObject->second.end())
231 {
232 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
233 }
234 if (minMap != sensorObject->second.end())
235 {
236 min = std::visit(VariantToDoubleVisitor(), minMap->second);
237 }
238 }
239 if (critical != sensorMap.end())
240 {
241 auto lower = critical->second.find("CriticalLow");
242 auto upper = critical->second.find("CriticalHigh");
243 if (lower != critical->second.end())
244 {
245 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300246 if (std::isfinite(value))
247 {
248 min = std::min(value, min);
249 }
Willy Tude54f482021-01-26 15:59:09 -0800250 }
251 if (upper != critical->second.end())
252 {
253 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300254 if (std::isfinite(value))
255 {
256 max = std::max(value, max);
257 }
Willy Tude54f482021-01-26 15:59:09 -0800258 }
259 }
260 if (warning != sensorMap.end())
261 {
Willy Tude54f482021-01-26 15:59:09 -0800262 auto lower = warning->second.find("WarningLow");
263 auto upper = warning->second.find("WarningHigh");
264 if (lower != warning->second.end())
265 {
266 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300267 if (std::isfinite(value))
268 {
269 min = std::min(value, min);
270 }
Willy Tude54f482021-01-26 15:59:09 -0800271 }
272 if (upper != warning->second.end())
273 {
274 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300275 if (std::isfinite(value))
276 {
277 max = std::max(value, max);
278 }
Willy Tude54f482021-01-26 15:59:09 -0800279 }
280 }
281}
282
283static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700284 std::string sensorPath, DbusInterfaceMap& sensorMap,
285 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800286{
Scron Chang2703b022021-07-06 15:47:45 +0800287#ifdef FEATURE_HYBRID_SENSORS
288 if (auto sensor = findStaticSensor(sensorPath);
289 sensor != ipmi::sensor::sensors.end() &&
290 getSensorEventTypeFromPath(sensorPath) !=
291 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
292 {
293 // If the incoming sensor is a discrete sensor, it might fail in
294 // getManagedObjects(), return true, and use its own getFunc to get
295 // value.
296 return true;
297 }
298#endif
299
Willy Tude54f482021-01-26 15:59:09 -0800300 static boost::container::flat_map<
301 std::string, std::chrono::time_point<std::chrono::steady_clock>>
302 updateTimeMap;
303
304 auto updateFind = updateTimeMap.find(sensorConnection);
305 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
306 if (updateFind != updateTimeMap.end())
307 {
308 lastUpdate = updateFind->second;
309 }
310
311 auto now = std::chrono::steady_clock::now();
312
313 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700314 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800315 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800316 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800317
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800318 // Object managers for different kinds of OpenBMC DBus interfaces.
319 // Documented in the phosphor-dbus-interfaces repository.
320 const char* paths[] = {
321 "/xyz/openbmc_project/sensors",
322 "/xyz/openbmc_project/vr",
323 };
324 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
325 ObjectValueTree allManagedObjects;
326
327 for (size_t i = 0; i < num_paths; i++)
328 {
329 ObjectValueTree managedObjects;
330 boost::system::error_code ec = getManagedObjects(
331 ctx, sensorConnection.c_str(), paths[i], managedObjects);
332 if (ec)
333 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800334 continue;
335 }
336 allManagedObjects.merge(managedObjects);
337 found = true;
338 }
339
340 if (!found)
341 {
Tom Tung6615d472023-05-31 18:48:12 +0800342 phosphor::logging::log<phosphor::logging::level::ERR>(
343 "GetMangagedObjects for getSensorMap failed",
344 phosphor::logging::entry("SERVICE=%s",
345 sensorConnection.c_str()));
346
Willy Tude54f482021-01-26 15:59:09 -0800347 return false;
348 }
349
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800350 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700351 // Update time after finish building the map which allow the
352 // data to be cached for updatePeriod plus the build time.
353 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800354 }
355 auto connection = SensorCache.find(sensorConnection);
356 if (connection == SensorCache.end())
357 {
358 return false;
359 }
360 auto path = connection->second.find(sensorPath);
361 if (path == connection->second.end())
362 {
363 return false;
364 }
365 sensorMap = path->second;
366
367 return true;
368}
369
Hao Jiangd2afd052020-12-10 15:09:32 -0800370namespace sensor
371{
Hao Jiangd48c9212021-02-03 15:45:06 -0800372// Read VR profiles from sensor(daemon) interface
373static std::optional<std::vector<std::string>>
374 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800375{
376 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800377 auto supportedProperty = object.find("Supported");
378 if (supportedProperty == object.end() ||
379 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800380 {
381 phosphor::logging::log<phosphor::logging::level::ERR>(
382 "Missing the required Supported and Selected properties");
383 return std::nullopt;
384 }
385
386 const auto profilesPtr =
387 std::get_if<std::vector<std::string>>(&supportedProperty->second);
388
389 if (profilesPtr == nullptr)
390 {
391 phosphor::logging::log<phosphor::logging::level::ERR>(
392 "property is not array of string");
393 return std::nullopt;
394 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800395 return *profilesPtr;
396}
397
398// Calculate VR Mode from input IPMI discrete event bytes
399static std::optional<std::string>
400 calculateVRMode(uint15_t assertOffset,
401 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
402{
403 // get VR mode profiles from Supported Interface
404 auto profiles = getSupportedVrProfiles(VRObject);
405 if (!profiles)
406 {
407 return std::nullopt;
408 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800409
410 // interpret IPMI cmd bits into profiles' index
411 long unsigned int index = 0;
412 // only one bit should be set and the highest bit should not be used.
413 if (assertOffset == 0 || assertOffset == (1u << 15) ||
414 (assertOffset & (assertOffset - 1)))
415 {
416 phosphor::logging::log<phosphor::logging::level::ERR>(
417 "IPMI cmd format incorrect",
418
419 phosphor::logging::entry("BYTES=%#02x",
420 static_cast<uint16_t>(assertOffset)));
421 return std::nullopt;
422 }
423
424 while (assertOffset != 1)
425 {
426 assertOffset >>= 1;
427 index++;
428 }
429
Hao Jiangd48c9212021-02-03 15:45:06 -0800430 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800431 {
432 phosphor::logging::log<phosphor::logging::level::ERR>(
433 "profile index out of boundary");
434 return std::nullopt;
435 }
436
Hao Jiangd48c9212021-02-03 15:45:06 -0800437 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800438}
439
440// Calculate sensor value from IPMI reading byte
441static std::optional<double>
442 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
443 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
444{
445 if (valueObject.find("Value") == valueObject.end())
446 {
447 phosphor::logging::log<phosphor::logging::level::ERR>(
448 "Missing the required Value property");
449 return std::nullopt;
450 }
451
452 double max = 0;
453 double min = 0;
454 getSensorMaxMin(sensorMap, max, min);
455
456 int16_t mValue = 0;
457 int16_t bValue = 0;
458 int8_t rExp = 0;
459 int8_t bExp = 0;
460 bool bSigned = false;
461
462 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
463 {
464 return std::nullopt;
465 }
466
467 double value = bSigned ? ((int8_t)reading) : reading;
468
469 value *= ((double)mValue);
470 value += ((double)bValue) * std::pow(10.0, bExp);
471 value *= std::pow(10.0, rExp);
472
473 return value;
474}
475
Willy Tu38e7a2b2021-03-29 15:09:56 -0700476// Extract file name from sensor path as the sensors SDR ID. Simplify the name
477// if it is too long.
478std::string parseSdrIdFromPath(const std::string& path)
479{
480 std::string name;
481 size_t nameStart = path.rfind("/");
482 if (nameStart != std::string::npos)
483 {
484 name = path.substr(nameStart + 1, std::string::npos - nameStart);
485 }
486
Willy Tu38e7a2b2021-03-29 15:09:56 -0700487 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
488 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200489#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800490 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700491 {
JeffLind950f412021-10-20 18:49:34 +0800492 if (boost::ends_with(name, suffix))
493 {
494 boost::replace_all(name, suffix, "");
495 break;
496 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700497 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200498#endif
499#ifdef SHORTNAME_REPLACE_WORDS
500 constexpr std::array<std::pair<const char*, const char*>, 2>
501 replaceWords = {std::make_pair("Output", "Out"),
502 std::make_pair("Input", "In")};
503 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800504 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200505 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800506 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200507#endif
508
509 // as a backup and if nothing else is configured
510 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700511 }
512 return name;
513}
514
Hao Jiangd48c9212021-02-03 15:45:06 -0800515bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
516 const std::string& path,
517 const ipmi::DbusInterfaceMap::mapped_type& object,
518 std::bitset<16>& assertions)
519{
520 auto profiles = sensor::getSupportedVrProfiles(object);
521 if (!profiles)
522 {
523 return false;
524 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700525 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800526
527 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700528 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800529 if (ec)
530 {
531 log<level::ERR>("Failed to get property",
532 entry("PROPERTY=%s", "Selected"),
533 entry("PATH=%s", path.c_str()),
534 entry("INTERFACE=%s", sensor::sensorInterface),
535 entry("WHAT=%s", ec.message().c_str()));
536 return false;
537 }
538
Willy Tu8366f0b2022-04-29 05:00:17 -0700539 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800540 if (itr == profiles->end())
541 {
542 using namespace phosphor::logging;
543 log<level::ERR>("VR mode doesn't match any of its profiles",
544 entry("PATH=%s", path.c_str()));
545 return false;
546 }
547 std::size_t index =
548 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
549
Willy Tubef102a2022-06-09 15:36:09 -0700550 // map index to response event assertion bit.
551 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800552 {
Willy Tubef102a2022-06-09 15:36:09 -0700553 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800554 }
555 else
556 {
557 log<level::ERR>("VR profile index reaches max assertion bit",
558 entry("PATH=%s", path.c_str()),
559 entry("INDEX=%uz", index));
560 return false;
561 }
562 if constexpr (debug)
563 {
564 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700565 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800566 }
567 return true;
568}
Hao Jiangd2afd052020-12-10 15:09:32 -0800569} // namespace sensor
570
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000571ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
572 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800573{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000574 constexpr const uint8_t validEnvmRev = 0x04;
575 constexpr const uint8_t lastSensorType = 0x2C;
576 constexpr const uint8_t oemReserved = 0xC0;
577
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700578 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000579 uint8_t evmRev = 0;
580 uint8_t sensorType = 0;
581 uint8_t sensorNum = 0;
582 uint8_t eventType = 0;
583 uint8_t eventData1 = 0;
584 std::optional<uint8_t> eventData2 = 0;
585 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700586 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000587 ipmi::ChannelInfo chInfo;
588
589 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
590 {
591 phosphor::logging::log<phosphor::logging::level::ERR>(
592 "Failed to get Channel Info",
593 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
594 return ipmi::responseUnspecifiedError();
595 }
596
597 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
598 ipmi::EChannelMediumType::systemInterface)
599 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700600 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000601 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700602 // Refer to IPMI Spec Table 32: SEL Event Records
603 generatorID = (ctx->channel << 12) // Channel
604 | (0x0 << 10) // Reserved
605 | (0x0 << 8) // 0x0 for sys-soft ID
606 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000607 }
608 else
609 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000610 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
611 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700612 // Refer to IPMI Spec Table 32: SEL Event Records
613 generatorID = (ctx->channel << 12) // Channel
614 | (0x0 << 10) // Reserved
615 | ((ctx->lun & 0x3) << 8) // Lun
616 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000617 }
618
619 if (!p.fullyUnpacked())
620 {
621 return ipmi::responseReqDataLenInvalid();
622 }
623
624 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
625 if (evmRev != validEnvmRev)
626 {
627 return ipmi::responseInvalidFieldRequest();
628 }
629 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
630 {
631 return ipmi::responseInvalidFieldRequest();
632 }
633
Willy Tude54f482021-01-26 15:59:09 -0800634 return ipmi::responseSuccess();
635}
636
Willy Tudbafbce2021-03-29 00:37:05 -0700637ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800638 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700639 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800640 bool, uint15_t, bool, uint8_t, uint8_t,
641 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700642{
643 std::string connection;
644 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700645 std::vector<std::string> interfaces;
646
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500647 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
648 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700649 if (status)
650 {
651 return ipmi::response(status);
652 }
653
Hao Jiangd2afd052020-12-10 15:09:32 -0800654 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700655 if (std::find(interfaces.begin(), interfaces.end(),
656 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700657 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700658 DbusInterfaceMap sensorMap;
659 if (!getSensorMap(ctx, connection, path, sensorMap))
660 {
661 return ipmi::responseResponseError();
662 }
663 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800664 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700665 {
666 return ipmi::responseResponseError();
667 }
668
Jie Yangf0a89942021-07-29 15:30:25 -0700669 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800670 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
671 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700672 {
673 return ipmi::responseResponseError();
674 }
675
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500676 auto value = sensor::calculateValue(reading, sensorMap,
677 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800678 if (!value)
679 {
680 return ipmi::responseResponseError();
681 }
682
683 if constexpr (debug)
684 {
685 phosphor::logging::log<phosphor::logging::level::INFO>(
686 "IPMI SET_SENSOR",
687 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
688 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
689 phosphor::logging::entry("VALUE=%f", *value));
690 }
691
692 boost::system::error_code ec =
693 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
694 "Value", ipmi::Value(*value));
695
696 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500697 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800698 // callback functions for now (e.g. ipmiSetSensorReading).
699 if (ec)
700 {
701 using namespace phosphor::logging;
702 log<level::ERR>("Failed to set property",
703 entry("PROPERTY=%s", "Value"),
704 entry("PATH=%s", path.c_str()),
705 entry("INTERFACE=%s", sensor::sensorInterface),
706 entry("WHAT=%s", ec.message().c_str()));
707 return ipmi::responseResponseError();
708 }
709 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700710 }
711
Hao Jiange39d4d82021-04-16 17:02:40 -0700712 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
713 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700714 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700715 DbusInterfaceMap sensorMap;
716 if (!getSensorMap(ctx, connection, path, sensorMap))
717 {
718 return ipmi::responseResponseError();
719 }
720 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800721 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700722 {
723 return ipmi::responseResponseError();
724 }
725
Hao Jiangd2afd052020-12-10 15:09:32 -0800726 // VR sensors are treated as a special case and we will not check the
727 // write permission for VR sensors, since they always deemed writable
728 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500729 auto vrMode = sensor::calculateVRMode(assertOffset,
730 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800731 if (!vrMode)
732 {
733 return ipmi::responseResponseError();
734 }
735 boost::system::error_code ec = setDbusProperty(
736 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
737 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500738 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800739 // callback functions for now (e.g. ipmiSetSensorReading).
740 if (ec)
741 {
742 using namespace phosphor::logging;
743 log<level::ERR>("Failed to set property",
744 entry("PROPERTY=%s", "Selected"),
745 entry("PATH=%s", path.c_str()),
746 entry("INTERFACE=%s", sensor::sensorInterface),
747 entry("WHAT=%s", ec.message().c_str()));
748 return ipmi::responseResponseError();
749 }
750 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700751 }
752
Hao Jiangd2afd052020-12-10 15:09:32 -0800753 phosphor::logging::log<phosphor::logging::level::ERR>(
754 "unknown sensor type",
755 phosphor::logging::entry("PATH=%s", path.c_str()));
756 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700757}
758
Willy Tude54f482021-01-26 15:59:09 -0800759ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
760 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
761{
762 std::string connection;
763 std::string path;
764
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000765 if (sensnum == reservedSensorNumber)
766 {
767 return ipmi::responseInvalidFieldRequest();
768 }
769
Willy Tude54f482021-01-26 15:59:09 -0800770 auto status = getSensorConnection(ctx, sensnum, connection, path);
771 if (status)
772 {
773 return ipmi::response(status);
774 }
775
Scron Chang2703b022021-07-06 15:47:45 +0800776#ifdef FEATURE_HYBRID_SENSORS
777 if (auto sensor = findStaticSensor(path);
778 sensor != ipmi::sensor::sensors.end() &&
779 getSensorEventTypeFromPath(path) !=
780 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
781 {
782 if (ipmi::sensor::Mutability::Read !=
783 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
784 {
785 return ipmi::responseIllegalCommand();
786 }
787
788 uint8_t operation;
789 try
790 {
791 ipmi::sensor::GetSensorResponse getResponse =
792 sensor->second.getFunc(sensor->second);
793
794 if (getResponse.readingOrStateUnavailable)
795 {
796 operation |= static_cast<uint8_t>(
797 IPMISensorReadingByte2::readingStateUnavailable);
798 }
799 if (getResponse.scanningEnabled)
800 {
801 operation |= static_cast<uint8_t>(
802 IPMISensorReadingByte2::sensorScanningEnable);
803 }
804 if (getResponse.allEventMessagesEnabled)
805 {
806 operation |= static_cast<uint8_t>(
807 IPMISensorReadingByte2::eventMessagesEnable);
808 }
809 return ipmi::responseSuccess(
810 getResponse.reading, operation,
811 getResponse.thresholdLevelsStates,
812 getResponse.discreteReadingSensorStates);
813 }
814 catch (const std::exception& e)
815 {
816 operation |= static_cast<uint8_t>(
817 IPMISensorReadingByte2::readingStateUnavailable);
818 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
819 }
820 }
821#endif
822
Willy Tude54f482021-01-26 15:59:09 -0800823 DbusInterfaceMap sensorMap;
824 if (!getSensorMap(ctx, connection, path, sensorMap))
825 {
826 return ipmi::responseResponseError();
827 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800828 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800829
830 if (sensorObject == sensorMap.end() ||
831 sensorObject->second.find("Value") == sensorObject->second.end())
832 {
833 return ipmi::responseResponseError();
834 }
835 auto& valueVariant = sensorObject->second["Value"];
836 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
837
838 double max = 0;
839 double min = 0;
840 getSensorMaxMin(sensorMap, max, min);
841
842 int16_t mValue = 0;
843 int16_t bValue = 0;
844 int8_t rExp = 0;
845 int8_t bExp = 0;
846 bool bSigned = false;
847
848 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
849 {
850 return ipmi::responseResponseError();
851 }
852
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500853 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
854 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800855 uint8_t operation =
856 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
857 operation |=
858 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
859 bool notReading = std::isnan(reading);
860
861 if (!notReading)
862 {
863 auto availableObject =
864 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
865 if (availableObject != sensorMap.end())
866 {
867 auto findAvailable = availableObject->second.find("Available");
868 if (findAvailable != availableObject->second.end())
869 {
870 bool* available = std::get_if<bool>(&(findAvailable->second));
871 if (available && !(*available))
872 {
873 notReading = true;
874 }
875 }
876 }
877 }
878
879 if (notReading)
880 {
881 operation |= static_cast<uint8_t>(
882 IPMISensorReadingByte2::readingStateUnavailable);
883 }
884
Josh Lehana55c9532020-10-28 21:59:06 -0700885 if constexpr (details::enableInstrumentation)
886 {
887 int byteValue;
888 if (bSigned)
889 {
890 byteValue = static_cast<int>(static_cast<int8_t>(value));
891 }
892 else
893 {
894 byteValue = static_cast<int>(static_cast<uint8_t>(value));
895 }
896
897 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800898 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
899 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700900 {
901 // This is the first reading, show the coefficients
902 double step = (max - min) / 255.0;
903 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800904 << details::sdrStatsTable.getName((ctx->lun << 8) |
905 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700906 << ": Range min=" << min << " max=" << max
907 << ", step=" << step
908 << ", Coefficients mValue=" << static_cast<int>(mValue)
909 << " rExp=" << static_cast<int>(rExp)
910 << " bValue=" << static_cast<int>(bValue)
911 << " bExp=" << static_cast<int>(bExp)
912 << " bSigned=" << static_cast<int>(bSigned) << "\n";
913 }
914 }
915
Willy Tude54f482021-01-26 15:59:09 -0800916 uint8_t thresholds = 0;
917
918 auto warningObject =
919 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
920 if (warningObject != sensorMap.end())
921 {
922 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
923 auto alarmLow = warningObject->second.find("WarningAlarmLow");
924 if (alarmHigh != warningObject->second.end())
925 {
926 if (std::get<bool>(alarmHigh->second))
927 {
928 thresholds |= static_cast<uint8_t>(
929 IPMISensorReadingByte3::upperNonCritical);
930 }
931 }
932 if (alarmLow != warningObject->second.end())
933 {
934 if (std::get<bool>(alarmLow->second))
935 {
936 thresholds |= static_cast<uint8_t>(
937 IPMISensorReadingByte3::lowerNonCritical);
938 }
939 }
940 }
941
942 auto criticalObject =
943 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
944 if (criticalObject != sensorMap.end())
945 {
946 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
947 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
948 if (alarmHigh != criticalObject->second.end())
949 {
950 if (std::get<bool>(alarmHigh->second))
951 {
952 thresholds |=
953 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
954 }
955 }
956 if (alarmLow != criticalObject->second.end())
957 {
958 if (std::get<bool>(alarmLow->second))
959 {
960 thresholds |=
961 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
962 }
963 }
964 }
965
966 // no discrete as of today so optional byte is never returned
967 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
968}
969
970/** @brief implements the Set Sensor threshold command
971 * @param sensorNumber - sensor number
972 * @param lowerNonCriticalThreshMask
973 * @param lowerCriticalThreshMask
974 * @param lowerNonRecovThreshMask
975 * @param upperNonCriticalThreshMask
976 * @param upperCriticalThreshMask
977 * @param upperNonRecovThreshMask
978 * @param reserved
979 * @param lowerNonCritical - lower non-critical threshold
980 * @param lowerCritical - Lower critical threshold
981 * @param lowerNonRecoverable - Lower non recovarable threshold
982 * @param upperNonCritical - Upper non-critical threshold
983 * @param upperCritical - Upper critical
984 * @param upperNonRecoverable - Upper Non-recoverable
985 *
986 * @returns IPMI completion code
987 */
988ipmi::RspType<> ipmiSenSetSensorThresholds(
989 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
990 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
991 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
992 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800993 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800994 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800995 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800996{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000997 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800998 {
999 return ipmi::responseInvalidFieldRequest();
1000 }
1001
1002 // lower nc and upper nc not suppported on any sensor
1003 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1004 {
1005 return ipmi::responseInvalidFieldRequest();
1006 }
1007
1008 // if none of the threshold mask are set, nothing to do
1009 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1010 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1011 upperCriticalThreshMask | upperNonRecovThreshMask))
1012 {
1013 return ipmi::responseSuccess();
1014 }
1015
1016 std::string connection;
1017 std::string path;
1018
1019 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1020 if (status)
1021 {
1022 return ipmi::response(status);
1023 }
1024 DbusInterfaceMap sensorMap;
1025 if (!getSensorMap(ctx, connection, path, sensorMap))
1026 {
1027 return ipmi::responseResponseError();
1028 }
1029
1030 double max = 0;
1031 double min = 0;
1032 getSensorMaxMin(sensorMap, max, min);
1033
1034 int16_t mValue = 0;
1035 int16_t bValue = 0;
1036 int8_t rExp = 0;
1037 int8_t bExp = 0;
1038 bool bSigned = false;
1039
1040 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1041 {
1042 return ipmi::responseResponseError();
1043 }
1044
1045 // store a vector of property name, value to set, and interface
1046 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1047
1048 // define the indexes of the tuple
1049 constexpr uint8_t propertyName = 0;
1050 constexpr uint8_t thresholdValue = 1;
1051 constexpr uint8_t interface = 2;
1052 // verifiy all needed fields are present
1053 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1054 {
1055 auto findThreshold =
1056 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1057 if (findThreshold == sensorMap.end())
1058 {
1059 return ipmi::responseInvalidFieldRequest();
1060 }
1061 if (lowerCriticalThreshMask)
1062 {
1063 auto findLower = findThreshold->second.find("CriticalLow");
1064 if (findLower == findThreshold->second.end())
1065 {
1066 return ipmi::responseInvalidFieldRequest();
1067 }
1068 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1069 findThreshold->first);
1070 }
1071 if (upperCriticalThreshMask)
1072 {
1073 auto findUpper = findThreshold->second.find("CriticalHigh");
1074 if (findUpper == findThreshold->second.end())
1075 {
1076 return ipmi::responseInvalidFieldRequest();
1077 }
1078 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1079 findThreshold->first);
1080 }
1081 }
1082 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1083 {
1084 auto findThreshold =
1085 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1086 if (findThreshold == sensorMap.end())
1087 {
1088 return ipmi::responseInvalidFieldRequest();
1089 }
1090 if (lowerNonCriticalThreshMask)
1091 {
1092 auto findLower = findThreshold->second.find("WarningLow");
1093 if (findLower == findThreshold->second.end())
1094 {
1095 return ipmi::responseInvalidFieldRequest();
1096 }
1097 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1098 findThreshold->first);
1099 }
1100 if (upperNonCriticalThreshMask)
1101 {
1102 auto findUpper = findThreshold->second.find("WarningHigh");
1103 if (findUpper == findThreshold->second.end())
1104 {
1105 return ipmi::responseInvalidFieldRequest();
1106 }
1107 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1108 findThreshold->first);
1109 }
1110 }
1111 for (const auto& property : thresholdsToSet)
1112 {
1113 // from section 36.3 in the IPMI Spec, assume all linear
1114 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1115 (bValue * std::pow(10.0, bExp))) *
1116 std::pow(10.0, rExp);
1117 setDbusProperty(
1118 *getSdBus(), connection, path, std::get<interface>(property),
1119 std::get<propertyName>(property), ipmi::Value(valueToSet));
1120 }
1121 return ipmi::responseSuccess();
1122}
1123
1124IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1125{
1126 IPMIThresholds resp;
1127 auto warningInterface =
1128 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1129 auto criticalInterface =
1130 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1131
1132 if ((warningInterface != sensorMap.end()) ||
1133 (criticalInterface != sensorMap.end()))
1134 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001135 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001136
1137 if (sensorPair == sensorMap.end())
1138 {
1139 // should not have been able to find a sensor not implementing
1140 // the sensor object
1141 throw std::runtime_error("Invalid sensor map");
1142 }
1143
1144 double max = 0;
1145 double min = 0;
1146 getSensorMaxMin(sensorMap, max, min);
1147
1148 int16_t mValue = 0;
1149 int16_t bValue = 0;
1150 int8_t rExp = 0;
1151 int8_t bExp = 0;
1152 bool bSigned = false;
1153
1154 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1155 {
1156 throw std::runtime_error("Invalid sensor atrributes");
1157 }
1158 if (warningInterface != sensorMap.end())
1159 {
1160 auto& warningMap = warningInterface->second;
1161
1162 auto warningHigh = warningMap.find("WarningHigh");
1163 auto warningLow = warningMap.find("WarningLow");
1164
1165 if (warningHigh != warningMap.end())
1166 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001167 double value = std::visit(VariantToDoubleVisitor(),
1168 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001169 if (std::isfinite(value))
1170 {
1171 resp.warningHigh = scaleIPMIValueFromDouble(
1172 value, mValue, rExp, bValue, bExp, bSigned);
1173 }
Willy Tude54f482021-01-26 15:59:09 -08001174 }
1175 if (warningLow != warningMap.end())
1176 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001177 double value = std::visit(VariantToDoubleVisitor(),
1178 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001179 if (std::isfinite(value))
1180 {
1181 resp.warningLow = scaleIPMIValueFromDouble(
1182 value, mValue, rExp, bValue, bExp, bSigned);
1183 }
Willy Tude54f482021-01-26 15:59:09 -08001184 }
1185 }
1186 if (criticalInterface != sensorMap.end())
1187 {
1188 auto& criticalMap = criticalInterface->second;
1189
1190 auto criticalHigh = criticalMap.find("CriticalHigh");
1191 auto criticalLow = criticalMap.find("CriticalLow");
1192
1193 if (criticalHigh != criticalMap.end())
1194 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001195 double value = std::visit(VariantToDoubleVisitor(),
1196 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001197 if (std::isfinite(value))
1198 {
1199 resp.criticalHigh = scaleIPMIValueFromDouble(
1200 value, mValue, rExp, bValue, bExp, bSigned);
1201 }
Willy Tude54f482021-01-26 15:59:09 -08001202 }
1203 if (criticalLow != criticalMap.end())
1204 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001205 double value = std::visit(VariantToDoubleVisitor(),
1206 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001207 if (std::isfinite(value))
1208 {
1209 resp.criticalLow = scaleIPMIValueFromDouble(
1210 value, mValue, rExp, bValue, bExp, bSigned);
1211 }
Willy Tude54f482021-01-26 15:59:09 -08001212 }
1213 }
1214 }
1215 return resp;
1216}
1217
1218ipmi::RspType<uint8_t, // readable
1219 uint8_t, // lowerNCrit
1220 uint8_t, // lowerCrit
1221 uint8_t, // lowerNrecoverable
1222 uint8_t, // upperNC
1223 uint8_t, // upperCrit
1224 uint8_t> // upperNRecoverable
1225 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1226{
1227 std::string connection;
1228 std::string path;
1229
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001230 if (sensorNumber == reservedSensorNumber)
1231 {
1232 return ipmi::responseInvalidFieldRequest();
1233 }
1234
Willy Tude54f482021-01-26 15:59:09 -08001235 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1236 if (status)
1237 {
1238 return ipmi::response(status);
1239 }
1240
1241 DbusInterfaceMap sensorMap;
1242 if (!getSensorMap(ctx, connection, path, sensorMap))
1243 {
1244 return ipmi::responseResponseError();
1245 }
1246
1247 IPMIThresholds thresholdData;
1248 try
1249 {
1250 thresholdData = getIPMIThresholds(sensorMap);
1251 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001252 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001253 {
1254 return ipmi::responseResponseError();
1255 }
1256
1257 uint8_t readable = 0;
1258 uint8_t lowerNC = 0;
1259 uint8_t lowerCritical = 0;
1260 uint8_t lowerNonRecoverable = 0;
1261 uint8_t upperNC = 0;
1262 uint8_t upperCritical = 0;
1263 uint8_t upperNonRecoverable = 0;
1264
1265 if (thresholdData.warningHigh)
1266 {
1267 readable |=
1268 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1269 upperNC = *thresholdData.warningHigh;
1270 }
1271 if (thresholdData.warningLow)
1272 {
1273 readable |=
1274 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1275 lowerNC = *thresholdData.warningLow;
1276 }
1277
1278 if (thresholdData.criticalHigh)
1279 {
1280 readable |=
1281 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1282 upperCritical = *thresholdData.criticalHigh;
1283 }
1284 if (thresholdData.criticalLow)
1285 {
1286 readable |=
1287 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1288 lowerCritical = *thresholdData.criticalLow;
1289 }
1290
1291 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1292 lowerNonRecoverable, upperNC, upperCritical,
1293 upperNonRecoverable);
1294}
1295
1296/** @brief implements the get Sensor event enable command
1297 * @param sensorNumber - sensor number
1298 *
1299 * @returns IPMI completion code plus response data
1300 * - enabled - Sensor Event messages
1301 * - assertionEnabledLsb - Assertion event messages
1302 * - assertionEnabledMsb - Assertion event messages
1303 * - deassertionEnabledLsb - Deassertion event messages
1304 * - deassertionEnabledMsb - Deassertion event messages
1305 */
1306
1307ipmi::RspType<uint8_t, // enabled
1308 uint8_t, // assertionEnabledLsb
1309 uint8_t, // assertionEnabledMsb
1310 uint8_t, // deassertionEnabledLsb
1311 uint8_t> // deassertionEnabledMsb
1312 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1313{
1314 std::string connection;
1315 std::string path;
1316
1317 uint8_t enabled = 0;
1318 uint8_t assertionEnabledLsb = 0;
1319 uint8_t assertionEnabledMsb = 0;
1320 uint8_t deassertionEnabledLsb = 0;
1321 uint8_t deassertionEnabledMsb = 0;
1322
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001323 if (sensorNum == reservedSensorNumber)
1324 {
1325 return ipmi::responseInvalidFieldRequest();
1326 }
1327
Willy Tude54f482021-01-26 15:59:09 -08001328 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1329 if (status)
1330 {
1331 return ipmi::response(status);
1332 }
1333
Scron Chang2703b022021-07-06 15:47:45 +08001334#ifdef FEATURE_HYBRID_SENSORS
1335 if (auto sensor = findStaticSensor(path);
1336 sensor != ipmi::sensor::sensors.end() &&
1337 getSensorEventTypeFromPath(path) !=
1338 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1339 {
1340 enabled = static_cast<uint8_t>(
1341 IPMISensorEventEnableByte2::sensorScanningEnable);
1342 uint16_t assertionEnabled = 0;
1343 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1344 ->second.begin()
1345 ->second.second)
1346 {
1347 assertionEnabled |= (1 << offsetValMap.first);
1348 }
1349 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1350 assertionEnabledMsb =
1351 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1352
1353 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1354 assertionEnabledMsb, deassertionEnabledLsb,
1355 deassertionEnabledMsb);
1356 }
1357#endif
1358
Willy Tude54f482021-01-26 15:59:09 -08001359 DbusInterfaceMap sensorMap;
1360 if (!getSensorMap(ctx, connection, path, sensorMap))
1361 {
1362 return ipmi::responseResponseError();
1363 }
1364
1365 auto warningInterface =
1366 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1367 auto criticalInterface =
1368 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1369 if ((warningInterface != sensorMap.end()) ||
1370 (criticalInterface != sensorMap.end()))
1371 {
1372 enabled = static_cast<uint8_t>(
1373 IPMISensorEventEnableByte2::sensorScanningEnable);
1374 if (warningInterface != sensorMap.end())
1375 {
1376 auto& warningMap = warningInterface->second;
1377
1378 auto warningHigh = warningMap.find("WarningHigh");
1379 auto warningLow = warningMap.find("WarningLow");
1380 if (warningHigh != warningMap.end())
1381 {
1382 assertionEnabledLsb |= static_cast<uint8_t>(
1383 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1384 deassertionEnabledLsb |= static_cast<uint8_t>(
1385 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1386 }
1387 if (warningLow != warningMap.end())
1388 {
1389 assertionEnabledLsb |= static_cast<uint8_t>(
1390 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1391 deassertionEnabledLsb |= static_cast<uint8_t>(
1392 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1393 }
1394 }
1395 if (criticalInterface != sensorMap.end())
1396 {
1397 auto& criticalMap = criticalInterface->second;
1398
1399 auto criticalHigh = criticalMap.find("CriticalHigh");
1400 auto criticalLow = criticalMap.find("CriticalLow");
1401
1402 if (criticalHigh != criticalMap.end())
1403 {
1404 assertionEnabledMsb |= static_cast<uint8_t>(
1405 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1406 deassertionEnabledMsb |= static_cast<uint8_t>(
1407 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1408 }
1409 if (criticalLow != criticalMap.end())
1410 {
1411 assertionEnabledLsb |= static_cast<uint8_t>(
1412 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1413 deassertionEnabledLsb |= static_cast<uint8_t>(
1414 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1415 }
1416 }
1417 }
1418
1419 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1420 assertionEnabledMsb, deassertionEnabledLsb,
1421 deassertionEnabledMsb);
1422}
1423
1424/** @brief implements the get Sensor event status command
1425 * @param sensorNumber - sensor number, FFh = reserved
1426 *
1427 * @returns IPMI completion code plus response data
1428 * - sensorEventStatus - Sensor Event messages state
1429 * - assertions - Assertion event messages
1430 * - deassertions - Deassertion event messages
1431 */
1432ipmi::RspType<uint8_t, // sensorEventStatus
1433 std::bitset<16>, // assertions
1434 std::bitset<16> // deassertion
1435 >
1436 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1437{
1438 if (sensorNum == reservedSensorNumber)
1439 {
1440 return ipmi::responseInvalidFieldRequest();
1441 }
1442
1443 std::string connection;
1444 std::string path;
1445 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1446 if (status)
1447 {
1448 phosphor::logging::log<phosphor::logging::level::ERR>(
1449 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1450 phosphor::logging::entry("SENSOR=%d", sensorNum));
1451 return ipmi::response(status);
1452 }
1453
Scron Chang2703b022021-07-06 15:47:45 +08001454#ifdef FEATURE_HYBRID_SENSORS
1455 if (auto sensor = findStaticSensor(path);
1456 sensor != ipmi::sensor::sensors.end() &&
1457 getSensorEventTypeFromPath(path) !=
1458 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1459 {
1460 auto response = ipmi::sensor::get::mapDbusToAssertion(
1461 sensor->second, path, sensor->second.sensorInterface);
1462 std::bitset<16> assertions;
1463 // deassertions are not used.
1464 std::bitset<16> deassertions = 0;
1465 uint8_t sensorEventStatus;
1466 if (response.readingOrStateUnavailable)
1467 {
1468 sensorEventStatus |= static_cast<uint8_t>(
1469 IPMISensorReadingByte2::readingStateUnavailable);
1470 }
1471 if (response.scanningEnabled)
1472 {
1473 sensorEventStatus |= static_cast<uint8_t>(
1474 IPMISensorReadingByte2::sensorScanningEnable);
1475 }
1476 if (response.allEventMessagesEnabled)
1477 {
1478 sensorEventStatus |= static_cast<uint8_t>(
1479 IPMISensorReadingByte2::eventMessagesEnable);
1480 }
1481 assertions |= response.discreteReadingSensorStates << 8;
1482 assertions |= response.thresholdLevelsStates;
1483 return ipmi::responseSuccess(sensorEventStatus, assertions,
1484 deassertions);
1485 }
1486#endif
1487
Willy Tude54f482021-01-26 15:59:09 -08001488 DbusInterfaceMap sensorMap;
1489 if (!getSensorMap(ctx, connection, path, sensorMap))
1490 {
1491 phosphor::logging::log<phosphor::logging::level::ERR>(
1492 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1493 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1494 return ipmi::responseResponseError();
1495 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001496
1497 uint8_t sensorEventStatus =
1498 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1499 std::bitset<16> assertions = 0;
1500 std::bitset<16> deassertions = 0;
1501
1502 // handle VR typed sensor
1503 auto vrInterface = sensorMap.find(sensor::vrInterface);
1504 if (vrInterface != sensorMap.end())
1505 {
1506 if (!sensor::getVrEventStatus(ctx, connection, path,
1507 vrInterface->second, assertions))
1508 {
1509 return ipmi::responseResponseError();
1510 }
1511
1512 // both Event Message and Sensor Scanning are disable for VR.
1513 sensorEventStatus = 0;
1514 return ipmi::responseSuccess(sensorEventStatus, assertions,
1515 deassertions);
1516 }
1517
Willy Tude54f482021-01-26 15:59:09 -08001518 auto warningInterface =
1519 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1520 auto criticalInterface =
1521 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1522
Willy Tude54f482021-01-26 15:59:09 -08001523 std::optional<bool> criticalDeassertHigh =
1524 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1525 std::optional<bool> criticalDeassertLow =
1526 thresholdDeassertMap[path]["CriticalAlarmLow"];
1527 std::optional<bool> warningDeassertHigh =
1528 thresholdDeassertMap[path]["WarningAlarmHigh"];
1529 std::optional<bool> warningDeassertLow =
1530 thresholdDeassertMap[path]["WarningAlarmLow"];
1531
Willy Tude54f482021-01-26 15:59:09 -08001532 if (criticalDeassertHigh && !*criticalDeassertHigh)
1533 {
1534 deassertions.set(static_cast<size_t>(
1535 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1536 }
1537 if (criticalDeassertLow && !*criticalDeassertLow)
1538 {
1539 deassertions.set(static_cast<size_t>(
1540 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1541 }
1542 if (warningDeassertHigh && !*warningDeassertHigh)
1543 {
1544 deassertions.set(static_cast<size_t>(
1545 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1546 }
1547 if (warningDeassertLow && !*warningDeassertLow)
1548 {
1549 deassertions.set(static_cast<size_t>(
1550 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1551 }
1552 if ((warningInterface != sensorMap.end()) ||
1553 (criticalInterface != sensorMap.end()))
1554 {
1555 sensorEventStatus = static_cast<size_t>(
1556 IPMISensorEventEnableByte2::eventMessagesEnable);
1557 if (warningInterface != sensorMap.end())
1558 {
1559 auto& warningMap = warningInterface->second;
1560
1561 auto warningHigh = warningMap.find("WarningAlarmHigh");
1562 auto warningLow = warningMap.find("WarningAlarmLow");
1563 auto warningHighAlarm = false;
1564 auto warningLowAlarm = false;
1565
1566 if (warningHigh != warningMap.end())
1567 {
1568 warningHighAlarm = std::get<bool>(warningHigh->second);
1569 }
1570 if (warningLow != warningMap.end())
1571 {
1572 warningLowAlarm = std::get<bool>(warningLow->second);
1573 }
1574 if (warningHighAlarm)
1575 {
1576 assertions.set(
1577 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1578 upperNonCriticalGoingHigh));
1579 }
1580 if (warningLowAlarm)
1581 {
1582 assertions.set(
1583 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1584 lowerNonCriticalGoingLow));
1585 }
1586 }
1587 if (criticalInterface != sensorMap.end())
1588 {
1589 auto& criticalMap = criticalInterface->second;
1590
1591 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1592 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1593 auto criticalHighAlarm = false;
1594 auto criticalLowAlarm = false;
1595
1596 if (criticalHigh != criticalMap.end())
1597 {
1598 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1599 }
1600 if (criticalLow != criticalMap.end())
1601 {
1602 criticalLowAlarm = std::get<bool>(criticalLow->second);
1603 }
1604 if (criticalHighAlarm)
1605 {
1606 assertions.set(
1607 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1608 upperCriticalGoingHigh));
1609 }
1610 if (criticalLowAlarm)
1611 {
1612 assertions.set(static_cast<size_t>(
1613 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1614 }
1615 }
1616 }
1617
1618 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1619}
1620
Willy Tu38e7a2b2021-03-29 15:09:56 -07001621// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001622void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1623 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001624{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001625 get_sdr::header::set_record_id(
1626 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1627
Willy Tu38e7a2b2021-03-29 15:09:56 -07001628 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1629 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1630
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001631 record.header.sdr_version = ipmiSdrVersion;
1632 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1633 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1634 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001635 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001636 record.key.owner_lun = lun;
1637 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001638}
Willy Tu4eca2512022-06-20 21:14:51 -07001639bool constructSensorSdr(
1640 ipmi::Context::ptr ctx,
1641 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1642 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1643 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001644{
Hao Jiange39d4d82021-04-16 17:02:40 -07001645 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1646
1647 DbusInterfaceMap sensorMap;
1648 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1649 {
1650 phosphor::logging::log<phosphor::logging::level::ERR>(
1651 "Failed to update sensor map for threshold sensor",
1652 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1653 phosphor::logging::entry("PATH=%s", path.c_str()));
1654 return false;
1655 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001656
1657 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1658 record.body.sensor_type = getSensorTypeFromPath(path);
1659 std::string type = getSensorTypeStringFromPath(path);
1660 auto typeCstr = type.c_str();
1661 auto findUnits = sensorUnits.find(typeCstr);
1662 if (findUnits != sensorUnits.end())
1663 {
1664 record.body.sensor_units_2_base =
1665 static_cast<uint8_t>(findUnits->second);
1666 } // else default 0x0 unspecified
1667
1668 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1669
Hao Jiangd2afd052020-12-10 15:09:32 -08001670 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001671 if (sensorObject == sensorMap.end())
1672 {
1673 phosphor::logging::log<phosphor::logging::level::ERR>(
1674 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001675 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001676 }
1677
1678 uint8_t entityId = 0;
1679 uint8_t entityInstance = 0x01;
1680
1681 // follow the association chain to get the parent board's entityid and
1682 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001683 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1684 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001685
1686 record.body.entity_id = entityId;
1687 record.body.entity_instance = entityInstance;
1688
Shakeeb Pasha93889722021-10-14 10:20:13 +05301689 double max = 0;
1690 double min = 0;
1691 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001692
1693 int16_t mValue = 0;
1694 int8_t rExp = 0;
1695 int16_t bValue = 0;
1696 int8_t bExp = 0;
1697 bool bSigned = false;
1698
1699 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1700 {
1701 phosphor::logging::log<phosphor::logging::level::ERR>(
1702 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001703 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001704 }
1705
1706 // The record.body is a struct SensorDataFullRecordBody
1707 // from sensorhandler.hpp in phosphor-ipmi-host.
1708 // The meaning of these bits appears to come from
1709 // table 43.1 of the IPMI spec.
1710 // The above 5 sensor attributes are stuffed in as follows:
1711 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1712 // Byte 22-24 are for other purposes
1713 // Byte 25 = MMMMMMMM = LSB of M
1714 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1715 // Byte 27 = BBBBBBBB = LSB of B
1716 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1717 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1718 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1719
1720 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1721 record.body.m_lsb = mValue & 0xFF;
1722
1723 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1724 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1725
1726 // move the smallest bit of the MSB into place (bit 9)
1727 // the MSbs are bits 7:8 in m_msb_and_tolerance
1728 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1729
1730 record.body.b_lsb = bValue & 0xFF;
1731
1732 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1733 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1734
1735 // move the smallest bit of the MSB into place (bit 9)
1736 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1737 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1738
1739 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1740 uint8_t rExpBits = rExp & 0x07;
1741
1742 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1743 uint8_t bExpBits = bExp & 0x07;
1744
1745 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001746 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1747 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001748
1749 // Set the analog reading byte interpretation accordingly
1750 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1751
1752 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1753 // These seem redundant, but derivable from the above 5 attributes
1754 // Original comment said "todo fill out rest of units"
1755
1756 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001757 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001758 get_sdr::body::set_id_strlen(name.size(), &record.body);
1759 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001760 std::memcpy(record.body.id_string, name.c_str(),
1761 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001762
Josh Lehana55c9532020-10-28 21:59:06 -07001763 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001764 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001765
Jie Yangf0a89942021-07-29 15:30:25 -07001766 bool sensorSettable = false;
1767 auto mutability =
1768 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1769 if (mutability != sensorMap.end())
1770 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001771 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1772 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001773 }
1774 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1775
1776 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001777 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001778
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001779 IPMIThresholds thresholdData;
1780 try
1781 {
1782 thresholdData = getIPMIThresholds(sensorMap);
1783 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001784 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001785 {
1786 phosphor::logging::log<phosphor::logging::level::ERR>(
1787 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001788 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001789 }
1790
1791 if (thresholdData.criticalHigh)
1792 {
1793 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1794 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1795 IPMISensorEventEnableThresholds::criticalThreshold);
1796 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1797 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1798 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1799 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1800 record.body.discrete_reading_setting_mask[0] |=
1801 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1802 }
1803 if (thresholdData.warningHigh)
1804 {
1805 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1806 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1807 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1808 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1809 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1810 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1811 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1812 record.body.discrete_reading_setting_mask[0] |=
1813 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1814 }
1815 if (thresholdData.criticalLow)
1816 {
1817 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1818 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1819 IPMISensorEventEnableThresholds::criticalThreshold);
1820 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1821 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1822 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1823 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1824 record.body.discrete_reading_setting_mask[0] |=
1825 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1826 }
1827 if (thresholdData.warningLow)
1828 {
1829 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1830 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1831 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1832 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1833 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1834 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1835 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1836 record.body.discrete_reading_setting_mask[0] |=
1837 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1838 }
1839
1840 // everything that is readable is setable
1841 record.body.discrete_reading_setting_mask[1] =
1842 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001843 return true;
1844}
1845
Scron Chang2703b022021-07-06 15:47:45 +08001846#ifdef FEATURE_HYBRID_SENSORS
1847// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001848void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001849 uint16_t recordID,
1850 ipmi::sensor::IdInfoMap::const_iterator sensor,
1851 get_sdr::SensorDataFullRecord& record)
1852{
1853 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1854
1855 record.body.entity_id = sensor->second.entityType;
1856 record.body.sensor_type = sensor->second.sensorType;
1857 record.body.event_reading_type = sensor->second.sensorReadingType;
1858 record.body.entity_instance = sensor->second.instance;
1859 if (ipmi::sensor::Mutability::Write ==
1860 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1861 {
1862 get_sdr::body::init_settable_state(true, &(record.body));
1863 }
1864
1865 auto id_string = sensor->second.sensorName;
1866
1867 if (id_string.empty())
1868 {
1869 id_string = sensor->second.sensorNameFunc(sensor->second);
1870 }
1871
1872 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1873 {
1874 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1875 &(record.body));
1876 }
1877 else
1878 {
1879 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1880 }
Paul Fertser51136982022-08-18 12:36:41 +00001881 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08001882 std::strncpy(record.body.id_string, id_string.c_str(),
1883 get_sdr::body::get_id_strlen(&(record.body)));
1884}
1885#endif
1886
Hao Jiange39d4d82021-04-16 17:02:40 -07001887// Construct type 3 SDR header and key (for VR and other discrete sensors)
1888void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1889 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001890{
1891 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1892 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1893
1894 get_sdr::header::set_record_id(
1895 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1896
1897 record.header.sdr_version = ipmiSdrVersion;
1898 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1899 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1900 sizeof(get_sdr::SensorDataRecordHeader);
1901 record.key.owner_id = bmcI2CAddr;
1902 record.key.owner_lun = lun;
1903 record.key.sensor_number = sensornumber;
1904
1905 record.body.entity_id = 0x00;
1906 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001907}
Willy Tu61992ad2021-03-29 15:33:20 -07001908
Hao Jiange39d4d82021-04-16 17:02:40 -07001909// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001910bool constructVrSdr(ipmi::Context::ptr ctx,
1911 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1912 uint16_t sensorNum, uint16_t recordID,
1913 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001914 get_sdr::SensorDataEventRecord& record)
1915{
Hao Jiange39d4d82021-04-16 17:02:40 -07001916 constructEventSdrHeaderKey(sensorNum, recordID, record);
1917
1918 DbusInterfaceMap sensorMap;
1919 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1920 {
1921 phosphor::logging::log<phosphor::logging::level::ERR>(
1922 "Failed to update sensor map for VR sensor",
1923 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1924 phosphor::logging::entry("PATH=%s", path.c_str()));
1925 return false;
1926 }
Willy Tu61992ad2021-03-29 15:33:20 -07001927 // follow the association chain to get the parent board's entityid and
1928 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001929 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1930 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001931 record.body.entity_instance);
1932
1933 // Sensor type is hardcoded as a module/board type instead of parsing from
1934 // sensor path. This is because VR control is allocated in an independent
1935 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1936 // types.
1937 static constexpr const uint8_t module_board_type = 0x15;
1938 record.body.sensor_type = module_board_type;
1939 record.body.event_reading_type = 0x00;
1940
1941 record.body.sensor_record_sharing_1 = 0x00;
1942 record.body.sensor_record_sharing_2 = 0x00;
1943
1944 // populate sensor name from path
1945 auto name = sensor::parseSdrIdFromPath(path);
1946 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00001947 get_sdr::body::set_id_strlen(nameSize, &record.body);
1948 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07001949 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1950 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1951
1952 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001953 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001954
1955 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001956}
1957
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001958static inline uint16_t getNumberOfSensors()
1959{
1960 return std::min(getSensorTree().size(), maxIPMISensors);
1961}
1962
Willy Tu4eca2512022-06-20 21:14:51 -07001963static int getSensorDataRecord(
1964 ipmi::Context::ptr ctx,
1965 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1966 std::vector<uint8_t>& recordData, uint16_t recordID,
1967 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001968{
1969 size_t fruCount = 0;
1970 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1971 if (ret != ipmi::ccSuccess)
1972 {
1973 phosphor::logging::log<phosphor::logging::level::ERR>(
1974 "getSensorDataRecord: getFruSdrCount error");
1975 return GENERAL_ERROR;
1976 }
1977
Harvey Wu05d17c02021-09-15 08:46:59 +08001978 const auto& entityRecords =
1979 ipmi::sensor::EntityInfoMapContainer::getContainer()
1980 ->getIpmiEntityRecords();
1981 size_t entityCount = entityRecords.size();
1982
selvaganapathi7b2e5502023-02-14 07:10:47 +05301983 recordData.clear();
Harvey Wu05d17c02021-09-15 08:46:59 +08001984 size_t lastRecord = getNumberOfSensors() + fruCount +
1985 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001986 if (recordID == lastRecordIndex)
1987 {
1988 recordID = lastRecord;
1989 }
1990 if (recordID > lastRecord)
1991 {
1992 phosphor::logging::log<phosphor::logging::level::ERR>(
1993 "getSensorDataRecord: recordID > lastRecord error");
1994 return GENERAL_ERROR;
1995 }
1996
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001997 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001998 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001999 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07002000
Harvey Wu05d17c02021-09-15 08:46:59 +08002001 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
2002 {
2003 // handle type 8 entity map records
2004 ipmi::sensor::EntityInfoMap::const_iterator entity =
2005 entityRecords.find(static_cast<uint8_t>(
2006 sdrIndex - fruCount - ipmi::storage::type12Count));
2007 if (entity == entityRecords.end())
2008 {
2009 return IPMI_CC_SENSOR_INVALID;
2010 }
2011 recordData = ipmi::storage::getType8SDRs(entity, recordID);
2012 }
2013 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07002014 {
2015 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08002016 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002017 if (type12Index >= ipmi::storage::type12Count)
2018 {
2019 phosphor::logging::log<phosphor::logging::level::ERR>(
2020 "getSensorDataRecord: type12Index error");
2021 return GENERAL_ERROR;
2022 }
2023 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
2024 }
2025 else
2026 {
2027 // handle fru records
2028 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08002029 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002030 if (ret != IPMI_CC_OK)
2031 {
2032 return GENERAL_ERROR;
2033 }
2034 data.header.record_id_msb = recordID >> 8;
2035 data.header.record_id_lsb = recordID & 0xFF;
selvaganapathi7b2e5502023-02-14 07:10:47 +05302036 recordData.insert(recordData.end(),
2037 reinterpret_cast<uint8_t*>(&data),
2038 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tu38e7a2b2021-03-29 15:09:56 -07002039 }
2040
2041 return 0;
2042 }
2043
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002044 // Perform a incremental scan of the SDR Record ID's and translate the
2045 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2046 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2047 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2048 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002049 std::string connection;
2050 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002051 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002052 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002053 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002054 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002055 // LUN 0 has one reserved sensor number. Compensate here by adding one
2056 // to the record ID
2057 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002058 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002059 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002060 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002061 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002062 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2063 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2064 // rules governing its use.
2065 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002066 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002067 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002068
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002069 auto status = getSensorConnection(ctx,
2070 static_cast<uint8_t>(sensNumFromRecID),
2071 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002072 if (status)
2073 {
2074 phosphor::logging::log<phosphor::logging::level::ERR>(
2075 "getSensorDataRecord: getSensorConnection error");
2076 return GENERAL_ERROR;
2077 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002078 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002079 // Return an error on LUN 2 assingments, and any sensor number beyond the
2080 // range of LUN 3
2081 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2082 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002083 {
2084 phosphor::logging::log<phosphor::logging::level::ERR>(
2085 "getSensorDataRecord: invalidSensorNumber");
2086 return GENERAL_ERROR;
2087 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002088 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2089 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2090
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002091 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2092 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002093 {
2094 phosphor::logging::log<phosphor::logging::level::ERR>(
2095 "getSensorDataRecord: sensor record mismatch");
2096 return GENERAL_ERROR;
2097 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002098
Willy Tu38e7a2b2021-03-29 15:09:56 -07002099 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002100 if (std::find(interfaces.begin(), interfaces.end(),
2101 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002102 {
Willy Tu11d68892022-01-20 10:37:34 -08002103 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002104
Hao Jiange39d4d82021-04-16 17:02:40 -07002105 // If the request doesn't read SDR body, construct only header and key
2106 // part to avoid additional DBus transaction.
2107 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2108 {
2109 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2110 }
Willy Tu4eca2512022-06-20 21:14:51 -07002111 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2112 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002113 {
2114 return GENERAL_ERROR;
2115 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002116
selvaganapathi7b2e5502023-02-14 07:10:47 +05302117 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2118 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002119
2120 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002121 }
Willy Tu61992ad2021-03-29 15:33:20 -07002122
Scron Chang2703b022021-07-06 15:47:45 +08002123#ifdef FEATURE_HYBRID_SENSORS
2124 if (auto sensor = findStaticSensor(path);
2125 sensor != ipmi::sensor::sensors.end() &&
2126 getSensorEventTypeFromPath(path) !=
2127 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2128 {
Willy Tu11d68892022-01-20 10:37:34 -08002129 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002130
2131 // If the request doesn't read SDR body, construct only header and key
2132 // part to avoid additional DBus transaction.
2133 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2134 {
2135 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2136 }
2137 else
2138 {
2139 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2140 }
2141
selvaganapathi7b2e5502023-02-14 07:10:47 +05302142 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2143 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002144
2145 return 0;
2146 }
2147#endif
2148
Willy Tu61992ad2021-03-29 15:33:20 -07002149 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002150 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2151 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002152 {
Willy Tu11d68892022-01-20 10:37:34 -08002153 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002154
Hao Jiange39d4d82021-04-16 17:02:40 -07002155 // If the request doesn't read SDR body, construct only header and key
2156 // part to avoid additional DBus transaction.
2157 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2158 {
2159 constructEventSdrHeaderKey(sensorNum, recordID, record);
2160 }
Willy Tu4eca2512022-06-20 21:14:51 -07002161 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2162 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002163 {
2164 return GENERAL_ERROR;
2165 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302166 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2167 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002168 }
2169
Willy Tude54f482021-01-26 15:59:09 -08002170 return 0;
2171}
2172
2173/** @brief implements the get SDR Info command
2174 * @param count - Operation
2175 *
2176 * @returns IPMI completion code plus response data
2177 * - sdrCount - sensor/SDR count
2178 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2179 */
2180static ipmi::RspType<uint8_t, // respcount
2181 uint8_t, // dynamic population flags
2182 uint32_t // last time a sensor was added
2183 >
2184 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2185 std::optional<uint8_t> count)
2186{
2187 auto& sensorTree = getSensorTree();
2188 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002189 uint16_t recordID = 0;
2190 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002191 // Sensors are dynamically allocated, and there is at least one LUN
2192 uint8_t lunsAndDynamicPopulation = 0x80;
2193 constexpr uint8_t getSdrCount = 0x01;
2194 constexpr uint8_t getSensorCount = 0x00;
2195
2196 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2197 {
2198 return ipmi::responseResponseError();
2199 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002200 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002201 if (count.value_or(0) == getSdrCount)
2202 {
Willy Tu4eca2512022-06-20 21:14:51 -07002203 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2204
Harvey Wu75893062023-03-22 17:17:31 +08002205 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002206 {
2207 recordID += maxSensorsPerLUN;
2208 }
Harvey Wu75893062023-03-22 17:17:31 +08002209 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002210 {
2211 recordID += maxSensorsPerLUN * 2;
2212 }
2213
Harvey Wu75893062023-03-22 17:17:31 +08002214 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2215 // assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002216 while (!getSensorDataRecord(
2217 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2218 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002219 {
2220 get_sdr::SensorDataRecordHeader* hdr =
2221 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002222 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302223 if (!hdr)
2224 {
2225 continue;
2226 }
2227
2228 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002229 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002230 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002231 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002232 record.data());
2233 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002234 {
2235 sdrCount++;
2236 }
2237 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302238 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2239 {
2240 get_sdr::SensorDataCompactRecord* recordData =
2241 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2242 record.data());
2243 if (ctx->lun == recordData->key.owner_lun)
2244 {
2245 sdrCount++;
2246 }
2247 }
Harvey Wua3476272023-03-22 10:09:38 +08002248 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2249 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302250 {
2251 sdrCount++;
2252 }
Harvey Wua3476272023-03-22 10:09:38 +08002253
2254 // Because response count data is 1 byte, so sdrCount need to avoid
2255 // overflow.
2256 if (sdrCount == maxSensorsPerLUN)
2257 {
2258 break;
2259 }
Willy Tude54f482021-01-26 15:59:09 -08002260 }
2261 }
2262 else if (count.value_or(0) == getSensorCount)
2263 {
2264 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002265 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002266 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002267 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2268 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002269 }
Harvey Wu75893062023-03-22 17:17:31 +08002270 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002271 {
2272 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2273 ? maxSensorsPerLUN
2274 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2275 }
Harvey Wu75893062023-03-22 17:17:31 +08002276 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002277 {
2278 if (numSensors <= maxIPMISensors)
2279 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002280 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2281 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002282 }
2283 else
2284 {
2285 // error
2286 throw std::out_of_range(
2287 "Maximum number of IPMI sensors exceeded.");
2288 }
2289 }
2290 }
2291 else
2292 {
2293 return ipmi::responseInvalidFieldRequest();
2294 }
2295
2296 // Get Sensor count. This returns the number of sensors
2297 if (numSensors > 0)
2298 {
2299 lunsAndDynamicPopulation |= 1;
2300 }
2301 if (numSensors > maxSensorsPerLUN)
2302 {
2303 lunsAndDynamicPopulation |= 2;
2304 }
2305 if (numSensors >= (maxSensorsPerLUN * 2))
2306 {
2307 lunsAndDynamicPopulation |= 8;
2308 }
2309 if (numSensors > maxIPMISensors)
2310 {
2311 // error
2312 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2313 }
2314
2315 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2316 sdrLastAdd);
2317}
2318
2319/* end sensor commands */
2320
2321/* storage commands */
2322
2323ipmi::RspType<uint8_t, // sdr version
2324 uint16_t, // record count
2325 uint16_t, // free space
2326 uint32_t, // most recent addition
2327 uint32_t, // most recent erase
2328 uint8_t // operationSupport
2329 >
2330 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2331{
2332 auto& sensorTree = getSensorTree();
2333 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002334 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002335 {
2336 return ipmi::responseResponseError();
2337 }
2338
2339 size_t fruCount = 0;
2340 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2341 if (ret != ipmi::ccSuccess)
2342 {
2343 return ipmi::response(ret);
2344 }
2345
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002346 uint16_t recordCount = getNumberOfSensors() + fruCount +
2347 ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002348
2349 uint8_t operationSupport = static_cast<uint8_t>(
2350 SdrRepositoryInfoOps::overflow); // write not supported
2351
2352 operationSupport |=
2353 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2354 operationSupport |= static_cast<uint8_t>(
2355 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2356 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2357 unspecifiedFreeSpace, sdrLastAdd,
2358 sdrLastRemove, operationSupport);
2359}
2360
2361/** @brief implements the get SDR allocation info command
2362 *
2363 * @returns IPMI completion code plus response data
2364 * - allocUnits - Number of possible allocation units
2365 * - allocUnitSize - Allocation unit size in bytes.
2366 * - allocUnitFree - Number of free allocation units
2367 * - allocUnitLargestFree - Largest free block in allocation units
2368 * - maxRecordSize - Maximum record size in allocation units.
2369 */
2370ipmi::RspType<uint16_t, // allocUnits
2371 uint16_t, // allocUnitSize
2372 uint16_t, // allocUnitFree
2373 uint16_t, // allocUnitLargestFree
2374 uint8_t // maxRecordSize
2375 >
2376 ipmiStorageGetSDRAllocationInfo()
2377{
2378 // 0000h unspecified number of alloc units
2379 constexpr uint16_t allocUnits = 0;
2380
2381 constexpr uint16_t allocUnitFree = 0;
2382 constexpr uint16_t allocUnitLargestFree = 0;
2383 // only allow one block at a time
2384 constexpr uint8_t maxRecordSize = 1;
2385
2386 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2387 allocUnitLargestFree, maxRecordSize);
2388}
2389
2390/** @brief implements the reserve SDR command
2391 * @returns IPMI completion code plus response data
2392 * - sdrReservationID
2393 */
2394ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2395{
2396 sdrReservationID++;
2397 if (sdrReservationID == 0)
2398 {
2399 sdrReservationID++;
2400 }
2401
2402 return ipmi::responseSuccess(sdrReservationID);
2403}
2404
2405ipmi::RspType<uint16_t, // next record ID
2406 std::vector<uint8_t> // payload
2407 >
2408 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2409 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2410{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002411 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002412 // reservation required for partial reads with non zero offset into
2413 // record
2414 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2415 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002416 phosphor::logging::log<phosphor::logging::level::ERR>(
2417 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002418 return ipmi::responseInvalidReservationId();
2419 }
Willy Tude54f482021-01-26 15:59:09 -08002420 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2421 if (ret != ipmi::ccSuccess)
2422 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002423 phosphor::logging::log<phosphor::logging::level::ERR>(
2424 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002425 return ipmi::response(ret);
2426 }
2427
Harvey Wu05d17c02021-09-15 08:46:59 +08002428 const auto& entityRecords =
2429 ipmi::sensor::EntityInfoMapContainer::getContainer()
2430 ->getIpmiEntityRecords();
2431 int entityCount = entityRecords.size();
2432
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002433 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002434 size_t lastRecord = getNumberOfSensors() + fruCount +
2435 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002436 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2437
2438 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002439 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002440 phosphor::logging::log<phosphor::logging::level::ERR>(
2441 "ipmiStorageGetSDR: getSensorSubtree error");
2442 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002443 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002444
Willy Tu4eca2512022-06-20 21:14:51 -07002445 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2446
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002447 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002448 if (getSensorDataRecord(
2449 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2450 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002451 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002452 phosphor::logging::log<phosphor::logging::level::ERR>(
2453 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002454 return ipmi::responseInvalidFieldRequest();
2455 }
Willy Tude54f482021-01-26 15:59:09 -08002456 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002457 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002458 if (!hdr)
2459 {
2460 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002461 "ipmiStorageGetSDR: record header is null");
2462 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002463 }
2464
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002465 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2466 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002467 if (offset >= sdrLength)
2468 {
2469 phosphor::logging::log<phosphor::logging::level::ERR>(
2470 "ipmiStorageGetSDR: offset is outside the record");
2471 return ipmi::responseParmOutOfRange();
2472 }
Willy Tude54f482021-01-26 15:59:09 -08002473 if (sdrLength < (offset + bytesToRead))
2474 {
2475 bytesToRead = sdrLength - offset;
2476 }
2477
2478 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2479 if (!respStart)
2480 {
2481 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002482 "ipmiStorageGetSDR: record is null");
2483 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002484 }
2485
2486 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002487
Willy Tude54f482021-01-26 15:59:09 -08002488 return ipmi::responseSuccess(nextRecordId, recordData);
2489}
adarshgrami042e9db2022-09-15 10:34:34 +05302490namespace dcmi
2491{
2492
Thang Tranb1416ef2023-08-02 13:57:09 +07002493std::tuple<uint8_t, // Total of instance sensors
2494 std::vector<sensorInfo> // The list of sensors
2495 >
2496 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2497 uint8_t entityInstance, uint8_t instanceStart)
2498{
2499 std::vector<sensorInfo> sensorList;
2500 uint8_t totalInstSensor = 0;
2501 auto match = ipmi::dcmi::validEntityId.find(entityId);
2502
2503 if (match == ipmi::dcmi::validEntityId.end())
2504 {
2505 return std::make_tuple(totalInstSensor, sensorList);
2506 }
2507
2508 auto& sensorTree = getSensorTree();
2509 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2510 {
2511 return std::make_tuple(totalInstSensor, sensorList);
2512 }
2513
2514 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2515
Willy Tu62e3ca82024-01-31 17:28:34 +00002516 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002517 for (const auto& sensor : sensorTree)
2518 {
2519 const std::string& sensorObjPath = sensor.first;
2520 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2521
2522 /*
2523 * In the DCMI specification, it only supports the sensor type is 0x01
2524 * (temperature type) for both Get Sensor Info and Get Temperature
2525 * Readings commands.
2526 */
2527 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2528 {
2529 continue;
2530 }
2531
2532 const auto& connection = sensor.second.begin()->first;
2533 DbusInterfaceMap sensorMap;
2534
2535 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2536 sensorMapSdrUpdatePeriod))
2537 {
2538 phosphor::logging::log<phosphor::logging::level::ERR>(
2539 "Failed to update sensor map for threshold sensor",
2540 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2541 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2542 continue;
2543 }
2544
2545 uint8_t entityIdValue = 0;
2546 uint8_t entityInstanceValue = 0;
2547
2548 /*
2549 * Get the Entity ID, Entity Instance information which are configured
2550 * in the Entity-Manger.
2551 */
2552 updateIpmiFromAssociation(
2553 sensorObjPath,
2554 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2555 sensorMap, entityIdValue, entityInstanceValue);
2556
2557 if (entityIdValue == match->first || entityIdValue == match->second)
2558 {
2559 totalInstSensor++;
2560
2561 /*
2562 * When Entity Instance parameter is not 0, we only get the first
2563 * sensor whose Entity Instance number is equal input Entity
2564 * Instance parameter.
2565 */
2566 if (entityInstance)
2567 {
2568 if (!sensorList.empty())
2569 {
2570 continue;
2571 }
2572
2573 if (entityInstanceValue == entityInstance)
2574 {
2575 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002576 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002577 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002578 ++invalidSensorNumberErrCount;
2579 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002580 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002581 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2582 recordId, entityIdValue,
2583 entityInstanceValue);
2584 }
2585 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002586 else if (entityInstanceValue >= instanceStart)
2587 {
2588 auto recordId = getSensorNumberFromPath(sensorObjPath);
2589 if (recordId == invalidSensorNumber)
2590 {
2591 ++invalidSensorNumberErrCount;
2592 continue;
2593 }
2594 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2595 recordId, entityIdValue,
2596 entityInstanceValue);
2597 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002598 }
2599 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002600 if (invalidSensorNumberErrCount != 0)
2601 {
2602 phosphor::logging::log<phosphor::logging::level::ERR>(
2603 std::format(
2604 "getSensorNumberFromPath returned invalidSensorNumber {} times",
2605 invalidSensorNumberErrCount)
2606 .data());
2607 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002608
2609 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2610 return first.entityInstance <= second.entityInstance;
2611 };
2612
2613 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2614
2615 return std::make_tuple(totalInstSensor, sensorList);
2616}
2617
Thang Tran3dad8262023-08-17 15:20:56 +07002618std::tuple<bool, // Reading result
2619 uint7_t, // Temp value
2620 bool> // Sign bit
2621 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2622{
2623 std::string service{};
2624 boost::system::error_code ec =
2625 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2626 if (ec.value())
2627 {
2628 return std::make_tuple(false, 0, false);
2629 }
2630
2631 ipmi::PropertyMap properties{};
2632 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2633 sensor::sensorInterface, properties);
2634 if (ec.value())
2635 {
2636 return std::make_tuple(false, 0, false);
2637 }
2638
2639 auto scaleIt = properties.find("Scale");
2640 double scaleVal = 0.0;
2641 if (scaleIt != properties.end())
2642 {
2643 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2644 }
2645
2646 auto tempValIt = properties.find("Value");
2647 double tempVal = 0.0;
2648 if (tempValIt == properties.end())
2649 {
2650 return std::make_tuple(false, 0, false);
2651 }
2652
2653 const double maxTemp = 127;
2654 double absTempVal = 0.0;
2655 bool signBit = false;
2656
2657 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2658 tempVal = std::pow(10, scaleVal) * tempVal;
2659 absTempVal = std::abs(tempVal);
2660 absTempVal = std::min(absTempVal, maxTemp);
2661 signBit = (tempVal < 0) ? true : false;
2662
2663 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2664}
2665
adarshgrami042e9db2022-09-15 10:34:34 +05302666ipmi::RspType<uint8_t, // No of instances for requested id
2667 uint8_t, // No of record ids in the response
2668 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2669 // IDs
2670 >
2671 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002672 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302673{
2674 auto match = ipmi::dcmi::validEntityId.find(entityId);
2675 if (match == ipmi::dcmi::validEntityId.end())
2676 {
2677 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2678
2679 return ipmi::responseInvalidFieldRequest();
2680 }
2681
2682 if (sensorType != ipmi::dcmi::temperatureSensorType)
2683 {
2684 log<level::ERR>("Invalid sensor type",
2685 entry("SENSOR_TYPE=%d", sensorType));
2686
2687 return ipmi::responseInvalidFieldRequest();
2688 }
adarshgrami042e9db2022-09-15 10:34:34 +05302689
2690 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002691 const auto& [totalSensorInst, sensorList] =
2692 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302693
Thang Tranb1416ef2023-08-02 13:57:09 +07002694 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302695 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002696 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2697 }
adarshgrami042e9db2022-09-15 10:34:34 +05302698
Thang Tranb1416ef2023-08-02 13:57:09 +07002699 /*
2700 * As DCMI specification, the maximum number of Record Ids of response data
2701 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2702 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2703 * in response data.
2704 */
2705 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2706
2707 for (const auto& sensor : sensorList)
2708 {
2709 sensorRec.emplace_back(sensor.recordId);
2710 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302711 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002712 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302713 }
2714 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002715
2716 return ipmi::responseSuccess(
2717 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302718}
Thang Tran3dad8262023-08-17 15:20:56 +07002719
2720ipmi::RspType<uint8_t, // No of instances for requested id
2721 uint8_t, // No of record ids in the response
2722 std::vector< // Temperature Data
2723 std::tuple<uint7_t, // Temperature value
2724 bool, // Sign bit
2725 uint8_t // Entity Instance of sensor
2726 >>>
2727 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2728 uint8_t entityId, uint8_t entityInstance,
2729 uint8_t instanceStart)
2730{
2731 auto match = ipmi::dcmi::validEntityId.find(entityId);
2732 if (match == ipmi::dcmi::validEntityId.end())
2733 {
2734 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2735
2736 return ipmi::responseInvalidFieldRequest();
2737 }
2738
2739 if (sensorType != ipmi::dcmi::temperatureSensorType)
2740 {
2741 log<level::ERR>("Invalid sensor type",
2742 entry("SENSOR_TYPE=%d", sensorType));
2743
2744 return ipmi::responseInvalidFieldRequest();
2745 }
2746
2747 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2748 const auto& [totalSensorInst, sensorList] =
2749 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2750
2751 if (sensorList.empty())
2752 {
2753 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2754 }
2755
2756 /*
2757 * As DCMI specification, the maximum number of Record Ids of response data
2758 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2759 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2760 * in response data.
2761 */
2762 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2763
2764 for (const auto& sensor : sensorList)
2765 {
2766 const auto& [readResult, tempVal,
2767 signBit] = readTemp(ctx, sensor.objectPath);
2768
2769 if (readResult)
2770 {
2771 tempReadingVal.emplace_back(
2772 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2773
2774 if (tempReadingVal.size() >= numOfRec)
2775 {
2776 break;
2777 }
2778 }
2779 }
2780
2781 return ipmi::responseSuccess(totalSensorInst,
2782 static_cast<uint8_t>(tempReadingVal.size()),
2783 tempReadingVal);
2784}
2785
adarshgrami042e9db2022-09-15 10:34:34 +05302786} // namespace dcmi
2787
Willy Tude54f482021-01-26 15:59:09 -08002788/* end storage commands */
2789
2790void registerSensorFunctions()
2791{
2792 // <Platform Event>
2793 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2794 ipmi::sensor_event::cmdPlatformEvent,
2795 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2796
Willy Tudbafbce2021-03-29 00:37:05 -07002797 // <Set Sensor Reading and Event Status>
2798 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2799 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2800 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002801
Willy Tude54f482021-01-26 15:59:09 -08002802 // <Get Sensor Reading>
2803 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2804 ipmi::sensor_event::cmdGetSensorReading,
2805 ipmi::Privilege::User, ipmiSenGetSensorReading);
2806
2807 // <Get Sensor Threshold>
2808 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2809 ipmi::sensor_event::cmdGetSensorThreshold,
2810 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2811
2812 // <Set Sensor Threshold>
2813 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2814 ipmi::sensor_event::cmdSetSensorThreshold,
2815 ipmi::Privilege::Operator,
2816 ipmiSenSetSensorThresholds);
2817
2818 // <Get Sensor Event Enable>
2819 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2820 ipmi::sensor_event::cmdGetSensorEventEnable,
2821 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2822
2823 // <Get Sensor Event Status>
2824 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2825 ipmi::sensor_event::cmdGetSensorEventStatus,
2826 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2827
2828 // register all storage commands for both Sensor and Storage command
2829 // versions
2830
2831 // <Get SDR Repository Info>
2832 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2833 ipmi::storage::cmdGetSdrRepositoryInfo,
2834 ipmi::Privilege::User,
2835 ipmiStorageGetSDRRepositoryInfo);
2836
2837 // <Get Device SDR Info>
2838 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2839 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2840 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2841
2842 // <Get SDR Allocation Info>
2843 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2844 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2845 ipmi::Privilege::User,
2846 ipmiStorageGetSDRAllocationInfo);
2847
2848 // <Reserve SDR Repo>
2849 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2850 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2851 ipmi::Privilege::User, ipmiStorageReserveSDR);
2852
2853 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2854 ipmi::storage::cmdReserveSdrRepository,
2855 ipmi::Privilege::User, ipmiStorageReserveSDR);
2856
2857 // <Get Sdr>
2858 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2859 ipmi::sensor_event::cmdGetDeviceSdr,
2860 ipmi::Privilege::User, ipmiStorageGetSDR);
2861
2862 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2863 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2864 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302865 // <Get DCMI Sensor Info>
2866 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2867 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002868 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302869 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002870 // <Get Temperature Readings>
2871 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2872 ipmi::dcmi::cmdGetTemperatureReadings,
2873 ipmi::Privilege::User,
2874 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002875}
2876} // namespace ipmi