blob: 55ba6d94aa72b6a716e76ab1eb04969e2826facb [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>
40#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080041#include <map>
42#include <memory>
43#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080044#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48
Scron Chang2703b022021-07-06 15:47:45 +080049#ifdef FEATURE_HYBRID_SENSORS
50
51#include "sensordatahandler.hpp"
52namespace ipmi
53{
54namespace sensor
55{
56extern const IdInfoMap sensors;
57} // namespace sensor
58} // namespace ipmi
59#endif
adarshgrami042e9db2022-09-15 10:34:34 +053060namespace ipmi
61{
62namespace dcmi
63{
64// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
65static const std::map<uint8_t, uint8_t> validEntityId{
66 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
67 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
68constexpr uint8_t temperatureSensorType = 0x01;
69constexpr uint8_t maxRecords = 8;
70} // namespace dcmi
71} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080072constexpr std::array<const char*, 7> suffixes = {
73 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
74 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080075namespace ipmi
76{
Hao Jiangd48c9212021-02-03 15:45:06 -080077
78using phosphor::logging::entry;
79using phosphor::logging::level;
80using phosphor::logging::log;
81
Willy Tude54f482021-01-26 15:59:09 -080082static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070083static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080084
Willy Tu38e7a2b2021-03-29 15:09:56 -070085// BMC I2C address is generally at 0x20
86static constexpr uint8_t bmcI2CAddr = 0x20;
87
Willy Tude54f482021-01-26 15:59:09 -080088constexpr size_t maxSDRTotalSize =
89 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
90constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
91
92static uint16_t sdrReservationID;
93static uint32_t sdrLastAdd = noTimestamp;
94static uint32_t sdrLastRemove = noTimestamp;
95static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070096
97// The IPMI spec defines four Logical Units (LUN), each capable of supporting
98// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
99// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800100// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700101// location.
102static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
103
Harvey Wu75893062023-03-22 17:17:31 +0800104static constexpr uint8_t lun0 = 0x0;
105static constexpr uint8_t lun1 = 0x1;
106static constexpr uint8_t lun3 = 0x3;
107
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700108static constexpr size_t lun0MaxSensorNum = 0xfe;
109static constexpr size_t lun1MaxSensorNum = 0x1fe;
110static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800111static constexpr int GENERAL_ERROR = -1;
112
Willy Tude54f482021-01-26 15:59:09 -0800113static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
114
115// Specify the comparison required to sort and find char* map objects
116struct CmpStr
117{
118 bool operator()(const char* a, const char* b) const
119 {
120 return std::strcmp(a, b) < 0;
121 }
122};
123const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
124 sensorUnits{{{"temperature", SensorUnits::degreesC},
125 {"voltage", SensorUnits::volts},
126 {"current", SensorUnits::amps},
127 {"fan_tach", SensorUnits::rpm},
128 {"power", SensorUnits::watts}}};
129
130void registerSensorFunctions() __attribute__((constructor));
131
Patrick Williams5d82f472022-07-22 19:26:53 -0500132static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800133 *getSdBus(),
134 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
135 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500136 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500137 getSensorTree().clear();
138 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
139 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
140 std::chrono::system_clock::now().time_since_epoch())
141 .count();
Patrick Williams369824e2023-10-20 11:18:23 -0500142});
Willy Tude54f482021-01-26 15:59:09 -0800143
Patrick Williams5d82f472022-07-22 19:26:53 -0500144static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800145 *getSdBus(),
146 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
147 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500148 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500149 getSensorTree().clear();
150 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
151 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
152 std::chrono::system_clock::now().time_since_epoch())
153 .count();
Patrick Williams369824e2023-10-20 11:18:23 -0500154});
Willy Tude54f482021-01-26 15:59:09 -0800155
156// this keeps track of deassertions for sensor event status command. A
157// deasertion can only happen if an assertion was seen first.
158static boost::container::flat_map<
159 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
160 thresholdDeassertMap;
161
Patrick Williams5d82f472022-07-22 19:26:53 -0500162static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800163 *getSdBus(),
164 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
165 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500166 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500167 boost::container::flat_map<std::string, std::variant<bool, double>> values;
168 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800169
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500170 auto findAssert = std::find_if(values.begin(), values.end(),
171 [](const auto& pair) {
172 return pair.first.find("Alarm") != std::string::npos;
173 });
174 if (findAssert != values.end())
175 {
176 auto ptr = std::get_if<bool>(&(findAssert->second));
177 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800178 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500179 phosphor::logging::log<phosphor::logging::level::ERR>(
180 "thresholdChanged: Assert non bool");
181 return;
182 }
183 if (*ptr)
184 {
185 phosphor::logging::log<phosphor::logging::level::INFO>(
186 "thresholdChanged: Assert",
187 phosphor::logging::entry("SENSOR=%s", m.get_path()));
188 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
189 }
190 else
191 {
192 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
193 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800194 {
195 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500196 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800197 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500198 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800199 }
200 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500201 }
Patrick Williams369824e2023-10-20 11:18:23 -0500202});
Willy Tude54f482021-01-26 15:59:09 -0800203
Hao Jiangd2afd052020-12-10 15:09:32 -0800204namespace sensor
205{
206static constexpr const char* vrInterface =
207 "xyz.openbmc_project.Control.VoltageRegulatorMode";
208static constexpr const char* sensorInterface =
209 "xyz.openbmc_project.Sensor.Value";
210} // namespace sensor
211
Willy Tude54f482021-01-26 15:59:09 -0800212static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
213 double& min)
214{
215 max = 127;
216 min = -128;
217
Hao Jiangd2afd052020-12-10 15:09:32 -0800218 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800219 auto critical =
220 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
221 auto warning =
222 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
223
224 if (sensorObject != sensorMap.end())
225 {
226 auto maxMap = sensorObject->second.find("MaxValue");
227 auto minMap = sensorObject->second.find("MinValue");
228
229 if (maxMap != sensorObject->second.end())
230 {
231 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
232 }
233 if (minMap != sensorObject->second.end())
234 {
235 min = std::visit(VariantToDoubleVisitor(), minMap->second);
236 }
237 }
238 if (critical != sensorMap.end())
239 {
240 auto lower = critical->second.find("CriticalLow");
241 auto upper = critical->second.find("CriticalHigh");
242 if (lower != critical->second.end())
243 {
244 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300245 if (std::isfinite(value))
246 {
247 min = std::min(value, min);
248 }
Willy Tude54f482021-01-26 15:59:09 -0800249 }
250 if (upper != critical->second.end())
251 {
252 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300253 if (std::isfinite(value))
254 {
255 max = std::max(value, max);
256 }
Willy Tude54f482021-01-26 15:59:09 -0800257 }
258 }
259 if (warning != sensorMap.end())
260 {
Willy Tude54f482021-01-26 15:59:09 -0800261 auto lower = warning->second.find("WarningLow");
262 auto upper = warning->second.find("WarningHigh");
263 if (lower != warning->second.end())
264 {
265 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300266 if (std::isfinite(value))
267 {
268 min = std::min(value, min);
269 }
Willy Tude54f482021-01-26 15:59:09 -0800270 }
271 if (upper != warning->second.end())
272 {
273 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300274 if (std::isfinite(value))
275 {
276 max = std::max(value, max);
277 }
Willy Tude54f482021-01-26 15:59:09 -0800278 }
279 }
280}
281
282static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700283 std::string sensorPath, DbusInterfaceMap& sensorMap,
284 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800285{
Scron Chang2703b022021-07-06 15:47:45 +0800286#ifdef FEATURE_HYBRID_SENSORS
287 if (auto sensor = findStaticSensor(sensorPath);
288 sensor != ipmi::sensor::sensors.end() &&
289 getSensorEventTypeFromPath(sensorPath) !=
290 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
291 {
292 // If the incoming sensor is a discrete sensor, it might fail in
293 // getManagedObjects(), return true, and use its own getFunc to get
294 // value.
295 return true;
296 }
297#endif
298
Willy Tude54f482021-01-26 15:59:09 -0800299 static boost::container::flat_map<
300 std::string, std::chrono::time_point<std::chrono::steady_clock>>
301 updateTimeMap;
302
303 auto updateFind = updateTimeMap.find(sensorConnection);
304 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
305 if (updateFind != updateTimeMap.end())
306 {
307 lastUpdate = updateFind->second;
308 }
309
310 auto now = std::chrono::steady_clock::now();
311
312 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700313 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800314 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800315 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800316
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800317 // Object managers for different kinds of OpenBMC DBus interfaces.
318 // Documented in the phosphor-dbus-interfaces repository.
319 const char* paths[] = {
320 "/xyz/openbmc_project/sensors",
321 "/xyz/openbmc_project/vr",
322 };
323 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
324 ObjectValueTree allManagedObjects;
325
326 for (size_t i = 0; i < num_paths; i++)
327 {
328 ObjectValueTree managedObjects;
329 boost::system::error_code ec = getManagedObjects(
330 ctx, sensorConnection.c_str(), paths[i], managedObjects);
331 if (ec)
332 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800333 continue;
334 }
335 allManagedObjects.merge(managedObjects);
336 found = true;
337 }
338
339 if (!found)
340 {
Tom Tung6615d472023-05-31 18:48:12 +0800341 phosphor::logging::log<phosphor::logging::level::ERR>(
342 "GetMangagedObjects for getSensorMap failed",
343 phosphor::logging::entry("SERVICE=%s",
344 sensorConnection.c_str()));
345
Willy Tude54f482021-01-26 15:59:09 -0800346 return false;
347 }
348
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800349 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700350 // Update time after finish building the map which allow the
351 // data to be cached for updatePeriod plus the build time.
352 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800353 }
354 auto connection = SensorCache.find(sensorConnection);
355 if (connection == SensorCache.end())
356 {
357 return false;
358 }
359 auto path = connection->second.find(sensorPath);
360 if (path == connection->second.end())
361 {
362 return false;
363 }
364 sensorMap = path->second;
365
366 return true;
367}
368
Hao Jiangd2afd052020-12-10 15:09:32 -0800369namespace sensor
370{
Hao Jiangd48c9212021-02-03 15:45:06 -0800371// Read VR profiles from sensor(daemon) interface
372static std::optional<std::vector<std::string>>
373 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800374{
375 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800376 auto supportedProperty = object.find("Supported");
377 if (supportedProperty == object.end() ||
378 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800379 {
380 phosphor::logging::log<phosphor::logging::level::ERR>(
381 "Missing the required Supported and Selected properties");
382 return std::nullopt;
383 }
384
385 const auto profilesPtr =
386 std::get_if<std::vector<std::string>>(&supportedProperty->second);
387
388 if (profilesPtr == nullptr)
389 {
390 phosphor::logging::log<phosphor::logging::level::ERR>(
391 "property is not array of string");
392 return std::nullopt;
393 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800394 return *profilesPtr;
395}
396
397// Calculate VR Mode from input IPMI discrete event bytes
398static std::optional<std::string>
399 calculateVRMode(uint15_t assertOffset,
400 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
401{
402 // get VR mode profiles from Supported Interface
403 auto profiles = getSupportedVrProfiles(VRObject);
404 if (!profiles)
405 {
406 return std::nullopt;
407 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800408
409 // interpret IPMI cmd bits into profiles' index
410 long unsigned int index = 0;
411 // only one bit should be set and the highest bit should not be used.
412 if (assertOffset == 0 || assertOffset == (1u << 15) ||
413 (assertOffset & (assertOffset - 1)))
414 {
415 phosphor::logging::log<phosphor::logging::level::ERR>(
416 "IPMI cmd format incorrect",
417
418 phosphor::logging::entry("BYTES=%#02x",
419 static_cast<uint16_t>(assertOffset)));
420 return std::nullopt;
421 }
422
423 while (assertOffset != 1)
424 {
425 assertOffset >>= 1;
426 index++;
427 }
428
Hao Jiangd48c9212021-02-03 15:45:06 -0800429 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800430 {
431 phosphor::logging::log<phosphor::logging::level::ERR>(
432 "profile index out of boundary");
433 return std::nullopt;
434 }
435
Hao Jiangd48c9212021-02-03 15:45:06 -0800436 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800437}
438
439// Calculate sensor value from IPMI reading byte
440static std::optional<double>
441 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
442 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
443{
444 if (valueObject.find("Value") == valueObject.end())
445 {
446 phosphor::logging::log<phosphor::logging::level::ERR>(
447 "Missing the required Value property");
448 return std::nullopt;
449 }
450
451 double max = 0;
452 double min = 0;
453 getSensorMaxMin(sensorMap, max, min);
454
455 int16_t mValue = 0;
456 int16_t bValue = 0;
457 int8_t rExp = 0;
458 int8_t bExp = 0;
459 bool bSigned = false;
460
461 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
462 {
463 return std::nullopt;
464 }
465
466 double value = bSigned ? ((int8_t)reading) : reading;
467
468 value *= ((double)mValue);
469 value += ((double)bValue) * std::pow(10.0, bExp);
470 value *= std::pow(10.0, rExp);
471
472 return value;
473}
474
Willy Tu38e7a2b2021-03-29 15:09:56 -0700475// Extract file name from sensor path as the sensors SDR ID. Simplify the name
476// if it is too long.
477std::string parseSdrIdFromPath(const std::string& path)
478{
479 std::string name;
480 size_t nameStart = path.rfind("/");
481 if (nameStart != std::string::npos)
482 {
483 name = path.substr(nameStart + 1, std::string::npos - nameStart);
484 }
485
Willy Tu38e7a2b2021-03-29 15:09:56 -0700486 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
487 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200488#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800489 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700490 {
JeffLind950f412021-10-20 18:49:34 +0800491 if (boost::ends_with(name, suffix))
492 {
493 boost::replace_all(name, suffix, "");
494 break;
495 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700496 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200497#endif
498#ifdef SHORTNAME_REPLACE_WORDS
499 constexpr std::array<std::pair<const char*, const char*>, 2>
500 replaceWords = {std::make_pair("Output", "Out"),
501 std::make_pair("Input", "In")};
502 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800503 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200504 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800505 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200506#endif
507
508 // as a backup and if nothing else is configured
509 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700510 }
511 return name;
512}
513
Hao Jiangd48c9212021-02-03 15:45:06 -0800514bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
515 const std::string& path,
516 const ipmi::DbusInterfaceMap::mapped_type& object,
517 std::bitset<16>& assertions)
518{
519 auto profiles = sensor::getSupportedVrProfiles(object);
520 if (!profiles)
521 {
522 return false;
523 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700524 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800525
526 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700527 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800528 if (ec)
529 {
530 log<level::ERR>("Failed to get property",
531 entry("PROPERTY=%s", "Selected"),
532 entry("PATH=%s", path.c_str()),
533 entry("INTERFACE=%s", sensor::sensorInterface),
534 entry("WHAT=%s", ec.message().c_str()));
535 return false;
536 }
537
Willy Tu8366f0b2022-04-29 05:00:17 -0700538 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800539 if (itr == profiles->end())
540 {
541 using namespace phosphor::logging;
542 log<level::ERR>("VR mode doesn't match any of its profiles",
543 entry("PATH=%s", path.c_str()));
544 return false;
545 }
546 std::size_t index =
547 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
548
Willy Tubef102a2022-06-09 15:36:09 -0700549 // map index to response event assertion bit.
550 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800551 {
Willy Tubef102a2022-06-09 15:36:09 -0700552 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800553 }
554 else
555 {
556 log<level::ERR>("VR profile index reaches max assertion bit",
557 entry("PATH=%s", path.c_str()),
558 entry("INDEX=%uz", index));
559 return false;
560 }
561 if constexpr (debug)
562 {
563 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700564 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800565 }
566 return true;
567}
Hao Jiangd2afd052020-12-10 15:09:32 -0800568} // namespace sensor
569
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000570ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
571 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800572{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000573 constexpr const uint8_t validEnvmRev = 0x04;
574 constexpr const uint8_t lastSensorType = 0x2C;
575 constexpr const uint8_t oemReserved = 0xC0;
576
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700577 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000578 uint8_t evmRev = 0;
579 uint8_t sensorType = 0;
580 uint8_t sensorNum = 0;
581 uint8_t eventType = 0;
582 uint8_t eventData1 = 0;
583 std::optional<uint8_t> eventData2 = 0;
584 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700585 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000586 ipmi::ChannelInfo chInfo;
587
588 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
589 {
590 phosphor::logging::log<phosphor::logging::level::ERR>(
591 "Failed to get Channel Info",
592 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
593 return ipmi::responseUnspecifiedError();
594 }
595
596 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
597 ipmi::EChannelMediumType::systemInterface)
598 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700599 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000600 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700601 // Refer to IPMI Spec Table 32: SEL Event Records
602 generatorID = (ctx->channel << 12) // Channel
603 | (0x0 << 10) // Reserved
604 | (0x0 << 8) // 0x0 for sys-soft ID
605 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000606 }
607 else
608 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000609 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
610 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700611 // Refer to IPMI Spec Table 32: SEL Event Records
612 generatorID = (ctx->channel << 12) // Channel
613 | (0x0 << 10) // Reserved
614 | ((ctx->lun & 0x3) << 8) // Lun
615 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000616 }
617
618 if (!p.fullyUnpacked())
619 {
620 return ipmi::responseReqDataLenInvalid();
621 }
622
623 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
624 if (evmRev != validEnvmRev)
625 {
626 return ipmi::responseInvalidFieldRequest();
627 }
628 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
629 {
630 return ipmi::responseInvalidFieldRequest();
631 }
632
Willy Tude54f482021-01-26 15:59:09 -0800633 return ipmi::responseSuccess();
634}
635
Willy Tudbafbce2021-03-29 00:37:05 -0700636ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800637 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700638 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800639 bool, uint15_t, bool, uint8_t, uint8_t,
640 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700641{
642 std::string connection;
643 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700644 std::vector<std::string> interfaces;
645
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500646 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
647 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700648 if (status)
649 {
650 return ipmi::response(status);
651 }
652
Hao Jiangd2afd052020-12-10 15:09:32 -0800653 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700654 if (std::find(interfaces.begin(), interfaces.end(),
655 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700656 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700657 DbusInterfaceMap sensorMap;
658 if (!getSensorMap(ctx, connection, path, sensorMap))
659 {
660 return ipmi::responseResponseError();
661 }
662 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800663 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700664 {
665 return ipmi::responseResponseError();
666 }
667
Jie Yangf0a89942021-07-29 15:30:25 -0700668 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800669 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
670 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700671 {
672 return ipmi::responseResponseError();
673 }
674
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500675 auto value = sensor::calculateValue(reading, sensorMap,
676 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800677 if (!value)
678 {
679 return ipmi::responseResponseError();
680 }
681
682 if constexpr (debug)
683 {
684 phosphor::logging::log<phosphor::logging::level::INFO>(
685 "IPMI SET_SENSOR",
686 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
687 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
688 phosphor::logging::entry("VALUE=%f", *value));
689 }
690
691 boost::system::error_code ec =
692 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
693 "Value", ipmi::Value(*value));
694
695 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500696 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800697 // callback functions for now (e.g. ipmiSetSensorReading).
698 if (ec)
699 {
700 using namespace phosphor::logging;
701 log<level::ERR>("Failed to set property",
702 entry("PROPERTY=%s", "Value"),
703 entry("PATH=%s", path.c_str()),
704 entry("INTERFACE=%s", sensor::sensorInterface),
705 entry("WHAT=%s", ec.message().c_str()));
706 return ipmi::responseResponseError();
707 }
708 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700709 }
710
Hao Jiange39d4d82021-04-16 17:02:40 -0700711 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
712 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700713 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700714 DbusInterfaceMap sensorMap;
715 if (!getSensorMap(ctx, connection, path, sensorMap))
716 {
717 return ipmi::responseResponseError();
718 }
719 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800720 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700721 {
722 return ipmi::responseResponseError();
723 }
724
Hao Jiangd2afd052020-12-10 15:09:32 -0800725 // VR sensors are treated as a special case and we will not check the
726 // write permission for VR sensors, since they always deemed writable
727 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500728 auto vrMode = sensor::calculateVRMode(assertOffset,
729 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800730 if (!vrMode)
731 {
732 return ipmi::responseResponseError();
733 }
734 boost::system::error_code ec = setDbusProperty(
735 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
736 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500737 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800738 // callback functions for now (e.g. ipmiSetSensorReading).
739 if (ec)
740 {
741 using namespace phosphor::logging;
742 log<level::ERR>("Failed to set property",
743 entry("PROPERTY=%s", "Selected"),
744 entry("PATH=%s", path.c_str()),
745 entry("INTERFACE=%s", sensor::sensorInterface),
746 entry("WHAT=%s", ec.message().c_str()));
747 return ipmi::responseResponseError();
748 }
749 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700750 }
751
Hao Jiangd2afd052020-12-10 15:09:32 -0800752 phosphor::logging::log<phosphor::logging::level::ERR>(
753 "unknown sensor type",
754 phosphor::logging::entry("PATH=%s", path.c_str()));
755 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700756}
757
Willy Tude54f482021-01-26 15:59:09 -0800758ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
759 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
760{
761 std::string connection;
762 std::string path;
763
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000764 if (sensnum == reservedSensorNumber)
765 {
766 return ipmi::responseInvalidFieldRequest();
767 }
768
Willy Tude54f482021-01-26 15:59:09 -0800769 auto status = getSensorConnection(ctx, sensnum, connection, path);
770 if (status)
771 {
772 return ipmi::response(status);
773 }
774
Scron Chang2703b022021-07-06 15:47:45 +0800775#ifdef FEATURE_HYBRID_SENSORS
776 if (auto sensor = findStaticSensor(path);
777 sensor != ipmi::sensor::sensors.end() &&
778 getSensorEventTypeFromPath(path) !=
779 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
780 {
781 if (ipmi::sensor::Mutability::Read !=
782 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
783 {
784 return ipmi::responseIllegalCommand();
785 }
786
787 uint8_t operation;
788 try
789 {
790 ipmi::sensor::GetSensorResponse getResponse =
791 sensor->second.getFunc(sensor->second);
792
793 if (getResponse.readingOrStateUnavailable)
794 {
795 operation |= static_cast<uint8_t>(
796 IPMISensorReadingByte2::readingStateUnavailable);
797 }
798 if (getResponse.scanningEnabled)
799 {
800 operation |= static_cast<uint8_t>(
801 IPMISensorReadingByte2::sensorScanningEnable);
802 }
803 if (getResponse.allEventMessagesEnabled)
804 {
805 operation |= static_cast<uint8_t>(
806 IPMISensorReadingByte2::eventMessagesEnable);
807 }
808 return ipmi::responseSuccess(
809 getResponse.reading, operation,
810 getResponse.thresholdLevelsStates,
811 getResponse.discreteReadingSensorStates);
812 }
813 catch (const std::exception& e)
814 {
815 operation |= static_cast<uint8_t>(
816 IPMISensorReadingByte2::readingStateUnavailable);
817 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
818 }
819 }
820#endif
821
Willy Tude54f482021-01-26 15:59:09 -0800822 DbusInterfaceMap sensorMap;
823 if (!getSensorMap(ctx, connection, path, sensorMap))
824 {
825 return ipmi::responseResponseError();
826 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800827 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800828
829 if (sensorObject == sensorMap.end() ||
830 sensorObject->second.find("Value") == sensorObject->second.end())
831 {
832 return ipmi::responseResponseError();
833 }
834 auto& valueVariant = sensorObject->second["Value"];
835 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
836
837 double max = 0;
838 double min = 0;
839 getSensorMaxMin(sensorMap, max, min);
840
841 int16_t mValue = 0;
842 int16_t bValue = 0;
843 int8_t rExp = 0;
844 int8_t bExp = 0;
845 bool bSigned = false;
846
847 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
848 {
849 return ipmi::responseResponseError();
850 }
851
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500852 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
853 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800854 uint8_t operation =
855 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
856 operation |=
857 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
858 bool notReading = std::isnan(reading);
859
860 if (!notReading)
861 {
862 auto availableObject =
863 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
864 if (availableObject != sensorMap.end())
865 {
866 auto findAvailable = availableObject->second.find("Available");
867 if (findAvailable != availableObject->second.end())
868 {
869 bool* available = std::get_if<bool>(&(findAvailable->second));
870 if (available && !(*available))
871 {
872 notReading = true;
873 }
874 }
875 }
876 }
877
878 if (notReading)
879 {
880 operation |= static_cast<uint8_t>(
881 IPMISensorReadingByte2::readingStateUnavailable);
882 }
883
Josh Lehana55c9532020-10-28 21:59:06 -0700884 if constexpr (details::enableInstrumentation)
885 {
886 int byteValue;
887 if (bSigned)
888 {
889 byteValue = static_cast<int>(static_cast<int8_t>(value));
890 }
891 else
892 {
893 byteValue = static_cast<int>(static_cast<uint8_t>(value));
894 }
895
896 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800897 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
898 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700899 {
900 // This is the first reading, show the coefficients
901 double step = (max - min) / 255.0;
902 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800903 << details::sdrStatsTable.getName((ctx->lun << 8) |
904 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700905 << ": Range min=" << min << " max=" << max
906 << ", step=" << step
907 << ", Coefficients mValue=" << static_cast<int>(mValue)
908 << " rExp=" << static_cast<int>(rExp)
909 << " bValue=" << static_cast<int>(bValue)
910 << " bExp=" << static_cast<int>(bExp)
911 << " bSigned=" << static_cast<int>(bSigned) << "\n";
912 }
913 }
914
Willy Tude54f482021-01-26 15:59:09 -0800915 uint8_t thresholds = 0;
916
917 auto warningObject =
918 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
919 if (warningObject != sensorMap.end())
920 {
921 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
922 auto alarmLow = warningObject->second.find("WarningAlarmLow");
923 if (alarmHigh != warningObject->second.end())
924 {
925 if (std::get<bool>(alarmHigh->second))
926 {
927 thresholds |= static_cast<uint8_t>(
928 IPMISensorReadingByte3::upperNonCritical);
929 }
930 }
931 if (alarmLow != warningObject->second.end())
932 {
933 if (std::get<bool>(alarmLow->second))
934 {
935 thresholds |= static_cast<uint8_t>(
936 IPMISensorReadingByte3::lowerNonCritical);
937 }
938 }
939 }
940
941 auto criticalObject =
942 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
943 if (criticalObject != sensorMap.end())
944 {
945 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
946 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
947 if (alarmHigh != criticalObject->second.end())
948 {
949 if (std::get<bool>(alarmHigh->second))
950 {
951 thresholds |=
952 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
953 }
954 }
955 if (alarmLow != criticalObject->second.end())
956 {
957 if (std::get<bool>(alarmLow->second))
958 {
959 thresholds |=
960 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
961 }
962 }
963 }
964
965 // no discrete as of today so optional byte is never returned
966 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
967}
968
969/** @brief implements the Set Sensor threshold command
970 * @param sensorNumber - sensor number
971 * @param lowerNonCriticalThreshMask
972 * @param lowerCriticalThreshMask
973 * @param lowerNonRecovThreshMask
974 * @param upperNonCriticalThreshMask
975 * @param upperCriticalThreshMask
976 * @param upperNonRecovThreshMask
977 * @param reserved
978 * @param lowerNonCritical - lower non-critical threshold
979 * @param lowerCritical - Lower critical threshold
980 * @param lowerNonRecoverable - Lower non recovarable threshold
981 * @param upperNonCritical - Upper non-critical threshold
982 * @param upperCritical - Upper critical
983 * @param upperNonRecoverable - Upper Non-recoverable
984 *
985 * @returns IPMI completion code
986 */
987ipmi::RspType<> ipmiSenSetSensorThresholds(
988 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
989 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
990 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
991 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800992 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800993 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800994 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800995{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000996 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800997 {
998 return ipmi::responseInvalidFieldRequest();
999 }
1000
1001 // lower nc and upper nc not suppported on any sensor
1002 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1003 {
1004 return ipmi::responseInvalidFieldRequest();
1005 }
1006
1007 // if none of the threshold mask are set, nothing to do
1008 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1009 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1010 upperCriticalThreshMask | upperNonRecovThreshMask))
1011 {
1012 return ipmi::responseSuccess();
1013 }
1014
1015 std::string connection;
1016 std::string path;
1017
1018 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1019 if (status)
1020 {
1021 return ipmi::response(status);
1022 }
1023 DbusInterfaceMap sensorMap;
1024 if (!getSensorMap(ctx, connection, path, sensorMap))
1025 {
1026 return ipmi::responseResponseError();
1027 }
1028
1029 double max = 0;
1030 double min = 0;
1031 getSensorMaxMin(sensorMap, max, min);
1032
1033 int16_t mValue = 0;
1034 int16_t bValue = 0;
1035 int8_t rExp = 0;
1036 int8_t bExp = 0;
1037 bool bSigned = false;
1038
1039 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1040 {
1041 return ipmi::responseResponseError();
1042 }
1043
1044 // store a vector of property name, value to set, and interface
1045 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1046
1047 // define the indexes of the tuple
1048 constexpr uint8_t propertyName = 0;
1049 constexpr uint8_t thresholdValue = 1;
1050 constexpr uint8_t interface = 2;
1051 // verifiy all needed fields are present
1052 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1053 {
1054 auto findThreshold =
1055 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1056 if (findThreshold == sensorMap.end())
1057 {
1058 return ipmi::responseInvalidFieldRequest();
1059 }
1060 if (lowerCriticalThreshMask)
1061 {
1062 auto findLower = findThreshold->second.find("CriticalLow");
1063 if (findLower == findThreshold->second.end())
1064 {
1065 return ipmi::responseInvalidFieldRequest();
1066 }
1067 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1068 findThreshold->first);
1069 }
1070 if (upperCriticalThreshMask)
1071 {
1072 auto findUpper = findThreshold->second.find("CriticalHigh");
1073 if (findUpper == findThreshold->second.end())
1074 {
1075 return ipmi::responseInvalidFieldRequest();
1076 }
1077 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1078 findThreshold->first);
1079 }
1080 }
1081 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1082 {
1083 auto findThreshold =
1084 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1085 if (findThreshold == sensorMap.end())
1086 {
1087 return ipmi::responseInvalidFieldRequest();
1088 }
1089 if (lowerNonCriticalThreshMask)
1090 {
1091 auto findLower = findThreshold->second.find("WarningLow");
1092 if (findLower == findThreshold->second.end())
1093 {
1094 return ipmi::responseInvalidFieldRequest();
1095 }
1096 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1097 findThreshold->first);
1098 }
1099 if (upperNonCriticalThreshMask)
1100 {
1101 auto findUpper = findThreshold->second.find("WarningHigh");
1102 if (findUpper == findThreshold->second.end())
1103 {
1104 return ipmi::responseInvalidFieldRequest();
1105 }
1106 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1107 findThreshold->first);
1108 }
1109 }
1110 for (const auto& property : thresholdsToSet)
1111 {
1112 // from section 36.3 in the IPMI Spec, assume all linear
1113 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1114 (bValue * std::pow(10.0, bExp))) *
1115 std::pow(10.0, rExp);
1116 setDbusProperty(
1117 *getSdBus(), connection, path, std::get<interface>(property),
1118 std::get<propertyName>(property), ipmi::Value(valueToSet));
1119 }
1120 return ipmi::responseSuccess();
1121}
1122
1123IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1124{
1125 IPMIThresholds resp;
1126 auto warningInterface =
1127 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1128 auto criticalInterface =
1129 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1130
1131 if ((warningInterface != sensorMap.end()) ||
1132 (criticalInterface != sensorMap.end()))
1133 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001134 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001135
1136 if (sensorPair == sensorMap.end())
1137 {
1138 // should not have been able to find a sensor not implementing
1139 // the sensor object
1140 throw std::runtime_error("Invalid sensor map");
1141 }
1142
1143 double max = 0;
1144 double min = 0;
1145 getSensorMaxMin(sensorMap, max, min);
1146
1147 int16_t mValue = 0;
1148 int16_t bValue = 0;
1149 int8_t rExp = 0;
1150 int8_t bExp = 0;
1151 bool bSigned = false;
1152
1153 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1154 {
1155 throw std::runtime_error("Invalid sensor atrributes");
1156 }
1157 if (warningInterface != sensorMap.end())
1158 {
1159 auto& warningMap = warningInterface->second;
1160
1161 auto warningHigh = warningMap.find("WarningHigh");
1162 auto warningLow = warningMap.find("WarningLow");
1163
1164 if (warningHigh != warningMap.end())
1165 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001166 double value = std::visit(VariantToDoubleVisitor(),
1167 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001168 if (std::isfinite(value))
1169 {
1170 resp.warningHigh = scaleIPMIValueFromDouble(
1171 value, mValue, rExp, bValue, bExp, bSigned);
1172 }
Willy Tude54f482021-01-26 15:59:09 -08001173 }
1174 if (warningLow != warningMap.end())
1175 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001176 double value = std::visit(VariantToDoubleVisitor(),
1177 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001178 if (std::isfinite(value))
1179 {
1180 resp.warningLow = scaleIPMIValueFromDouble(
1181 value, mValue, rExp, bValue, bExp, bSigned);
1182 }
Willy Tude54f482021-01-26 15:59:09 -08001183 }
1184 }
1185 if (criticalInterface != sensorMap.end())
1186 {
1187 auto& criticalMap = criticalInterface->second;
1188
1189 auto criticalHigh = criticalMap.find("CriticalHigh");
1190 auto criticalLow = criticalMap.find("CriticalLow");
1191
1192 if (criticalHigh != criticalMap.end())
1193 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001194 double value = std::visit(VariantToDoubleVisitor(),
1195 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001196 if (std::isfinite(value))
1197 {
1198 resp.criticalHigh = scaleIPMIValueFromDouble(
1199 value, mValue, rExp, bValue, bExp, bSigned);
1200 }
Willy Tude54f482021-01-26 15:59:09 -08001201 }
1202 if (criticalLow != criticalMap.end())
1203 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001204 double value = std::visit(VariantToDoubleVisitor(),
1205 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001206 if (std::isfinite(value))
1207 {
1208 resp.criticalLow = scaleIPMIValueFromDouble(
1209 value, mValue, rExp, bValue, bExp, bSigned);
1210 }
Willy Tude54f482021-01-26 15:59:09 -08001211 }
1212 }
1213 }
1214 return resp;
1215}
1216
1217ipmi::RspType<uint8_t, // readable
1218 uint8_t, // lowerNCrit
1219 uint8_t, // lowerCrit
1220 uint8_t, // lowerNrecoverable
1221 uint8_t, // upperNC
1222 uint8_t, // upperCrit
1223 uint8_t> // upperNRecoverable
1224 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1225{
1226 std::string connection;
1227 std::string path;
1228
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001229 if (sensorNumber == reservedSensorNumber)
1230 {
1231 return ipmi::responseInvalidFieldRequest();
1232 }
1233
Willy Tude54f482021-01-26 15:59:09 -08001234 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1235 if (status)
1236 {
1237 return ipmi::response(status);
1238 }
1239
1240 DbusInterfaceMap sensorMap;
1241 if (!getSensorMap(ctx, connection, path, sensorMap))
1242 {
1243 return ipmi::responseResponseError();
1244 }
1245
1246 IPMIThresholds thresholdData;
1247 try
1248 {
1249 thresholdData = getIPMIThresholds(sensorMap);
1250 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001251 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001252 {
1253 return ipmi::responseResponseError();
1254 }
1255
1256 uint8_t readable = 0;
1257 uint8_t lowerNC = 0;
1258 uint8_t lowerCritical = 0;
1259 uint8_t lowerNonRecoverable = 0;
1260 uint8_t upperNC = 0;
1261 uint8_t upperCritical = 0;
1262 uint8_t upperNonRecoverable = 0;
1263
1264 if (thresholdData.warningHigh)
1265 {
1266 readable |=
1267 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1268 upperNC = *thresholdData.warningHigh;
1269 }
1270 if (thresholdData.warningLow)
1271 {
1272 readable |=
1273 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1274 lowerNC = *thresholdData.warningLow;
1275 }
1276
1277 if (thresholdData.criticalHigh)
1278 {
1279 readable |=
1280 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1281 upperCritical = *thresholdData.criticalHigh;
1282 }
1283 if (thresholdData.criticalLow)
1284 {
1285 readable |=
1286 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1287 lowerCritical = *thresholdData.criticalLow;
1288 }
1289
1290 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1291 lowerNonRecoverable, upperNC, upperCritical,
1292 upperNonRecoverable);
1293}
1294
1295/** @brief implements the get Sensor event enable command
1296 * @param sensorNumber - sensor number
1297 *
1298 * @returns IPMI completion code plus response data
1299 * - enabled - Sensor Event messages
1300 * - assertionEnabledLsb - Assertion event messages
1301 * - assertionEnabledMsb - Assertion event messages
1302 * - deassertionEnabledLsb - Deassertion event messages
1303 * - deassertionEnabledMsb - Deassertion event messages
1304 */
1305
1306ipmi::RspType<uint8_t, // enabled
1307 uint8_t, // assertionEnabledLsb
1308 uint8_t, // assertionEnabledMsb
1309 uint8_t, // deassertionEnabledLsb
1310 uint8_t> // deassertionEnabledMsb
1311 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1312{
1313 std::string connection;
1314 std::string path;
1315
1316 uint8_t enabled = 0;
1317 uint8_t assertionEnabledLsb = 0;
1318 uint8_t assertionEnabledMsb = 0;
1319 uint8_t deassertionEnabledLsb = 0;
1320 uint8_t deassertionEnabledMsb = 0;
1321
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001322 if (sensorNum == reservedSensorNumber)
1323 {
1324 return ipmi::responseInvalidFieldRequest();
1325 }
1326
Willy Tude54f482021-01-26 15:59:09 -08001327 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1328 if (status)
1329 {
1330 return ipmi::response(status);
1331 }
1332
Scron Chang2703b022021-07-06 15:47:45 +08001333#ifdef FEATURE_HYBRID_SENSORS
1334 if (auto sensor = findStaticSensor(path);
1335 sensor != ipmi::sensor::sensors.end() &&
1336 getSensorEventTypeFromPath(path) !=
1337 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1338 {
1339 enabled = static_cast<uint8_t>(
1340 IPMISensorEventEnableByte2::sensorScanningEnable);
1341 uint16_t assertionEnabled = 0;
1342 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1343 ->second.begin()
1344 ->second.second)
1345 {
1346 assertionEnabled |= (1 << offsetValMap.first);
1347 }
1348 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1349 assertionEnabledMsb =
1350 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1351
1352 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1353 assertionEnabledMsb, deassertionEnabledLsb,
1354 deassertionEnabledMsb);
1355 }
1356#endif
1357
Willy Tude54f482021-01-26 15:59:09 -08001358 DbusInterfaceMap sensorMap;
1359 if (!getSensorMap(ctx, connection, path, sensorMap))
1360 {
1361 return ipmi::responseResponseError();
1362 }
1363
1364 auto warningInterface =
1365 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1366 auto criticalInterface =
1367 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1368 if ((warningInterface != sensorMap.end()) ||
1369 (criticalInterface != sensorMap.end()))
1370 {
1371 enabled = static_cast<uint8_t>(
1372 IPMISensorEventEnableByte2::sensorScanningEnable);
1373 if (warningInterface != sensorMap.end())
1374 {
1375 auto& warningMap = warningInterface->second;
1376
1377 auto warningHigh = warningMap.find("WarningHigh");
1378 auto warningLow = warningMap.find("WarningLow");
1379 if (warningHigh != warningMap.end())
1380 {
1381 assertionEnabledLsb |= static_cast<uint8_t>(
1382 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1383 deassertionEnabledLsb |= static_cast<uint8_t>(
1384 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1385 }
1386 if (warningLow != warningMap.end())
1387 {
1388 assertionEnabledLsb |= static_cast<uint8_t>(
1389 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1390 deassertionEnabledLsb |= static_cast<uint8_t>(
1391 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1392 }
1393 }
1394 if (criticalInterface != sensorMap.end())
1395 {
1396 auto& criticalMap = criticalInterface->second;
1397
1398 auto criticalHigh = criticalMap.find("CriticalHigh");
1399 auto criticalLow = criticalMap.find("CriticalLow");
1400
1401 if (criticalHigh != criticalMap.end())
1402 {
1403 assertionEnabledMsb |= static_cast<uint8_t>(
1404 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1405 deassertionEnabledMsb |= static_cast<uint8_t>(
1406 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1407 }
1408 if (criticalLow != criticalMap.end())
1409 {
1410 assertionEnabledLsb |= static_cast<uint8_t>(
1411 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1412 deassertionEnabledLsb |= static_cast<uint8_t>(
1413 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1414 }
1415 }
1416 }
1417
1418 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1419 assertionEnabledMsb, deassertionEnabledLsb,
1420 deassertionEnabledMsb);
1421}
1422
1423/** @brief implements the get Sensor event status command
1424 * @param sensorNumber - sensor number, FFh = reserved
1425 *
1426 * @returns IPMI completion code plus response data
1427 * - sensorEventStatus - Sensor Event messages state
1428 * - assertions - Assertion event messages
1429 * - deassertions - Deassertion event messages
1430 */
1431ipmi::RspType<uint8_t, // sensorEventStatus
1432 std::bitset<16>, // assertions
1433 std::bitset<16> // deassertion
1434 >
1435 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1436{
1437 if (sensorNum == reservedSensorNumber)
1438 {
1439 return ipmi::responseInvalidFieldRequest();
1440 }
1441
1442 std::string connection;
1443 std::string path;
1444 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1445 if (status)
1446 {
1447 phosphor::logging::log<phosphor::logging::level::ERR>(
1448 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1449 phosphor::logging::entry("SENSOR=%d", sensorNum));
1450 return ipmi::response(status);
1451 }
1452
Scron Chang2703b022021-07-06 15:47:45 +08001453#ifdef FEATURE_HYBRID_SENSORS
1454 if (auto sensor = findStaticSensor(path);
1455 sensor != ipmi::sensor::sensors.end() &&
1456 getSensorEventTypeFromPath(path) !=
1457 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1458 {
1459 auto response = ipmi::sensor::get::mapDbusToAssertion(
1460 sensor->second, path, sensor->second.sensorInterface);
1461 std::bitset<16> assertions;
1462 // deassertions are not used.
1463 std::bitset<16> deassertions = 0;
1464 uint8_t sensorEventStatus;
1465 if (response.readingOrStateUnavailable)
1466 {
1467 sensorEventStatus |= static_cast<uint8_t>(
1468 IPMISensorReadingByte2::readingStateUnavailable);
1469 }
1470 if (response.scanningEnabled)
1471 {
1472 sensorEventStatus |= static_cast<uint8_t>(
1473 IPMISensorReadingByte2::sensorScanningEnable);
1474 }
1475 if (response.allEventMessagesEnabled)
1476 {
1477 sensorEventStatus |= static_cast<uint8_t>(
1478 IPMISensorReadingByte2::eventMessagesEnable);
1479 }
1480 assertions |= response.discreteReadingSensorStates << 8;
1481 assertions |= response.thresholdLevelsStates;
1482 return ipmi::responseSuccess(sensorEventStatus, assertions,
1483 deassertions);
1484 }
1485#endif
1486
Willy Tude54f482021-01-26 15:59:09 -08001487 DbusInterfaceMap sensorMap;
1488 if (!getSensorMap(ctx, connection, path, sensorMap))
1489 {
1490 phosphor::logging::log<phosphor::logging::level::ERR>(
1491 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1492 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1493 return ipmi::responseResponseError();
1494 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001495
1496 uint8_t sensorEventStatus =
1497 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1498 std::bitset<16> assertions = 0;
1499 std::bitset<16> deassertions = 0;
1500
1501 // handle VR typed sensor
1502 auto vrInterface = sensorMap.find(sensor::vrInterface);
1503 if (vrInterface != sensorMap.end())
1504 {
1505 if (!sensor::getVrEventStatus(ctx, connection, path,
1506 vrInterface->second, assertions))
1507 {
1508 return ipmi::responseResponseError();
1509 }
1510
1511 // both Event Message and Sensor Scanning are disable for VR.
1512 sensorEventStatus = 0;
1513 return ipmi::responseSuccess(sensorEventStatus, assertions,
1514 deassertions);
1515 }
1516
Willy Tude54f482021-01-26 15:59:09 -08001517 auto warningInterface =
1518 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1519 auto criticalInterface =
1520 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1521
Willy Tude54f482021-01-26 15:59:09 -08001522 std::optional<bool> criticalDeassertHigh =
1523 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1524 std::optional<bool> criticalDeassertLow =
1525 thresholdDeassertMap[path]["CriticalAlarmLow"];
1526 std::optional<bool> warningDeassertHigh =
1527 thresholdDeassertMap[path]["WarningAlarmHigh"];
1528 std::optional<bool> warningDeassertLow =
1529 thresholdDeassertMap[path]["WarningAlarmLow"];
1530
Willy Tude54f482021-01-26 15:59:09 -08001531 if (criticalDeassertHigh && !*criticalDeassertHigh)
1532 {
1533 deassertions.set(static_cast<size_t>(
1534 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1535 }
1536 if (criticalDeassertLow && !*criticalDeassertLow)
1537 {
1538 deassertions.set(static_cast<size_t>(
1539 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1540 }
1541 if (warningDeassertHigh && !*warningDeassertHigh)
1542 {
1543 deassertions.set(static_cast<size_t>(
1544 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1545 }
1546 if (warningDeassertLow && !*warningDeassertLow)
1547 {
1548 deassertions.set(static_cast<size_t>(
1549 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1550 }
1551 if ((warningInterface != sensorMap.end()) ||
1552 (criticalInterface != sensorMap.end()))
1553 {
1554 sensorEventStatus = static_cast<size_t>(
1555 IPMISensorEventEnableByte2::eventMessagesEnable);
1556 if (warningInterface != sensorMap.end())
1557 {
1558 auto& warningMap = warningInterface->second;
1559
1560 auto warningHigh = warningMap.find("WarningAlarmHigh");
1561 auto warningLow = warningMap.find("WarningAlarmLow");
1562 auto warningHighAlarm = false;
1563 auto warningLowAlarm = false;
1564
1565 if (warningHigh != warningMap.end())
1566 {
1567 warningHighAlarm = std::get<bool>(warningHigh->second);
1568 }
1569 if (warningLow != warningMap.end())
1570 {
1571 warningLowAlarm = std::get<bool>(warningLow->second);
1572 }
1573 if (warningHighAlarm)
1574 {
1575 assertions.set(
1576 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1577 upperNonCriticalGoingHigh));
1578 }
1579 if (warningLowAlarm)
1580 {
1581 assertions.set(
1582 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1583 lowerNonCriticalGoingLow));
1584 }
1585 }
1586 if (criticalInterface != sensorMap.end())
1587 {
1588 auto& criticalMap = criticalInterface->second;
1589
1590 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1591 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1592 auto criticalHighAlarm = false;
1593 auto criticalLowAlarm = false;
1594
1595 if (criticalHigh != criticalMap.end())
1596 {
1597 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1598 }
1599 if (criticalLow != criticalMap.end())
1600 {
1601 criticalLowAlarm = std::get<bool>(criticalLow->second);
1602 }
1603 if (criticalHighAlarm)
1604 {
1605 assertions.set(
1606 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1607 upperCriticalGoingHigh));
1608 }
1609 if (criticalLowAlarm)
1610 {
1611 assertions.set(static_cast<size_t>(
1612 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1613 }
1614 }
1615 }
1616
1617 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1618}
1619
Willy Tu38e7a2b2021-03-29 15:09:56 -07001620// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001621void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1622 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001623{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001624 get_sdr::header::set_record_id(
1625 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1626
Willy Tu38e7a2b2021-03-29 15:09:56 -07001627 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1628 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1629
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001630 record.header.sdr_version = ipmiSdrVersion;
1631 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1632 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1633 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001634 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001635 record.key.owner_lun = lun;
1636 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001637}
Willy Tu4eca2512022-06-20 21:14:51 -07001638bool constructSensorSdr(
1639 ipmi::Context::ptr ctx,
1640 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1641 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1642 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001643{
Hao Jiange39d4d82021-04-16 17:02:40 -07001644 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1645
1646 DbusInterfaceMap sensorMap;
1647 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1648 {
1649 phosphor::logging::log<phosphor::logging::level::ERR>(
1650 "Failed to update sensor map for threshold sensor",
1651 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1652 phosphor::logging::entry("PATH=%s", path.c_str()));
1653 return false;
1654 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001655
1656 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1657 record.body.sensor_type = getSensorTypeFromPath(path);
1658 std::string type = getSensorTypeStringFromPath(path);
1659 auto typeCstr = type.c_str();
1660 auto findUnits = sensorUnits.find(typeCstr);
1661 if (findUnits != sensorUnits.end())
1662 {
1663 record.body.sensor_units_2_base =
1664 static_cast<uint8_t>(findUnits->second);
1665 } // else default 0x0 unspecified
1666
1667 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1668
Hao Jiangd2afd052020-12-10 15:09:32 -08001669 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001670 if (sensorObject == sensorMap.end())
1671 {
1672 phosphor::logging::log<phosphor::logging::level::ERR>(
1673 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001674 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001675 }
1676
1677 uint8_t entityId = 0;
1678 uint8_t entityInstance = 0x01;
1679
1680 // follow the association chain to get the parent board's entityid and
1681 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001682 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1683 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001684
1685 record.body.entity_id = entityId;
1686 record.body.entity_instance = entityInstance;
1687
Shakeeb Pasha93889722021-10-14 10:20:13 +05301688 double max = 0;
1689 double min = 0;
1690 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001691
1692 int16_t mValue = 0;
1693 int8_t rExp = 0;
1694 int16_t bValue = 0;
1695 int8_t bExp = 0;
1696 bool bSigned = false;
1697
1698 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1699 {
1700 phosphor::logging::log<phosphor::logging::level::ERR>(
1701 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001702 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001703 }
1704
1705 // The record.body is a struct SensorDataFullRecordBody
1706 // from sensorhandler.hpp in phosphor-ipmi-host.
1707 // The meaning of these bits appears to come from
1708 // table 43.1 of the IPMI spec.
1709 // The above 5 sensor attributes are stuffed in as follows:
1710 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1711 // Byte 22-24 are for other purposes
1712 // Byte 25 = MMMMMMMM = LSB of M
1713 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1714 // Byte 27 = BBBBBBBB = LSB of B
1715 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1716 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1717 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1718
1719 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1720 record.body.m_lsb = mValue & 0xFF;
1721
1722 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1723 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1724
1725 // move the smallest bit of the MSB into place (bit 9)
1726 // the MSbs are bits 7:8 in m_msb_and_tolerance
1727 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1728
1729 record.body.b_lsb = bValue & 0xFF;
1730
1731 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1732 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1733
1734 // move the smallest bit of the MSB into place (bit 9)
1735 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1736 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1737
1738 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1739 uint8_t rExpBits = rExp & 0x07;
1740
1741 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1742 uint8_t bExpBits = bExp & 0x07;
1743
1744 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001745 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1746 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001747
1748 // Set the analog reading byte interpretation accordingly
1749 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1750
1751 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1752 // These seem redundant, but derivable from the above 5 attributes
1753 // Original comment said "todo fill out rest of units"
1754
1755 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001756 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001757 get_sdr::body::set_id_strlen(name.size(), &record.body);
1758 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001759 std::memcpy(record.body.id_string, name.c_str(),
1760 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001761
Josh Lehana55c9532020-10-28 21:59:06 -07001762 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001763 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001764
Jie Yangf0a89942021-07-29 15:30:25 -07001765 bool sensorSettable = false;
1766 auto mutability =
1767 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1768 if (mutability != sensorMap.end())
1769 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001770 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1771 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001772 }
1773 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1774
1775 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001776 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001777
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001778 IPMIThresholds thresholdData;
1779 try
1780 {
1781 thresholdData = getIPMIThresholds(sensorMap);
1782 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001783 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001784 {
1785 phosphor::logging::log<phosphor::logging::level::ERR>(
1786 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001787 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001788 }
1789
1790 if (thresholdData.criticalHigh)
1791 {
1792 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1793 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1794 IPMISensorEventEnableThresholds::criticalThreshold);
1795 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1796 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1797 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1798 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1799 record.body.discrete_reading_setting_mask[0] |=
1800 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1801 }
1802 if (thresholdData.warningHigh)
1803 {
1804 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1805 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1806 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1807 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1808 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1809 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1810 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1811 record.body.discrete_reading_setting_mask[0] |=
1812 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1813 }
1814 if (thresholdData.criticalLow)
1815 {
1816 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1817 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1818 IPMISensorEventEnableThresholds::criticalThreshold);
1819 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1820 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1821 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1822 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1823 record.body.discrete_reading_setting_mask[0] |=
1824 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1825 }
1826 if (thresholdData.warningLow)
1827 {
1828 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1829 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1830 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1831 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1832 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1833 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1834 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1835 record.body.discrete_reading_setting_mask[0] |=
1836 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1837 }
1838
1839 // everything that is readable is setable
1840 record.body.discrete_reading_setting_mask[1] =
1841 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001842 return true;
1843}
1844
Scron Chang2703b022021-07-06 15:47:45 +08001845#ifdef FEATURE_HYBRID_SENSORS
1846// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001847void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001848 uint16_t recordID,
1849 ipmi::sensor::IdInfoMap::const_iterator sensor,
1850 get_sdr::SensorDataFullRecord& record)
1851{
1852 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1853
1854 record.body.entity_id = sensor->second.entityType;
1855 record.body.sensor_type = sensor->second.sensorType;
1856 record.body.event_reading_type = sensor->second.sensorReadingType;
1857 record.body.entity_instance = sensor->second.instance;
1858 if (ipmi::sensor::Mutability::Write ==
1859 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1860 {
1861 get_sdr::body::init_settable_state(true, &(record.body));
1862 }
1863
1864 auto id_string = sensor->second.sensorName;
1865
1866 if (id_string.empty())
1867 {
1868 id_string = sensor->second.sensorNameFunc(sensor->second);
1869 }
1870
1871 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1872 {
1873 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1874 &(record.body));
1875 }
1876 else
1877 {
1878 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1879 }
Paul Fertser51136982022-08-18 12:36:41 +00001880 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08001881 std::strncpy(record.body.id_string, id_string.c_str(),
1882 get_sdr::body::get_id_strlen(&(record.body)));
1883}
1884#endif
1885
Hao Jiange39d4d82021-04-16 17:02:40 -07001886// Construct type 3 SDR header and key (for VR and other discrete sensors)
1887void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1888 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001889{
1890 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1891 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1892
1893 get_sdr::header::set_record_id(
1894 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1895
1896 record.header.sdr_version = ipmiSdrVersion;
1897 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1898 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1899 sizeof(get_sdr::SensorDataRecordHeader);
1900 record.key.owner_id = bmcI2CAddr;
1901 record.key.owner_lun = lun;
1902 record.key.sensor_number = sensornumber;
1903
1904 record.body.entity_id = 0x00;
1905 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001906}
Willy Tu61992ad2021-03-29 15:33:20 -07001907
Hao Jiange39d4d82021-04-16 17:02:40 -07001908// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001909bool constructVrSdr(ipmi::Context::ptr ctx,
1910 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1911 uint16_t sensorNum, uint16_t recordID,
1912 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001913 get_sdr::SensorDataEventRecord& record)
1914{
Hao Jiange39d4d82021-04-16 17:02:40 -07001915 constructEventSdrHeaderKey(sensorNum, recordID, record);
1916
1917 DbusInterfaceMap sensorMap;
1918 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1919 {
1920 phosphor::logging::log<phosphor::logging::level::ERR>(
1921 "Failed to update sensor map for VR sensor",
1922 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1923 phosphor::logging::entry("PATH=%s", path.c_str()));
1924 return false;
1925 }
Willy Tu61992ad2021-03-29 15:33:20 -07001926 // follow the association chain to get the parent board's entityid and
1927 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001928 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1929 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001930 record.body.entity_instance);
1931
1932 // Sensor type is hardcoded as a module/board type instead of parsing from
1933 // sensor path. This is because VR control is allocated in an independent
1934 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1935 // types.
1936 static constexpr const uint8_t module_board_type = 0x15;
1937 record.body.sensor_type = module_board_type;
1938 record.body.event_reading_type = 0x00;
1939
1940 record.body.sensor_record_sharing_1 = 0x00;
1941 record.body.sensor_record_sharing_2 = 0x00;
1942
1943 // populate sensor name from path
1944 auto name = sensor::parseSdrIdFromPath(path);
1945 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00001946 get_sdr::body::set_id_strlen(nameSize, &record.body);
1947 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07001948 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1949 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1950
1951 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001952 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001953
1954 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001955}
1956
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001957static inline uint16_t getNumberOfSensors()
1958{
1959 return std::min(getSensorTree().size(), maxIPMISensors);
1960}
1961
Willy Tu4eca2512022-06-20 21:14:51 -07001962static int getSensorDataRecord(
1963 ipmi::Context::ptr ctx,
1964 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1965 std::vector<uint8_t>& recordData, uint16_t recordID,
1966 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001967{
1968 size_t fruCount = 0;
1969 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1970 if (ret != ipmi::ccSuccess)
1971 {
1972 phosphor::logging::log<phosphor::logging::level::ERR>(
1973 "getSensorDataRecord: getFruSdrCount error");
1974 return GENERAL_ERROR;
1975 }
1976
Harvey Wu05d17c02021-09-15 08:46:59 +08001977 const auto& entityRecords =
1978 ipmi::sensor::EntityInfoMapContainer::getContainer()
1979 ->getIpmiEntityRecords();
1980 size_t entityCount = entityRecords.size();
1981
selvaganapathi7b2e5502023-02-14 07:10:47 +05301982 recordData.clear();
Harvey Wu05d17c02021-09-15 08:46:59 +08001983 size_t lastRecord = getNumberOfSensors() + fruCount +
1984 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001985 if (recordID == lastRecordIndex)
1986 {
1987 recordID = lastRecord;
1988 }
1989 if (recordID > lastRecord)
1990 {
1991 phosphor::logging::log<phosphor::logging::level::ERR>(
1992 "getSensorDataRecord: recordID > lastRecord error");
1993 return GENERAL_ERROR;
1994 }
1995
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001996 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001997 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001998 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001999
Harvey Wu05d17c02021-09-15 08:46:59 +08002000 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
2001 {
2002 // handle type 8 entity map records
2003 ipmi::sensor::EntityInfoMap::const_iterator entity =
2004 entityRecords.find(static_cast<uint8_t>(
2005 sdrIndex - fruCount - ipmi::storage::type12Count));
2006 if (entity == entityRecords.end())
2007 {
2008 return IPMI_CC_SENSOR_INVALID;
2009 }
2010 recordData = ipmi::storage::getType8SDRs(entity, recordID);
2011 }
2012 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07002013 {
2014 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08002015 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002016 if (type12Index >= ipmi::storage::type12Count)
2017 {
2018 phosphor::logging::log<phosphor::logging::level::ERR>(
2019 "getSensorDataRecord: type12Index error");
2020 return GENERAL_ERROR;
2021 }
2022 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
2023 }
2024 else
2025 {
2026 // handle fru records
2027 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08002028 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002029 if (ret != IPMI_CC_OK)
2030 {
2031 return GENERAL_ERROR;
2032 }
2033 data.header.record_id_msb = recordID >> 8;
2034 data.header.record_id_lsb = recordID & 0xFF;
selvaganapathi7b2e5502023-02-14 07:10:47 +05302035 recordData.insert(recordData.end(),
2036 reinterpret_cast<uint8_t*>(&data),
2037 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tu38e7a2b2021-03-29 15:09:56 -07002038 }
2039
2040 return 0;
2041 }
2042
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002043 // Perform a incremental scan of the SDR Record ID's and translate the
2044 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2045 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2046 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2047 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002048 std::string connection;
2049 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002050 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002051 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002052 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002053 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002054 // LUN 0 has one reserved sensor number. Compensate here by adding one
2055 // to the record ID
2056 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002057 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002058 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002059 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002060 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002061 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2062 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2063 // rules governing its use.
2064 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002065 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002066 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002067
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002068 auto status = getSensorConnection(ctx,
2069 static_cast<uint8_t>(sensNumFromRecID),
2070 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002071 if (status)
2072 {
2073 phosphor::logging::log<phosphor::logging::level::ERR>(
2074 "getSensorDataRecord: getSensorConnection error");
2075 return GENERAL_ERROR;
2076 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002077 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002078 // Return an error on LUN 2 assingments, and any sensor number beyond the
2079 // range of LUN 3
2080 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2081 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002082 {
2083 phosphor::logging::log<phosphor::logging::level::ERR>(
2084 "getSensorDataRecord: invalidSensorNumber");
2085 return GENERAL_ERROR;
2086 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002087 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2088 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2089
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002090 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2091 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002092 {
2093 phosphor::logging::log<phosphor::logging::level::ERR>(
2094 "getSensorDataRecord: sensor record mismatch");
2095 return GENERAL_ERROR;
2096 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002097
Willy Tu38e7a2b2021-03-29 15:09:56 -07002098 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002099 if (std::find(interfaces.begin(), interfaces.end(),
2100 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002101 {
Willy Tu11d68892022-01-20 10:37:34 -08002102 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002103
Hao Jiange39d4d82021-04-16 17:02:40 -07002104 // If the request doesn't read SDR body, construct only header and key
2105 // part to avoid additional DBus transaction.
2106 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2107 {
2108 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2109 }
Willy Tu4eca2512022-06-20 21:14:51 -07002110 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2111 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002112 {
2113 return GENERAL_ERROR;
2114 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002115
selvaganapathi7b2e5502023-02-14 07:10:47 +05302116 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2117 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002118
2119 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002120 }
Willy Tu61992ad2021-03-29 15:33:20 -07002121
Scron Chang2703b022021-07-06 15:47:45 +08002122#ifdef FEATURE_HYBRID_SENSORS
2123 if (auto sensor = findStaticSensor(path);
2124 sensor != ipmi::sensor::sensors.end() &&
2125 getSensorEventTypeFromPath(path) !=
2126 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2127 {
Willy Tu11d68892022-01-20 10:37:34 -08002128 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002129
2130 // If the request doesn't read SDR body, construct only header and key
2131 // part to avoid additional DBus transaction.
2132 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2133 {
2134 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2135 }
2136 else
2137 {
2138 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2139 }
2140
selvaganapathi7b2e5502023-02-14 07:10:47 +05302141 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2142 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002143
2144 return 0;
2145 }
2146#endif
2147
Willy Tu61992ad2021-03-29 15:33:20 -07002148 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002149 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2150 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002151 {
Willy Tu11d68892022-01-20 10:37:34 -08002152 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002153
Hao Jiange39d4d82021-04-16 17:02:40 -07002154 // If the request doesn't read SDR body, construct only header and key
2155 // part to avoid additional DBus transaction.
2156 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2157 {
2158 constructEventSdrHeaderKey(sensorNum, recordID, record);
2159 }
Willy Tu4eca2512022-06-20 21:14:51 -07002160 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2161 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002162 {
2163 return GENERAL_ERROR;
2164 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302165 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2166 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002167 }
2168
Willy Tude54f482021-01-26 15:59:09 -08002169 return 0;
2170}
2171
2172/** @brief implements the get SDR Info command
2173 * @param count - Operation
2174 *
2175 * @returns IPMI completion code plus response data
2176 * - sdrCount - sensor/SDR count
2177 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2178 */
2179static ipmi::RspType<uint8_t, // respcount
2180 uint8_t, // dynamic population flags
2181 uint32_t // last time a sensor was added
2182 >
2183 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2184 std::optional<uint8_t> count)
2185{
2186 auto& sensorTree = getSensorTree();
2187 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002188 uint16_t recordID = 0;
2189 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002190 // Sensors are dynamically allocated, and there is at least one LUN
2191 uint8_t lunsAndDynamicPopulation = 0x80;
2192 constexpr uint8_t getSdrCount = 0x01;
2193 constexpr uint8_t getSensorCount = 0x00;
2194
2195 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2196 {
2197 return ipmi::responseResponseError();
2198 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002199 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002200 if (count.value_or(0) == getSdrCount)
2201 {
Willy Tu4eca2512022-06-20 21:14:51 -07002202 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2203
Harvey Wu75893062023-03-22 17:17:31 +08002204 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002205 {
2206 recordID += maxSensorsPerLUN;
2207 }
Harvey Wu75893062023-03-22 17:17:31 +08002208 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002209 {
2210 recordID += maxSensorsPerLUN * 2;
2211 }
2212
Harvey Wu75893062023-03-22 17:17:31 +08002213 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2214 // assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002215 while (!getSensorDataRecord(
2216 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2217 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002218 {
2219 get_sdr::SensorDataRecordHeader* hdr =
2220 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002221 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302222 if (!hdr)
2223 {
2224 continue;
2225 }
2226
2227 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002228 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002229 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002230 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002231 record.data());
2232 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002233 {
2234 sdrCount++;
2235 }
2236 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302237 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2238 {
2239 get_sdr::SensorDataCompactRecord* recordData =
2240 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2241 record.data());
2242 if (ctx->lun == recordData->key.owner_lun)
2243 {
2244 sdrCount++;
2245 }
2246 }
Harvey Wua3476272023-03-22 10:09:38 +08002247 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2248 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302249 {
2250 sdrCount++;
2251 }
Harvey Wua3476272023-03-22 10:09:38 +08002252
2253 // Because response count data is 1 byte, so sdrCount need to avoid
2254 // overflow.
2255 if (sdrCount == maxSensorsPerLUN)
2256 {
2257 break;
2258 }
Willy Tude54f482021-01-26 15:59:09 -08002259 }
2260 }
2261 else if (count.value_or(0) == getSensorCount)
2262 {
2263 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002264 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002265 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002266 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2267 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002268 }
Harvey Wu75893062023-03-22 17:17:31 +08002269 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002270 {
2271 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2272 ? maxSensorsPerLUN
2273 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2274 }
Harvey Wu75893062023-03-22 17:17:31 +08002275 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002276 {
2277 if (numSensors <= maxIPMISensors)
2278 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002279 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2280 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002281 }
2282 else
2283 {
2284 // error
2285 throw std::out_of_range(
2286 "Maximum number of IPMI sensors exceeded.");
2287 }
2288 }
2289 }
2290 else
2291 {
2292 return ipmi::responseInvalidFieldRequest();
2293 }
2294
2295 // Get Sensor count. This returns the number of sensors
2296 if (numSensors > 0)
2297 {
2298 lunsAndDynamicPopulation |= 1;
2299 }
2300 if (numSensors > maxSensorsPerLUN)
2301 {
2302 lunsAndDynamicPopulation |= 2;
2303 }
2304 if (numSensors >= (maxSensorsPerLUN * 2))
2305 {
2306 lunsAndDynamicPopulation |= 8;
2307 }
2308 if (numSensors > maxIPMISensors)
2309 {
2310 // error
2311 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2312 }
2313
2314 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2315 sdrLastAdd);
2316}
2317
2318/* end sensor commands */
2319
2320/* storage commands */
2321
2322ipmi::RspType<uint8_t, // sdr version
2323 uint16_t, // record count
2324 uint16_t, // free space
2325 uint32_t, // most recent addition
2326 uint32_t, // most recent erase
2327 uint8_t // operationSupport
2328 >
2329 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2330{
2331 auto& sensorTree = getSensorTree();
2332 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002333 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002334 {
2335 return ipmi::responseResponseError();
2336 }
2337
2338 size_t fruCount = 0;
2339 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2340 if (ret != ipmi::ccSuccess)
2341 {
2342 return ipmi::response(ret);
2343 }
2344
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002345 uint16_t recordCount = getNumberOfSensors() + fruCount +
2346 ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002347
2348 uint8_t operationSupport = static_cast<uint8_t>(
2349 SdrRepositoryInfoOps::overflow); // write not supported
2350
2351 operationSupport |=
2352 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2353 operationSupport |= static_cast<uint8_t>(
2354 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2355 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2356 unspecifiedFreeSpace, sdrLastAdd,
2357 sdrLastRemove, operationSupport);
2358}
2359
2360/** @brief implements the get SDR allocation info command
2361 *
2362 * @returns IPMI completion code plus response data
2363 * - allocUnits - Number of possible allocation units
2364 * - allocUnitSize - Allocation unit size in bytes.
2365 * - allocUnitFree - Number of free allocation units
2366 * - allocUnitLargestFree - Largest free block in allocation units
2367 * - maxRecordSize - Maximum record size in allocation units.
2368 */
2369ipmi::RspType<uint16_t, // allocUnits
2370 uint16_t, // allocUnitSize
2371 uint16_t, // allocUnitFree
2372 uint16_t, // allocUnitLargestFree
2373 uint8_t // maxRecordSize
2374 >
2375 ipmiStorageGetSDRAllocationInfo()
2376{
2377 // 0000h unspecified number of alloc units
2378 constexpr uint16_t allocUnits = 0;
2379
2380 constexpr uint16_t allocUnitFree = 0;
2381 constexpr uint16_t allocUnitLargestFree = 0;
2382 // only allow one block at a time
2383 constexpr uint8_t maxRecordSize = 1;
2384
2385 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2386 allocUnitLargestFree, maxRecordSize);
2387}
2388
2389/** @brief implements the reserve SDR command
2390 * @returns IPMI completion code plus response data
2391 * - sdrReservationID
2392 */
2393ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2394{
2395 sdrReservationID++;
2396 if (sdrReservationID == 0)
2397 {
2398 sdrReservationID++;
2399 }
2400
2401 return ipmi::responseSuccess(sdrReservationID);
2402}
2403
2404ipmi::RspType<uint16_t, // next record ID
2405 std::vector<uint8_t> // payload
2406 >
2407 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2408 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2409{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002410 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002411 // reservation required for partial reads with non zero offset into
2412 // record
2413 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2414 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002415 phosphor::logging::log<phosphor::logging::level::ERR>(
2416 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002417 return ipmi::responseInvalidReservationId();
2418 }
Willy Tude54f482021-01-26 15:59:09 -08002419 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2420 if (ret != ipmi::ccSuccess)
2421 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002422 phosphor::logging::log<phosphor::logging::level::ERR>(
2423 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002424 return ipmi::response(ret);
2425 }
2426
Harvey Wu05d17c02021-09-15 08:46:59 +08002427 const auto& entityRecords =
2428 ipmi::sensor::EntityInfoMapContainer::getContainer()
2429 ->getIpmiEntityRecords();
2430 int entityCount = entityRecords.size();
2431
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002432 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002433 size_t lastRecord = getNumberOfSensors() + fruCount +
2434 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002435 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2436
2437 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002438 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002439 phosphor::logging::log<phosphor::logging::level::ERR>(
2440 "ipmiStorageGetSDR: getSensorSubtree error");
2441 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002442 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002443
Willy Tu4eca2512022-06-20 21:14:51 -07002444 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2445
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002446 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002447 if (getSensorDataRecord(
2448 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2449 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002450 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002451 phosphor::logging::log<phosphor::logging::level::ERR>(
2452 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002453 return ipmi::responseInvalidFieldRequest();
2454 }
Willy Tude54f482021-01-26 15:59:09 -08002455 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002456 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002457 if (!hdr)
2458 {
2459 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002460 "ipmiStorageGetSDR: record header is null");
2461 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002462 }
2463
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002464 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2465 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002466 if (offset >= sdrLength)
2467 {
2468 phosphor::logging::log<phosphor::logging::level::ERR>(
2469 "ipmiStorageGetSDR: offset is outside the record");
2470 return ipmi::responseParmOutOfRange();
2471 }
Willy Tude54f482021-01-26 15:59:09 -08002472 if (sdrLength < (offset + bytesToRead))
2473 {
2474 bytesToRead = sdrLength - offset;
2475 }
2476
2477 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2478 if (!respStart)
2479 {
2480 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002481 "ipmiStorageGetSDR: record is null");
2482 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002483 }
2484
2485 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002486
Willy Tude54f482021-01-26 15:59:09 -08002487 return ipmi::responseSuccess(nextRecordId, recordData);
2488}
adarshgrami042e9db2022-09-15 10:34:34 +05302489namespace dcmi
2490{
2491
Thang Tranb1416ef2023-08-02 13:57:09 +07002492std::tuple<uint8_t, // Total of instance sensors
2493 std::vector<sensorInfo> // The list of sensors
2494 >
2495 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2496 uint8_t entityInstance, uint8_t instanceStart)
2497{
2498 std::vector<sensorInfo> sensorList;
2499 uint8_t totalInstSensor = 0;
2500 auto match = ipmi::dcmi::validEntityId.find(entityId);
2501
2502 if (match == ipmi::dcmi::validEntityId.end())
2503 {
2504 return std::make_tuple(totalInstSensor, sensorList);
2505 }
2506
2507 auto& sensorTree = getSensorTree();
2508 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2509 {
2510 return std::make_tuple(totalInstSensor, sensorList);
2511 }
2512
2513 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2514
2515 for (const auto& sensor : sensorTree)
2516 {
2517 const std::string& sensorObjPath = sensor.first;
2518 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2519
2520 /*
2521 * In the DCMI specification, it only supports the sensor type is 0x01
2522 * (temperature type) for both Get Sensor Info and Get Temperature
2523 * Readings commands.
2524 */
2525 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2526 {
2527 continue;
2528 }
2529
2530 const auto& connection = sensor.second.begin()->first;
2531 DbusInterfaceMap sensorMap;
2532
2533 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2534 sensorMapSdrUpdatePeriod))
2535 {
2536 phosphor::logging::log<phosphor::logging::level::ERR>(
2537 "Failed to update sensor map for threshold sensor",
2538 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2539 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2540 continue;
2541 }
2542
2543 uint8_t entityIdValue = 0;
2544 uint8_t entityInstanceValue = 0;
2545
2546 /*
2547 * Get the Entity ID, Entity Instance information which are configured
2548 * in the Entity-Manger.
2549 */
2550 updateIpmiFromAssociation(
2551 sensorObjPath,
2552 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2553 sensorMap, entityIdValue, entityInstanceValue);
2554
2555 if (entityIdValue == match->first || entityIdValue == match->second)
2556 {
2557 totalInstSensor++;
2558
2559 /*
2560 * When Entity Instance parameter is not 0, we only get the first
2561 * sensor whose Entity Instance number is equal input Entity
2562 * Instance parameter.
2563 */
2564 if (entityInstance)
2565 {
2566 if (!sensorList.empty())
2567 {
2568 continue;
2569 }
2570
2571 if (entityInstanceValue == entityInstance)
2572 {
2573 auto recordId = getSensorNumberFromPath(sensorObjPath);
2574 if (recordId != invalidSensorNumber)
2575 {
2576 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2577 recordId, entityIdValue,
2578 entityInstanceValue);
2579 }
2580 }
2581 }
2582 else if (entityInstanceValue >= instanceStart)
2583 {
2584 auto recordId = getSensorNumberFromPath(sensorObjPath);
2585 if (recordId != invalidSensorNumber)
2586 {
2587 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2588 recordId, entityIdValue,
2589 entityInstanceValue);
2590 }
2591 }
2592 }
2593 }
2594
2595 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2596 return first.entityInstance <= second.entityInstance;
2597 };
2598
2599 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2600
2601 return std::make_tuple(totalInstSensor, sensorList);
2602}
2603
Thang Tran3dad8262023-08-17 15:20:56 +07002604std::tuple<bool, // Reading result
2605 uint7_t, // Temp value
2606 bool> // Sign bit
2607 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2608{
2609 std::string service{};
2610 boost::system::error_code ec =
2611 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2612 if (ec.value())
2613 {
2614 return std::make_tuple(false, 0, false);
2615 }
2616
2617 ipmi::PropertyMap properties{};
2618 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2619 sensor::sensorInterface, properties);
2620 if (ec.value())
2621 {
2622 return std::make_tuple(false, 0, false);
2623 }
2624
2625 auto scaleIt = properties.find("Scale");
2626 double scaleVal = 0.0;
2627 if (scaleIt != properties.end())
2628 {
2629 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2630 }
2631
2632 auto tempValIt = properties.find("Value");
2633 double tempVal = 0.0;
2634 if (tempValIt == properties.end())
2635 {
2636 return std::make_tuple(false, 0, false);
2637 }
2638
2639 const double maxTemp = 127;
2640 double absTempVal = 0.0;
2641 bool signBit = false;
2642
2643 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2644 tempVal = std::pow(10, scaleVal) * tempVal;
2645 absTempVal = std::abs(tempVal);
2646 absTempVal = std::min(absTempVal, maxTemp);
2647 signBit = (tempVal < 0) ? true : false;
2648
2649 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2650}
2651
adarshgrami042e9db2022-09-15 10:34:34 +05302652ipmi::RspType<uint8_t, // No of instances for requested id
2653 uint8_t, // No of record ids in the response
2654 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2655 // IDs
2656 >
2657 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002658 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302659{
2660 auto match = ipmi::dcmi::validEntityId.find(entityId);
2661 if (match == ipmi::dcmi::validEntityId.end())
2662 {
2663 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2664
2665 return ipmi::responseInvalidFieldRequest();
2666 }
2667
2668 if (sensorType != ipmi::dcmi::temperatureSensorType)
2669 {
2670 log<level::ERR>("Invalid sensor type",
2671 entry("SENSOR_TYPE=%d", sensorType));
2672
2673 return ipmi::responseInvalidFieldRequest();
2674 }
adarshgrami042e9db2022-09-15 10:34:34 +05302675
2676 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002677 const auto& [totalSensorInst, sensorList] =
2678 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302679
Thang Tranb1416ef2023-08-02 13:57:09 +07002680 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302681 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002682 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2683 }
adarshgrami042e9db2022-09-15 10:34:34 +05302684
Thang Tranb1416ef2023-08-02 13:57:09 +07002685 /*
2686 * As DCMI specification, the maximum number of Record Ids of response data
2687 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2688 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2689 * in response data.
2690 */
2691 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2692
2693 for (const auto& sensor : sensorList)
2694 {
2695 sensorRec.emplace_back(sensor.recordId);
2696 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302697 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002698 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302699 }
2700 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002701
2702 return ipmi::responseSuccess(
2703 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302704}
Thang Tran3dad8262023-08-17 15:20:56 +07002705
2706ipmi::RspType<uint8_t, // No of instances for requested id
2707 uint8_t, // No of record ids in the response
2708 std::vector< // Temperature Data
2709 std::tuple<uint7_t, // Temperature value
2710 bool, // Sign bit
2711 uint8_t // Entity Instance of sensor
2712 >>>
2713 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2714 uint8_t entityId, uint8_t entityInstance,
2715 uint8_t instanceStart)
2716{
2717 auto match = ipmi::dcmi::validEntityId.find(entityId);
2718 if (match == ipmi::dcmi::validEntityId.end())
2719 {
2720 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2721
2722 return ipmi::responseInvalidFieldRequest();
2723 }
2724
2725 if (sensorType != ipmi::dcmi::temperatureSensorType)
2726 {
2727 log<level::ERR>("Invalid sensor type",
2728 entry("SENSOR_TYPE=%d", sensorType));
2729
2730 return ipmi::responseInvalidFieldRequest();
2731 }
2732
2733 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2734 const auto& [totalSensorInst, sensorList] =
2735 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2736
2737 if (sensorList.empty())
2738 {
2739 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2740 }
2741
2742 /*
2743 * As DCMI specification, the maximum number of Record Ids of response data
2744 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2745 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2746 * in response data.
2747 */
2748 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2749
2750 for (const auto& sensor : sensorList)
2751 {
2752 const auto& [readResult, tempVal,
2753 signBit] = readTemp(ctx, sensor.objectPath);
2754
2755 if (readResult)
2756 {
2757 tempReadingVal.emplace_back(
2758 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2759
2760 if (tempReadingVal.size() >= numOfRec)
2761 {
2762 break;
2763 }
2764 }
2765 }
2766
2767 return ipmi::responseSuccess(totalSensorInst,
2768 static_cast<uint8_t>(tempReadingVal.size()),
2769 tempReadingVal);
2770}
2771
adarshgrami042e9db2022-09-15 10:34:34 +05302772} // namespace dcmi
2773
Willy Tude54f482021-01-26 15:59:09 -08002774/* end storage commands */
2775
2776void registerSensorFunctions()
2777{
2778 // <Platform Event>
2779 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2780 ipmi::sensor_event::cmdPlatformEvent,
2781 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2782
Willy Tudbafbce2021-03-29 00:37:05 -07002783 // <Set Sensor Reading and Event Status>
2784 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2785 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2786 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002787
Willy Tude54f482021-01-26 15:59:09 -08002788 // <Get Sensor Reading>
2789 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2790 ipmi::sensor_event::cmdGetSensorReading,
2791 ipmi::Privilege::User, ipmiSenGetSensorReading);
2792
2793 // <Get Sensor Threshold>
2794 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2795 ipmi::sensor_event::cmdGetSensorThreshold,
2796 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2797
2798 // <Set Sensor Threshold>
2799 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2800 ipmi::sensor_event::cmdSetSensorThreshold,
2801 ipmi::Privilege::Operator,
2802 ipmiSenSetSensorThresholds);
2803
2804 // <Get Sensor Event Enable>
2805 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2806 ipmi::sensor_event::cmdGetSensorEventEnable,
2807 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2808
2809 // <Get Sensor Event Status>
2810 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2811 ipmi::sensor_event::cmdGetSensorEventStatus,
2812 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2813
2814 // register all storage commands for both Sensor and Storage command
2815 // versions
2816
2817 // <Get SDR Repository Info>
2818 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2819 ipmi::storage::cmdGetSdrRepositoryInfo,
2820 ipmi::Privilege::User,
2821 ipmiStorageGetSDRRepositoryInfo);
2822
2823 // <Get Device SDR Info>
2824 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2825 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2826 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2827
2828 // <Get SDR Allocation Info>
2829 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2830 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2831 ipmi::Privilege::User,
2832 ipmiStorageGetSDRAllocationInfo);
2833
2834 // <Reserve SDR Repo>
2835 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2836 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2837 ipmi::Privilege::User, ipmiStorageReserveSDR);
2838
2839 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2840 ipmi::storage::cmdReserveSdrRepository,
2841 ipmi::Privilege::User, ipmiStorageReserveSDR);
2842
2843 // <Get Sdr>
2844 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2845 ipmi::sensor_event::cmdGetDeviceSdr,
2846 ipmi::Privilege::User, ipmiStorageGetSDR);
2847
2848 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2849 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2850 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302851 // <Get DCMI Sensor Info>
2852 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2853 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002854 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302855 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002856 // <Get Temperature Readings>
2857 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2858 ipmi::dcmi::cmdGetTemperatureReadings,
2859 ipmi::Privilege::User,
2860 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002861}
2862} // namespace ipmi