blob: 72cf377bc8dea524010736a289a35447c799c43e [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Alexander Hansenc2c26f92023-07-17 09:38:43 +020017#include "config.h"
18
Willy Tude54f482021-01-26 15:59:09 -080019#include "dbus-sdr/sensorcommands.hpp"
20
21#include "dbus-sdr/sdrutils.hpp"
22#include "dbus-sdr/sensorutils.hpp"
23#include "dbus-sdr/storagecommands.hpp"
24
Willy Tude54f482021-01-26 15:59:09 -080025#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050027#include <ipmid/api.hpp>
Vernon Mauery9cf08382023-04-28 14:00:11 -070028#include <ipmid/entity_map_json.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050029#include <ipmid/types.hpp>
30#include <ipmid/utils.hpp>
31#include <phosphor-logging/log.hpp>
32#include <sdbusplus/bus.hpp>
33#include <user_channel/channel_layer.hpp>
34
35#include <algorithm>
36#include <array>
Willy Tude54f482021-01-26 15:59:09 -080037#include <chrono>
38#include <cmath>
39#include <cstring>
Willy Tu62e3ca82024-01-31 17:28:34 +000040#include <format>
Willy Tude54f482021-01-26 15:59:09 -080041#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080042#include <map>
Willy Tude54f482021-01-26 15:59:09 -080043#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080044#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48
Scron Chang2703b022021-07-06 15:47:45 +080049#ifdef FEATURE_HYBRID_SENSORS
50
51#include "sensordatahandler.hpp"
52namespace ipmi
53{
54namespace sensor
55{
56extern const IdInfoMap sensors;
57} // namespace sensor
58} // namespace ipmi
59#endif
adarshgrami042e9db2022-09-15 10:34:34 +053060namespace ipmi
61{
62namespace dcmi
63{
64// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
65static const std::map<uint8_t, uint8_t> validEntityId{
66 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
67 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
68constexpr uint8_t temperatureSensorType = 0x01;
69constexpr uint8_t maxRecords = 8;
70} // namespace dcmi
71} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080072constexpr std::array<const char*, 7> suffixes = {
73 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
74 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080075namespace ipmi
76{
Hao Jiangd48c9212021-02-03 15:45:06 -080077
78using phosphor::logging::entry;
79using phosphor::logging::level;
80using phosphor::logging::log;
81
Willy Tude54f482021-01-26 15:59:09 -080082static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070083static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080084
Willy Tu38e7a2b2021-03-29 15:09:56 -070085// BMC I2C address is generally at 0x20
86static constexpr uint8_t bmcI2CAddr = 0x20;
87
Willy Tude54f482021-01-26 15:59:09 -080088constexpr size_t maxSDRTotalSize =
89 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
90constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
91
92static uint16_t sdrReservationID;
93static uint32_t sdrLastAdd = noTimestamp;
94static uint32_t sdrLastRemove = noTimestamp;
95static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070096
97// The IPMI spec defines four Logical Units (LUN), each capable of supporting
98// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
99// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +0800100// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700101// location.
102static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
103
Harvey Wu75893062023-03-22 17:17:31 +0800104static constexpr uint8_t lun0 = 0x0;
105static constexpr uint8_t lun1 = 0x1;
106static constexpr uint8_t lun3 = 0x3;
107
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700108static constexpr size_t lun0MaxSensorNum = 0xfe;
109static constexpr size_t lun1MaxSensorNum = 0x1fe;
110static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800111static constexpr int GENERAL_ERROR = -1;
112
Willy Tude54f482021-01-26 15:59:09 -0800113static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
114
115// Specify the comparison required to sort and find char* map objects
116struct CmpStr
117{
118 bool operator()(const char* a, const char* b) const
119 {
120 return std::strcmp(a, b) < 0;
121 }
122};
123const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
124 sensorUnits{{{"temperature", SensorUnits::degreesC},
125 {"voltage", SensorUnits::volts},
126 {"current", SensorUnits::amps},
127 {"fan_tach", SensorUnits::rpm},
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
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700156ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
157 std::string& connection, std::string& path,
158 std::vector<std::string>* interfaces)
159{
160 auto& sensorTree = getSensorTree();
161 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
162 {
163 return IPMI_CC_RESPONSE_ERROR;
164 }
165
166 if (ctx == nullptr)
167 {
168 return IPMI_CC_RESPONSE_ERROR;
169 }
170
171 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
172 if (path.empty())
173 {
174 return IPMI_CC_INVALID_FIELD_REQUEST;
175 }
176
177 for (const auto& sensor : sensorTree)
178 {
179 if (path == sensor.first)
180 {
181 connection = sensor.second.begin()->first;
182 if (interfaces)
183 *interfaces = sensor.second.begin()->second;
184 break;
185 }
186 }
187
188 return 0;
189}
190
191SensorSubTree& getSensorTree()
192{
193 static SensorSubTree sensorTree;
194 return sensorTree;
195}
196
Willy Tude54f482021-01-26 15:59:09 -0800197// this keeps track of deassertions for sensor event status command. A
198// deasertion can only happen if an assertion was seen first.
199static boost::container::flat_map<
200 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
201 thresholdDeassertMap;
202
Patrick Williams5d82f472022-07-22 19:26:53 -0500203static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800204 *getSdBus(),
205 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
206 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500207 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500208 boost::container::flat_map<std::string, std::variant<bool, double>> values;
209 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800210
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500211 auto findAssert = std::find_if(values.begin(), values.end(),
212 [](const auto& pair) {
213 return pair.first.find("Alarm") != std::string::npos;
214 });
215 if (findAssert != values.end())
216 {
217 auto ptr = std::get_if<bool>(&(findAssert->second));
218 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800219 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500220 phosphor::logging::log<phosphor::logging::level::ERR>(
221 "thresholdChanged: Assert non bool");
222 return;
223 }
224 if (*ptr)
225 {
226 phosphor::logging::log<phosphor::logging::level::INFO>(
227 "thresholdChanged: Assert",
228 phosphor::logging::entry("SENSOR=%s", m.get_path()));
229 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
230 }
231 else
232 {
233 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
234 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800235 {
236 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500237 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800238 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500239 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800240 }
241 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500242 }
Patrick Williams369824e2023-10-20 11:18:23 -0500243});
Willy Tude54f482021-01-26 15:59:09 -0800244
Hao Jiangd2afd052020-12-10 15:09:32 -0800245namespace sensor
246{
247static constexpr const char* vrInterface =
248 "xyz.openbmc_project.Control.VoltageRegulatorMode";
249static constexpr const char* sensorInterface =
250 "xyz.openbmc_project.Sensor.Value";
251} // namespace sensor
252
Willy Tude54f482021-01-26 15:59:09 -0800253static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
254 double& min)
255{
256 max = 127;
257 min = -128;
258
Hao Jiangd2afd052020-12-10 15:09:32 -0800259 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800260 auto critical =
261 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
262 auto warning =
263 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
264
265 if (sensorObject != sensorMap.end())
266 {
267 auto maxMap = sensorObject->second.find("MaxValue");
268 auto minMap = sensorObject->second.find("MinValue");
269
270 if (maxMap != sensorObject->second.end())
271 {
272 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
273 }
274 if (minMap != sensorObject->second.end())
275 {
276 min = std::visit(VariantToDoubleVisitor(), minMap->second);
277 }
278 }
279 if (critical != sensorMap.end())
280 {
281 auto lower = critical->second.find("CriticalLow");
282 auto upper = critical->second.find("CriticalHigh");
283 if (lower != critical->second.end())
284 {
285 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300286 if (std::isfinite(value))
287 {
288 min = std::min(value, min);
289 }
Willy Tude54f482021-01-26 15:59:09 -0800290 }
291 if (upper != critical->second.end())
292 {
293 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300294 if (std::isfinite(value))
295 {
296 max = std::max(value, max);
297 }
Willy Tude54f482021-01-26 15:59:09 -0800298 }
299 }
300 if (warning != sensorMap.end())
301 {
Willy Tude54f482021-01-26 15:59:09 -0800302 auto lower = warning->second.find("WarningLow");
303 auto upper = warning->second.find("WarningHigh");
304 if (lower != warning->second.end())
305 {
306 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300307 if (std::isfinite(value))
308 {
309 min = std::min(value, min);
310 }
Willy Tude54f482021-01-26 15:59:09 -0800311 }
312 if (upper != warning->second.end())
313 {
314 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300315 if (std::isfinite(value))
316 {
317 max = std::max(value, max);
318 }
Willy Tude54f482021-01-26 15:59:09 -0800319 }
320 }
321}
322
323static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700324 std::string sensorPath, DbusInterfaceMap& sensorMap,
325 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800326{
Scron Chang2703b022021-07-06 15:47:45 +0800327#ifdef FEATURE_HYBRID_SENSORS
328 if (auto sensor = findStaticSensor(sensorPath);
329 sensor != ipmi::sensor::sensors.end() &&
330 getSensorEventTypeFromPath(sensorPath) !=
331 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
332 {
333 // If the incoming sensor is a discrete sensor, it might fail in
334 // getManagedObjects(), return true, and use its own getFunc to get
335 // value.
336 return true;
337 }
338#endif
339
Willy Tude54f482021-01-26 15:59:09 -0800340 static boost::container::flat_map<
341 std::string, std::chrono::time_point<std::chrono::steady_clock>>
342 updateTimeMap;
343
344 auto updateFind = updateTimeMap.find(sensorConnection);
345 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
346 if (updateFind != updateTimeMap.end())
347 {
348 lastUpdate = updateFind->second;
349 }
350
351 auto now = std::chrono::steady_clock::now();
352
353 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700354 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800355 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800356 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800357
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800358 // Object managers for different kinds of OpenBMC DBus interfaces.
359 // Documented in the phosphor-dbus-interfaces repository.
360 const char* paths[] = {
361 "/xyz/openbmc_project/sensors",
362 "/xyz/openbmc_project/vr",
363 };
364 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
365 ObjectValueTree allManagedObjects;
366
367 for (size_t i = 0; i < num_paths; i++)
368 {
369 ObjectValueTree managedObjects;
370 boost::system::error_code ec = getManagedObjects(
371 ctx, sensorConnection.c_str(), paths[i], managedObjects);
372 if (ec)
373 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800374 continue;
375 }
376 allManagedObjects.merge(managedObjects);
377 found = true;
378 }
379
380 if (!found)
381 {
Tom Tung6615d472023-05-31 18:48:12 +0800382 phosphor::logging::log<phosphor::logging::level::ERR>(
383 "GetMangagedObjects for getSensorMap failed",
384 phosphor::logging::entry("SERVICE=%s",
385 sensorConnection.c_str()));
386
Willy Tude54f482021-01-26 15:59:09 -0800387 return false;
388 }
389
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800390 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700391 // Update time after finish building the map which allow the
392 // data to be cached for updatePeriod plus the build time.
393 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800394 }
395 auto connection = SensorCache.find(sensorConnection);
396 if (connection == SensorCache.end())
397 {
398 return false;
399 }
400 auto path = connection->second.find(sensorPath);
401 if (path == connection->second.end())
402 {
403 return false;
404 }
405 sensorMap = path->second;
406
407 return true;
408}
409
Hao Jiangd2afd052020-12-10 15:09:32 -0800410namespace sensor
411{
Hao Jiangd48c9212021-02-03 15:45:06 -0800412// Read VR profiles from sensor(daemon) interface
413static std::optional<std::vector<std::string>>
414 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800415{
416 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800417 auto supportedProperty = object.find("Supported");
418 if (supportedProperty == object.end() ||
419 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800420 {
421 phosphor::logging::log<phosphor::logging::level::ERR>(
422 "Missing the required Supported and Selected properties");
423 return std::nullopt;
424 }
425
426 const auto profilesPtr =
427 std::get_if<std::vector<std::string>>(&supportedProperty->second);
428
429 if (profilesPtr == nullptr)
430 {
431 phosphor::logging::log<phosphor::logging::level::ERR>(
432 "property is not array of string");
433 return std::nullopt;
434 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800435 return *profilesPtr;
436}
437
438// Calculate VR Mode from input IPMI discrete event bytes
439static std::optional<std::string>
440 calculateVRMode(uint15_t assertOffset,
441 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
442{
443 // get VR mode profiles from Supported Interface
444 auto profiles = getSupportedVrProfiles(VRObject);
445 if (!profiles)
446 {
447 return std::nullopt;
448 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800449
450 // interpret IPMI cmd bits into profiles' index
451 long unsigned int index = 0;
452 // only one bit should be set and the highest bit should not be used.
453 if (assertOffset == 0 || assertOffset == (1u << 15) ||
454 (assertOffset & (assertOffset - 1)))
455 {
456 phosphor::logging::log<phosphor::logging::level::ERR>(
457 "IPMI cmd format incorrect",
458
459 phosphor::logging::entry("BYTES=%#02x",
460 static_cast<uint16_t>(assertOffset)));
461 return std::nullopt;
462 }
463
464 while (assertOffset != 1)
465 {
466 assertOffset >>= 1;
467 index++;
468 }
469
Hao Jiangd48c9212021-02-03 15:45:06 -0800470 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800471 {
472 phosphor::logging::log<phosphor::logging::level::ERR>(
473 "profile index out of boundary");
474 return std::nullopt;
475 }
476
Hao Jiangd48c9212021-02-03 15:45:06 -0800477 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800478}
479
480// Calculate sensor value from IPMI reading byte
481static std::optional<double>
482 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
483 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
484{
485 if (valueObject.find("Value") == valueObject.end())
486 {
487 phosphor::logging::log<phosphor::logging::level::ERR>(
488 "Missing the required Value property");
489 return std::nullopt;
490 }
491
492 double max = 0;
493 double min = 0;
494 getSensorMaxMin(sensorMap, max, min);
495
496 int16_t mValue = 0;
497 int16_t bValue = 0;
498 int8_t rExp = 0;
499 int8_t bExp = 0;
500 bool bSigned = false;
501
502 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
503 {
504 return std::nullopt;
505 }
506
507 double value = bSigned ? ((int8_t)reading) : reading;
508
509 value *= ((double)mValue);
510 value += ((double)bValue) * std::pow(10.0, bExp);
511 value *= std::pow(10.0, rExp);
512
513 return value;
514}
515
Willy Tu38e7a2b2021-03-29 15:09:56 -0700516// Extract file name from sensor path as the sensors SDR ID. Simplify the name
517// if it is too long.
518std::string parseSdrIdFromPath(const std::string& path)
519{
520 std::string name;
521 size_t nameStart = path.rfind("/");
522 if (nameStart != std::string::npos)
523 {
524 name = path.substr(nameStart + 1, std::string::npos - nameStart);
525 }
526
Willy Tu38e7a2b2021-03-29 15:09:56 -0700527 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
528 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200529#ifdef SHORTNAME_REMOVE_SUFFIX
JeffLind950f412021-10-20 18:49:34 +0800530 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700531 {
JeffLind950f412021-10-20 18:49:34 +0800532 if (boost::ends_with(name, suffix))
533 {
534 boost::replace_all(name, suffix, "");
535 break;
536 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700537 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200538#endif
539#ifdef SHORTNAME_REPLACE_WORDS
540 constexpr std::array<std::pair<const char*, const char*>, 2>
541 replaceWords = {std::make_pair("Output", "Out"),
542 std::make_pair("Input", "In")};
543 for (const auto& [find, replace] : replaceWords)
Duke Du97014f52021-12-16 17:21:01 +0800544 {
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200545 boost::replace_all(name, find, replace);
Duke Du97014f52021-12-16 17:21:01 +0800546 }
Alexander Hansenc2c26f92023-07-17 09:38:43 +0200547#endif
548
549 // as a backup and if nothing else is configured
550 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
Willy Tu38e7a2b2021-03-29 15:09:56 -0700551 }
552 return name;
553}
554
Hao Jiangd48c9212021-02-03 15:45:06 -0800555bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
556 const std::string& path,
557 const ipmi::DbusInterfaceMap::mapped_type& object,
558 std::bitset<16>& assertions)
559{
560 auto profiles = sensor::getSupportedVrProfiles(object);
561 if (!profiles)
562 {
563 return false;
564 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700565 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800566
567 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700568 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800569 if (ec)
570 {
571 log<level::ERR>("Failed to get property",
572 entry("PROPERTY=%s", "Selected"),
573 entry("PATH=%s", path.c_str()),
574 entry("INTERFACE=%s", sensor::sensorInterface),
575 entry("WHAT=%s", ec.message().c_str()));
576 return false;
577 }
578
Willy Tu8366f0b2022-04-29 05:00:17 -0700579 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800580 if (itr == profiles->end())
581 {
582 using namespace phosphor::logging;
583 log<level::ERR>("VR mode doesn't match any of its profiles",
584 entry("PATH=%s", path.c_str()));
585 return false;
586 }
587 std::size_t index =
588 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
589
Willy Tubef102a2022-06-09 15:36:09 -0700590 // map index to response event assertion bit.
591 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800592 {
Willy Tubef102a2022-06-09 15:36:09 -0700593 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800594 }
595 else
596 {
597 log<level::ERR>("VR profile index reaches max assertion bit",
598 entry("PATH=%s", path.c_str()),
599 entry("INDEX=%uz", index));
600 return false;
601 }
602 if constexpr (debug)
603 {
604 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700605 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800606 }
607 return true;
608}
Johnathan Mantey777cfaf2024-06-13 10:45:47 -0700609
610/*
611 * Handle every Sensor Data Record besides Type 01
612 *
613 * The D-Bus sensors work well for generating Type 01 SDRs.
614 * After the Type 01 sensors are processed the remaining sensor types require
615 * special handling. Each BMC vendor is going to have their own requirements for
616 * insertion of non-Type 01 records.
617 * Manage non-Type 01 records:
618 *
619 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
620 * Populate it with the two weakly linked functions below, without adding the
621 * 'weak' attribute definition prior to the function definition.
622 * getOtherSensorsCount(...)
623 * getOtherSensorsDataRecord(...)
624 * Example contents are provided in the weak definitions below
625 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
626 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
627 * The contents of the sensorcommands_oem.cpp file will then override the code
628 * provided below.
629 */
630
631size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
632size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
633{
634 size_t fruCount = 0;
635
636 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
637 if (ret != ipmi::ccSuccess)
638 {
639 phosphor::logging::log<phosphor::logging::level::ERR>(
640 "getOtherSensorsCount: getFruSdrCount error");
641 return std::numeric_limits<size_t>::max();
642 }
643
644 const auto& entityRecords =
645 ipmi::sensor::EntityInfoMapContainer::getContainer()
646 ->getIpmiEntityRecords();
647 size_t entityCount = entityRecords.size();
648
649 return fruCount + ipmi::storage::type12Count + entityCount;
650}
651
652int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
653 std::vector<uint8_t>& recordData)
654 __attribute__((weak));
655int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
656 std::vector<uint8_t>& recordData)
657{
658 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
659 if (otherCount == std::numeric_limits<size_t>::max())
660 {
661 return GENERAL_ERROR;
662 }
663 const auto& entityRecords =
664 ipmi::sensor::EntityInfoMapContainer::getContainer()
665 ->getIpmiEntityRecords();
666
667 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
668 size_t entityCount{entityRecords.size()};
669 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
670
671 if (sdrIndex > otherCount)
672 {
673 return std::numeric_limits<int>::min();
674 }
675 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
676 {
677 // handle type 8 entity map records
678 ipmi::sensor::EntityInfoMap::const_iterator entity =
679 entityRecords.find(static_cast<uint8_t>(
680 sdrIndex - fruCount - ipmi::storage::type12Count));
681
682 if (entity == entityRecords.end())
683 {
684 return GENERAL_ERROR;
685 }
686 recordData = ipmi::storage::getType8SDRs(entity, recordID);
687 }
688 else if (sdrIndex >= fruCount)
689 {
690 // handle type 12 hardcoded records
691 size_t type12Index = sdrIndex - fruCount;
692 if (type12Index >= ipmi::storage::type12Count)
693 {
694 phosphor::logging::log<phosphor::logging::level::ERR>(
695 "getSensorDataRecord: type12Index error");
696 return GENERAL_ERROR;
697 }
698 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
699 }
700 else
701 {
702 // handle fru records
703 get_sdr::SensorDataFruRecord data;
704 if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
705 ret != IPMI_CC_OK)
706 {
707 return GENERAL_ERROR;
708 }
709 data.header.record_id_msb = recordID >> 8;
710 data.header.record_id_lsb = recordID & 0xFF;
711 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
712 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
713 }
714
715 return 0;
716}
717
Hao Jiangd2afd052020-12-10 15:09:32 -0800718} // namespace sensor
719
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000720ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
721 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800722{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000723 constexpr const uint8_t validEnvmRev = 0x04;
724 constexpr const uint8_t lastSensorType = 0x2C;
725 constexpr const uint8_t oemReserved = 0xC0;
726
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700727 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000728 uint8_t evmRev = 0;
729 uint8_t sensorType = 0;
730 uint8_t sensorNum = 0;
731 uint8_t eventType = 0;
732 uint8_t eventData1 = 0;
733 std::optional<uint8_t> eventData2 = 0;
734 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700735 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000736 ipmi::ChannelInfo chInfo;
737
738 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
739 {
740 phosphor::logging::log<phosphor::logging::level::ERR>(
741 "Failed to get Channel Info",
742 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
743 return ipmi::responseUnspecifiedError();
744 }
745
746 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
747 ipmi::EChannelMediumType::systemInterface)
748 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700749 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000750 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700751 // Refer to IPMI Spec Table 32: SEL Event Records
752 generatorID = (ctx->channel << 12) // Channel
753 | (0x0 << 10) // Reserved
754 | (0x0 << 8) // 0x0 for sys-soft ID
755 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000756 }
757 else
758 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000759 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
760 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700761 // Refer to IPMI Spec Table 32: SEL Event Records
762 generatorID = (ctx->channel << 12) // Channel
763 | (0x0 << 10) // Reserved
764 | ((ctx->lun & 0x3) << 8) // Lun
765 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000766 }
767
768 if (!p.fullyUnpacked())
769 {
770 return ipmi::responseReqDataLenInvalid();
771 }
772
773 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
774 if (evmRev != validEnvmRev)
775 {
776 return ipmi::responseInvalidFieldRequest();
777 }
778 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
779 {
780 return ipmi::responseInvalidFieldRequest();
781 }
782
Willy Tude54f482021-01-26 15:59:09 -0800783 return ipmi::responseSuccess();
784}
785
Willy Tudbafbce2021-03-29 00:37:05 -0700786ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800787 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700788 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800789 bool, uint15_t, bool, uint8_t, uint8_t,
790 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700791{
792 std::string connection;
793 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700794 std::vector<std::string> interfaces;
795
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500796 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
797 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700798 if (status)
799 {
800 return ipmi::response(status);
801 }
802
Hao Jiangd2afd052020-12-10 15:09:32 -0800803 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700804 if (std::find(interfaces.begin(), interfaces.end(),
805 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700806 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700807 DbusInterfaceMap sensorMap;
808 if (!getSensorMap(ctx, connection, path, sensorMap))
809 {
810 return ipmi::responseResponseError();
811 }
812 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800813 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700814 {
815 return ipmi::responseResponseError();
816 }
817
Jie Yangf0a89942021-07-29 15:30:25 -0700818 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800819 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
820 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700821 {
822 return ipmi::responseResponseError();
823 }
824
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500825 auto value = sensor::calculateValue(reading, sensorMap,
826 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800827 if (!value)
828 {
829 return ipmi::responseResponseError();
830 }
831
832 if constexpr (debug)
833 {
834 phosphor::logging::log<phosphor::logging::level::INFO>(
835 "IPMI SET_SENSOR",
836 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
837 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
838 phosphor::logging::entry("VALUE=%f", *value));
839 }
840
841 boost::system::error_code ec =
842 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
843 "Value", ipmi::Value(*value));
844
845 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500846 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800847 // callback functions for now (e.g. ipmiSetSensorReading).
848 if (ec)
849 {
850 using namespace phosphor::logging;
851 log<level::ERR>("Failed to set property",
852 entry("PROPERTY=%s", "Value"),
853 entry("PATH=%s", path.c_str()),
854 entry("INTERFACE=%s", sensor::sensorInterface),
855 entry("WHAT=%s", ec.message().c_str()));
856 return ipmi::responseResponseError();
857 }
858 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700859 }
860
Hao Jiange39d4d82021-04-16 17:02:40 -0700861 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
862 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700863 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700864 DbusInterfaceMap sensorMap;
865 if (!getSensorMap(ctx, connection, path, sensorMap))
866 {
867 return ipmi::responseResponseError();
868 }
869 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800870 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700871 {
872 return ipmi::responseResponseError();
873 }
874
Hao Jiangd2afd052020-12-10 15:09:32 -0800875 // VR sensors are treated as a special case and we will not check the
876 // write permission for VR sensors, since they always deemed writable
877 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500878 auto vrMode = sensor::calculateVRMode(assertOffset,
879 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800880 if (!vrMode)
881 {
882 return ipmi::responseResponseError();
883 }
884 boost::system::error_code ec = setDbusProperty(
885 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
886 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500887 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800888 // callback functions for now (e.g. ipmiSetSensorReading).
889 if (ec)
890 {
891 using namespace phosphor::logging;
892 log<level::ERR>("Failed to set property",
893 entry("PROPERTY=%s", "Selected"),
894 entry("PATH=%s", path.c_str()),
895 entry("INTERFACE=%s", sensor::sensorInterface),
896 entry("WHAT=%s", ec.message().c_str()));
897 return ipmi::responseResponseError();
898 }
899 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700900 }
901
Hao Jiangd2afd052020-12-10 15:09:32 -0800902 phosphor::logging::log<phosphor::logging::level::ERR>(
903 "unknown sensor type",
904 phosphor::logging::entry("PATH=%s", path.c_str()));
905 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700906}
907
Willy Tude54f482021-01-26 15:59:09 -0800908ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
909 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
910{
911 std::string connection;
912 std::string path;
913
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000914 if (sensnum == reservedSensorNumber)
915 {
916 return ipmi::responseInvalidFieldRequest();
917 }
918
Willy Tude54f482021-01-26 15:59:09 -0800919 auto status = getSensorConnection(ctx, sensnum, connection, path);
920 if (status)
921 {
922 return ipmi::response(status);
923 }
924
Scron Chang2703b022021-07-06 15:47:45 +0800925#ifdef FEATURE_HYBRID_SENSORS
926 if (auto sensor = findStaticSensor(path);
927 sensor != ipmi::sensor::sensors.end() &&
928 getSensorEventTypeFromPath(path) !=
929 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
930 {
931 if (ipmi::sensor::Mutability::Read !=
932 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
933 {
934 return ipmi::responseIllegalCommand();
935 }
936
937 uint8_t operation;
938 try
939 {
940 ipmi::sensor::GetSensorResponse getResponse =
941 sensor->second.getFunc(sensor->second);
942
943 if (getResponse.readingOrStateUnavailable)
944 {
945 operation |= static_cast<uint8_t>(
946 IPMISensorReadingByte2::readingStateUnavailable);
947 }
948 if (getResponse.scanningEnabled)
949 {
950 operation |= static_cast<uint8_t>(
951 IPMISensorReadingByte2::sensorScanningEnable);
952 }
953 if (getResponse.allEventMessagesEnabled)
954 {
955 operation |= static_cast<uint8_t>(
956 IPMISensorReadingByte2::eventMessagesEnable);
957 }
958 return ipmi::responseSuccess(
959 getResponse.reading, operation,
960 getResponse.thresholdLevelsStates,
961 getResponse.discreteReadingSensorStates);
962 }
963 catch (const std::exception& e)
964 {
965 operation |= static_cast<uint8_t>(
966 IPMISensorReadingByte2::readingStateUnavailable);
967 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
968 }
969 }
970#endif
971
Willy Tude54f482021-01-26 15:59:09 -0800972 DbusInterfaceMap sensorMap;
973 if (!getSensorMap(ctx, connection, path, sensorMap))
974 {
975 return ipmi::responseResponseError();
976 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800977 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800978
979 if (sensorObject == sensorMap.end() ||
980 sensorObject->second.find("Value") == sensorObject->second.end())
981 {
982 return ipmi::responseResponseError();
983 }
984 auto& valueVariant = sensorObject->second["Value"];
985 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
986
987 double max = 0;
988 double min = 0;
989 getSensorMaxMin(sensorMap, max, min);
990
991 int16_t mValue = 0;
992 int16_t bValue = 0;
993 int8_t rExp = 0;
994 int8_t bExp = 0;
995 bool bSigned = false;
996
997 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
998 {
999 return ipmi::responseResponseError();
1000 }
1001
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001002 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
1003 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -08001004 uint8_t operation =
1005 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
1006 operation |=
1007 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
1008 bool notReading = std::isnan(reading);
1009
1010 if (!notReading)
1011 {
1012 auto availableObject =
1013 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
1014 if (availableObject != sensorMap.end())
1015 {
1016 auto findAvailable = availableObject->second.find("Available");
1017 if (findAvailable != availableObject->second.end())
1018 {
1019 bool* available = std::get_if<bool>(&(findAvailable->second));
1020 if (available && !(*available))
1021 {
1022 notReading = true;
1023 }
1024 }
1025 }
1026 }
1027
1028 if (notReading)
1029 {
1030 operation |= static_cast<uint8_t>(
1031 IPMISensorReadingByte2::readingStateUnavailable);
1032 }
1033
Josh Lehana55c9532020-10-28 21:59:06 -07001034 if constexpr (details::enableInstrumentation)
1035 {
1036 int byteValue;
1037 if (bSigned)
1038 {
1039 byteValue = static_cast<int>(static_cast<int8_t>(value));
1040 }
1041 else
1042 {
1043 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1044 }
1045
1046 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001047 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1048 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -07001049 {
1050 // This is the first reading, show the coefficients
1051 double step = (max - min) / 255.0;
1052 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001053 << details::sdrStatsTable.getName((ctx->lun << 8) |
1054 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -07001055 << ": Range min=" << min << " max=" << max
1056 << ", step=" << step
1057 << ", Coefficients mValue=" << static_cast<int>(mValue)
1058 << " rExp=" << static_cast<int>(rExp)
1059 << " bValue=" << static_cast<int>(bValue)
1060 << " bExp=" << static_cast<int>(bExp)
1061 << " bSigned=" << static_cast<int>(bSigned) << "\n";
1062 }
1063 }
1064
Willy Tude54f482021-01-26 15:59:09 -08001065 uint8_t thresholds = 0;
1066
1067 auto warningObject =
1068 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1069 if (warningObject != sensorMap.end())
1070 {
1071 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1072 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1073 if (alarmHigh != warningObject->second.end())
1074 {
1075 if (std::get<bool>(alarmHigh->second))
1076 {
1077 thresholds |= static_cast<uint8_t>(
1078 IPMISensorReadingByte3::upperNonCritical);
1079 }
1080 }
1081 if (alarmLow != warningObject->second.end())
1082 {
1083 if (std::get<bool>(alarmLow->second))
1084 {
1085 thresholds |= static_cast<uint8_t>(
1086 IPMISensorReadingByte3::lowerNonCritical);
1087 }
1088 }
1089 }
1090
1091 auto criticalObject =
1092 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1093 if (criticalObject != sensorMap.end())
1094 {
1095 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1096 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1097 if (alarmHigh != criticalObject->second.end())
1098 {
1099 if (std::get<bool>(alarmHigh->second))
1100 {
1101 thresholds |=
1102 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1103 }
1104 }
1105 if (alarmLow != criticalObject->second.end())
1106 {
1107 if (std::get<bool>(alarmLow->second))
1108 {
1109 thresholds |=
1110 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1111 }
1112 }
1113 }
1114
1115 // no discrete as of today so optional byte is never returned
1116 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1117}
1118
1119/** @brief implements the Set Sensor threshold command
1120 * @param sensorNumber - sensor number
1121 * @param lowerNonCriticalThreshMask
1122 * @param lowerCriticalThreshMask
1123 * @param lowerNonRecovThreshMask
1124 * @param upperNonCriticalThreshMask
1125 * @param upperCriticalThreshMask
1126 * @param upperNonRecovThreshMask
1127 * @param reserved
1128 * @param lowerNonCritical - lower non-critical threshold
1129 * @param lowerCritical - Lower critical threshold
1130 * @param lowerNonRecoverable - Lower non recovarable threshold
1131 * @param upperNonCritical - Upper non-critical threshold
1132 * @param upperCritical - Upper critical
1133 * @param upperNonRecoverable - Upper Non-recoverable
1134 *
1135 * @returns IPMI completion code
1136 */
1137ipmi::RspType<> ipmiSenSetSensorThresholds(
1138 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1139 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1140 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1141 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001142 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -08001143 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -08001144 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -08001145{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001146 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -08001147 {
1148 return ipmi::responseInvalidFieldRequest();
1149 }
1150
1151 // lower nc and upper nc not suppported on any sensor
1152 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1153 {
1154 return ipmi::responseInvalidFieldRequest();
1155 }
1156
1157 // if none of the threshold mask are set, nothing to do
1158 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1159 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1160 upperCriticalThreshMask | upperNonRecovThreshMask))
1161 {
1162 return ipmi::responseSuccess();
1163 }
1164
1165 std::string connection;
1166 std::string path;
1167
1168 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1169 if (status)
1170 {
1171 return ipmi::response(status);
1172 }
1173 DbusInterfaceMap sensorMap;
1174 if (!getSensorMap(ctx, connection, path, sensorMap))
1175 {
1176 return ipmi::responseResponseError();
1177 }
1178
1179 double max = 0;
1180 double min = 0;
1181 getSensorMaxMin(sensorMap, max, min);
1182
1183 int16_t mValue = 0;
1184 int16_t bValue = 0;
1185 int8_t rExp = 0;
1186 int8_t bExp = 0;
1187 bool bSigned = false;
1188
1189 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1190 {
1191 return ipmi::responseResponseError();
1192 }
1193
1194 // store a vector of property name, value to set, and interface
1195 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1196
1197 // define the indexes of the tuple
1198 constexpr uint8_t propertyName = 0;
1199 constexpr uint8_t thresholdValue = 1;
1200 constexpr uint8_t interface = 2;
1201 // verifiy all needed fields are present
1202 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1203 {
1204 auto findThreshold =
1205 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1206 if (findThreshold == sensorMap.end())
1207 {
1208 return ipmi::responseInvalidFieldRequest();
1209 }
1210 if (lowerCriticalThreshMask)
1211 {
1212 auto findLower = findThreshold->second.find("CriticalLow");
1213 if (findLower == findThreshold->second.end())
1214 {
1215 return ipmi::responseInvalidFieldRequest();
1216 }
1217 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1218 findThreshold->first);
1219 }
1220 if (upperCriticalThreshMask)
1221 {
1222 auto findUpper = findThreshold->second.find("CriticalHigh");
1223 if (findUpper == findThreshold->second.end())
1224 {
1225 return ipmi::responseInvalidFieldRequest();
1226 }
1227 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1228 findThreshold->first);
1229 }
1230 }
1231 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1232 {
1233 auto findThreshold =
1234 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1235 if (findThreshold == sensorMap.end())
1236 {
1237 return ipmi::responseInvalidFieldRequest();
1238 }
1239 if (lowerNonCriticalThreshMask)
1240 {
1241 auto findLower = findThreshold->second.find("WarningLow");
1242 if (findLower == findThreshold->second.end())
1243 {
1244 return ipmi::responseInvalidFieldRequest();
1245 }
1246 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1247 findThreshold->first);
1248 }
1249 if (upperNonCriticalThreshMask)
1250 {
1251 auto findUpper = findThreshold->second.find("WarningHigh");
1252 if (findUpper == findThreshold->second.end())
1253 {
1254 return ipmi::responseInvalidFieldRequest();
1255 }
1256 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1257 findThreshold->first);
1258 }
1259 }
1260 for (const auto& property : thresholdsToSet)
1261 {
1262 // from section 36.3 in the IPMI Spec, assume all linear
1263 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1264 (bValue * std::pow(10.0, bExp))) *
1265 std::pow(10.0, rExp);
1266 setDbusProperty(
1267 *getSdBus(), connection, path, std::get<interface>(property),
1268 std::get<propertyName>(property), ipmi::Value(valueToSet));
1269 }
1270 return ipmi::responseSuccess();
1271}
1272
1273IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1274{
1275 IPMIThresholds resp;
1276 auto warningInterface =
1277 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1278 auto criticalInterface =
1279 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1280
1281 if ((warningInterface != sensorMap.end()) ||
1282 (criticalInterface != sensorMap.end()))
1283 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001284 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001285
1286 if (sensorPair == sensorMap.end())
1287 {
1288 // should not have been able to find a sensor not implementing
1289 // the sensor object
1290 throw std::runtime_error("Invalid sensor map");
1291 }
1292
1293 double max = 0;
1294 double min = 0;
1295 getSensorMaxMin(sensorMap, max, min);
1296
1297 int16_t mValue = 0;
1298 int16_t bValue = 0;
1299 int8_t rExp = 0;
1300 int8_t bExp = 0;
1301 bool bSigned = false;
1302
1303 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1304 {
1305 throw std::runtime_error("Invalid sensor atrributes");
1306 }
1307 if (warningInterface != sensorMap.end())
1308 {
1309 auto& warningMap = warningInterface->second;
1310
1311 auto warningHigh = warningMap.find("WarningHigh");
1312 auto warningLow = warningMap.find("WarningLow");
1313
1314 if (warningHigh != warningMap.end())
1315 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001316 double value = std::visit(VariantToDoubleVisitor(),
1317 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001318 if (std::isfinite(value))
1319 {
1320 resp.warningHigh = scaleIPMIValueFromDouble(
1321 value, mValue, rExp, bValue, bExp, bSigned);
1322 }
Willy Tude54f482021-01-26 15:59:09 -08001323 }
1324 if (warningLow != warningMap.end())
1325 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001326 double value = std::visit(VariantToDoubleVisitor(),
1327 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001328 if (std::isfinite(value))
1329 {
1330 resp.warningLow = scaleIPMIValueFromDouble(
1331 value, mValue, rExp, bValue, bExp, bSigned);
1332 }
Willy Tude54f482021-01-26 15:59:09 -08001333 }
1334 }
1335 if (criticalInterface != sensorMap.end())
1336 {
1337 auto& criticalMap = criticalInterface->second;
1338
1339 auto criticalHigh = criticalMap.find("CriticalHigh");
1340 auto criticalLow = criticalMap.find("CriticalLow");
1341
1342 if (criticalHigh != criticalMap.end())
1343 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001344 double value = std::visit(VariantToDoubleVisitor(),
1345 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001346 if (std::isfinite(value))
1347 {
1348 resp.criticalHigh = scaleIPMIValueFromDouble(
1349 value, mValue, rExp, bValue, bExp, bSigned);
1350 }
Willy Tude54f482021-01-26 15:59:09 -08001351 }
1352 if (criticalLow != criticalMap.end())
1353 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001354 double value = std::visit(VariantToDoubleVisitor(),
1355 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001356 if (std::isfinite(value))
1357 {
1358 resp.criticalLow = scaleIPMIValueFromDouble(
1359 value, mValue, rExp, bValue, bExp, bSigned);
1360 }
Willy Tude54f482021-01-26 15:59:09 -08001361 }
1362 }
1363 }
1364 return resp;
1365}
1366
1367ipmi::RspType<uint8_t, // readable
1368 uint8_t, // lowerNCrit
1369 uint8_t, // lowerCrit
1370 uint8_t, // lowerNrecoverable
1371 uint8_t, // upperNC
1372 uint8_t, // upperCrit
1373 uint8_t> // upperNRecoverable
1374 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1375{
1376 std::string connection;
1377 std::string path;
1378
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001379 if (sensorNumber == reservedSensorNumber)
1380 {
1381 return ipmi::responseInvalidFieldRequest();
1382 }
1383
Willy Tude54f482021-01-26 15:59:09 -08001384 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1385 if (status)
1386 {
1387 return ipmi::response(status);
1388 }
1389
1390 DbusInterfaceMap sensorMap;
1391 if (!getSensorMap(ctx, connection, path, sensorMap))
1392 {
1393 return ipmi::responseResponseError();
1394 }
1395
1396 IPMIThresholds thresholdData;
1397 try
1398 {
1399 thresholdData = getIPMIThresholds(sensorMap);
1400 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001401 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001402 {
1403 return ipmi::responseResponseError();
1404 }
1405
1406 uint8_t readable = 0;
1407 uint8_t lowerNC = 0;
1408 uint8_t lowerCritical = 0;
1409 uint8_t lowerNonRecoverable = 0;
1410 uint8_t upperNC = 0;
1411 uint8_t upperCritical = 0;
1412 uint8_t upperNonRecoverable = 0;
1413
1414 if (thresholdData.warningHigh)
1415 {
1416 readable |=
1417 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1418 upperNC = *thresholdData.warningHigh;
1419 }
1420 if (thresholdData.warningLow)
1421 {
1422 readable |=
1423 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1424 lowerNC = *thresholdData.warningLow;
1425 }
1426
1427 if (thresholdData.criticalHigh)
1428 {
1429 readable |=
1430 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1431 upperCritical = *thresholdData.criticalHigh;
1432 }
1433 if (thresholdData.criticalLow)
1434 {
1435 readable |=
1436 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1437 lowerCritical = *thresholdData.criticalLow;
1438 }
1439
1440 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1441 lowerNonRecoverable, upperNC, upperCritical,
1442 upperNonRecoverable);
1443}
1444
1445/** @brief implements the get Sensor event enable command
1446 * @param sensorNumber - sensor number
1447 *
1448 * @returns IPMI completion code plus response data
1449 * - enabled - Sensor Event messages
1450 * - assertionEnabledLsb - Assertion event messages
1451 * - assertionEnabledMsb - Assertion event messages
1452 * - deassertionEnabledLsb - Deassertion event messages
1453 * - deassertionEnabledMsb - Deassertion event messages
1454 */
1455
1456ipmi::RspType<uint8_t, // enabled
1457 uint8_t, // assertionEnabledLsb
1458 uint8_t, // assertionEnabledMsb
1459 uint8_t, // deassertionEnabledLsb
1460 uint8_t> // deassertionEnabledMsb
1461 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1462{
1463 std::string connection;
1464 std::string path;
1465
1466 uint8_t enabled = 0;
1467 uint8_t assertionEnabledLsb = 0;
1468 uint8_t assertionEnabledMsb = 0;
1469 uint8_t deassertionEnabledLsb = 0;
1470 uint8_t deassertionEnabledMsb = 0;
1471
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001472 if (sensorNum == reservedSensorNumber)
1473 {
1474 return ipmi::responseInvalidFieldRequest();
1475 }
1476
Willy Tude54f482021-01-26 15:59:09 -08001477 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1478 if (status)
1479 {
1480 return ipmi::response(status);
1481 }
1482
Scron Chang2703b022021-07-06 15:47:45 +08001483#ifdef FEATURE_HYBRID_SENSORS
1484 if (auto sensor = findStaticSensor(path);
1485 sensor != ipmi::sensor::sensors.end() &&
1486 getSensorEventTypeFromPath(path) !=
1487 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1488 {
1489 enabled = static_cast<uint8_t>(
1490 IPMISensorEventEnableByte2::sensorScanningEnable);
1491 uint16_t assertionEnabled = 0;
1492 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1493 ->second.begin()
1494 ->second.second)
1495 {
1496 assertionEnabled |= (1 << offsetValMap.first);
1497 }
1498 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1499 assertionEnabledMsb =
1500 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1501
1502 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1503 assertionEnabledMsb, deassertionEnabledLsb,
1504 deassertionEnabledMsb);
1505 }
1506#endif
1507
Willy Tude54f482021-01-26 15:59:09 -08001508 DbusInterfaceMap sensorMap;
1509 if (!getSensorMap(ctx, connection, path, sensorMap))
1510 {
1511 return ipmi::responseResponseError();
1512 }
1513
1514 auto warningInterface =
1515 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1516 auto criticalInterface =
1517 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1518 if ((warningInterface != sensorMap.end()) ||
1519 (criticalInterface != sensorMap.end()))
1520 {
1521 enabled = static_cast<uint8_t>(
1522 IPMISensorEventEnableByte2::sensorScanningEnable);
1523 if (warningInterface != sensorMap.end())
1524 {
1525 auto& warningMap = warningInterface->second;
1526
1527 auto warningHigh = warningMap.find("WarningHigh");
1528 auto warningLow = warningMap.find("WarningLow");
1529 if (warningHigh != warningMap.end())
1530 {
1531 assertionEnabledLsb |= static_cast<uint8_t>(
1532 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1533 deassertionEnabledLsb |= static_cast<uint8_t>(
1534 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1535 }
1536 if (warningLow != warningMap.end())
1537 {
1538 assertionEnabledLsb |= static_cast<uint8_t>(
1539 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1540 deassertionEnabledLsb |= static_cast<uint8_t>(
1541 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1542 }
1543 }
1544 if (criticalInterface != sensorMap.end())
1545 {
1546 auto& criticalMap = criticalInterface->second;
1547
1548 auto criticalHigh = criticalMap.find("CriticalHigh");
1549 auto criticalLow = criticalMap.find("CriticalLow");
1550
1551 if (criticalHigh != criticalMap.end())
1552 {
1553 assertionEnabledMsb |= static_cast<uint8_t>(
1554 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1555 deassertionEnabledMsb |= static_cast<uint8_t>(
1556 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1557 }
1558 if (criticalLow != criticalMap.end())
1559 {
1560 assertionEnabledLsb |= static_cast<uint8_t>(
1561 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1562 deassertionEnabledLsb |= static_cast<uint8_t>(
1563 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1564 }
1565 }
1566 }
1567
1568 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1569 assertionEnabledMsb, deassertionEnabledLsb,
1570 deassertionEnabledMsb);
1571}
1572
1573/** @brief implements the get Sensor event status command
1574 * @param sensorNumber - sensor number, FFh = reserved
1575 *
1576 * @returns IPMI completion code plus response data
1577 * - sensorEventStatus - Sensor Event messages state
1578 * - assertions - Assertion event messages
1579 * - deassertions - Deassertion event messages
1580 */
1581ipmi::RspType<uint8_t, // sensorEventStatus
1582 std::bitset<16>, // assertions
1583 std::bitset<16> // deassertion
1584 >
1585 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1586{
1587 if (sensorNum == reservedSensorNumber)
1588 {
1589 return ipmi::responseInvalidFieldRequest();
1590 }
1591
1592 std::string connection;
1593 std::string path;
1594 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1595 if (status)
1596 {
1597 phosphor::logging::log<phosphor::logging::level::ERR>(
1598 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1599 phosphor::logging::entry("SENSOR=%d", sensorNum));
1600 return ipmi::response(status);
1601 }
1602
Scron Chang2703b022021-07-06 15:47:45 +08001603#ifdef FEATURE_HYBRID_SENSORS
1604 if (auto sensor = findStaticSensor(path);
1605 sensor != ipmi::sensor::sensors.end() &&
1606 getSensorEventTypeFromPath(path) !=
1607 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1608 {
1609 auto response = ipmi::sensor::get::mapDbusToAssertion(
1610 sensor->second, path, sensor->second.sensorInterface);
1611 std::bitset<16> assertions;
1612 // deassertions are not used.
1613 std::bitset<16> deassertions = 0;
1614 uint8_t sensorEventStatus;
1615 if (response.readingOrStateUnavailable)
1616 {
1617 sensorEventStatus |= static_cast<uint8_t>(
1618 IPMISensorReadingByte2::readingStateUnavailable);
1619 }
1620 if (response.scanningEnabled)
1621 {
1622 sensorEventStatus |= static_cast<uint8_t>(
1623 IPMISensorReadingByte2::sensorScanningEnable);
1624 }
1625 if (response.allEventMessagesEnabled)
1626 {
1627 sensorEventStatus |= static_cast<uint8_t>(
1628 IPMISensorReadingByte2::eventMessagesEnable);
1629 }
1630 assertions |= response.discreteReadingSensorStates << 8;
1631 assertions |= response.thresholdLevelsStates;
1632 return ipmi::responseSuccess(sensorEventStatus, assertions,
1633 deassertions);
1634 }
1635#endif
1636
Willy Tude54f482021-01-26 15:59:09 -08001637 DbusInterfaceMap sensorMap;
1638 if (!getSensorMap(ctx, connection, path, sensorMap))
1639 {
1640 phosphor::logging::log<phosphor::logging::level::ERR>(
1641 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1642 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1643 return ipmi::responseResponseError();
1644 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001645
1646 uint8_t sensorEventStatus =
1647 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1648 std::bitset<16> assertions = 0;
1649 std::bitset<16> deassertions = 0;
1650
1651 // handle VR typed sensor
1652 auto vrInterface = sensorMap.find(sensor::vrInterface);
1653 if (vrInterface != sensorMap.end())
1654 {
1655 if (!sensor::getVrEventStatus(ctx, connection, path,
1656 vrInterface->second, assertions))
1657 {
1658 return ipmi::responseResponseError();
1659 }
1660
1661 // both Event Message and Sensor Scanning are disable for VR.
1662 sensorEventStatus = 0;
1663 return ipmi::responseSuccess(sensorEventStatus, assertions,
1664 deassertions);
1665 }
1666
Willy Tude54f482021-01-26 15:59:09 -08001667 auto warningInterface =
1668 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1669 auto criticalInterface =
1670 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1671
Willy Tude54f482021-01-26 15:59:09 -08001672 std::optional<bool> criticalDeassertHigh =
1673 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1674 std::optional<bool> criticalDeassertLow =
1675 thresholdDeassertMap[path]["CriticalAlarmLow"];
1676 std::optional<bool> warningDeassertHigh =
1677 thresholdDeassertMap[path]["WarningAlarmHigh"];
1678 std::optional<bool> warningDeassertLow =
1679 thresholdDeassertMap[path]["WarningAlarmLow"];
1680
Willy Tude54f482021-01-26 15:59:09 -08001681 if (criticalDeassertHigh && !*criticalDeassertHigh)
1682 {
1683 deassertions.set(static_cast<size_t>(
1684 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1685 }
1686 if (criticalDeassertLow && !*criticalDeassertLow)
1687 {
1688 deassertions.set(static_cast<size_t>(
1689 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1690 }
1691 if (warningDeassertHigh && !*warningDeassertHigh)
1692 {
1693 deassertions.set(static_cast<size_t>(
1694 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1695 }
1696 if (warningDeassertLow && !*warningDeassertLow)
1697 {
1698 deassertions.set(static_cast<size_t>(
1699 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1700 }
1701 if ((warningInterface != sensorMap.end()) ||
1702 (criticalInterface != sensorMap.end()))
1703 {
1704 sensorEventStatus = static_cast<size_t>(
1705 IPMISensorEventEnableByte2::eventMessagesEnable);
1706 if (warningInterface != sensorMap.end())
1707 {
1708 auto& warningMap = warningInterface->second;
1709
1710 auto warningHigh = warningMap.find("WarningAlarmHigh");
1711 auto warningLow = warningMap.find("WarningAlarmLow");
1712 auto warningHighAlarm = false;
1713 auto warningLowAlarm = false;
1714
1715 if (warningHigh != warningMap.end())
1716 {
1717 warningHighAlarm = std::get<bool>(warningHigh->second);
1718 }
1719 if (warningLow != warningMap.end())
1720 {
1721 warningLowAlarm = std::get<bool>(warningLow->second);
1722 }
1723 if (warningHighAlarm)
1724 {
1725 assertions.set(
1726 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1727 upperNonCriticalGoingHigh));
1728 }
1729 if (warningLowAlarm)
1730 {
1731 assertions.set(
1732 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1733 lowerNonCriticalGoingLow));
1734 }
1735 }
1736 if (criticalInterface != sensorMap.end())
1737 {
1738 auto& criticalMap = criticalInterface->second;
1739
1740 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1741 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1742 auto criticalHighAlarm = false;
1743 auto criticalLowAlarm = false;
1744
1745 if (criticalHigh != criticalMap.end())
1746 {
1747 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1748 }
1749 if (criticalLow != criticalMap.end())
1750 {
1751 criticalLowAlarm = std::get<bool>(criticalLow->second);
1752 }
1753 if (criticalHighAlarm)
1754 {
1755 assertions.set(
1756 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1757 upperCriticalGoingHigh));
1758 }
1759 if (criticalLowAlarm)
1760 {
1761 assertions.set(static_cast<size_t>(
1762 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1763 }
1764 }
1765 }
1766
1767 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1768}
1769
Willy Tu38e7a2b2021-03-29 15:09:56 -07001770// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001771void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1772 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001773{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001774 get_sdr::header::set_record_id(
1775 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1776
Willy Tu38e7a2b2021-03-29 15:09:56 -07001777 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1778 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1779
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001780 record.header.sdr_version = ipmiSdrVersion;
1781 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1782 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1783 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001784 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001785 record.key.owner_lun = lun;
1786 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001787}
Willy Tu4eca2512022-06-20 21:14:51 -07001788bool constructSensorSdr(
1789 ipmi::Context::ptr ctx,
1790 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1791 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1792 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001793{
Hao Jiange39d4d82021-04-16 17:02:40 -07001794 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1795
1796 DbusInterfaceMap sensorMap;
1797 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1798 {
1799 phosphor::logging::log<phosphor::logging::level::ERR>(
1800 "Failed to update sensor map for threshold sensor",
1801 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1802 phosphor::logging::entry("PATH=%s", path.c_str()));
1803 return false;
1804 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001805
1806 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1807 record.body.sensor_type = getSensorTypeFromPath(path);
1808 std::string type = getSensorTypeStringFromPath(path);
1809 auto typeCstr = type.c_str();
1810 auto findUnits = sensorUnits.find(typeCstr);
1811 if (findUnits != sensorUnits.end())
1812 {
1813 record.body.sensor_units_2_base =
1814 static_cast<uint8_t>(findUnits->second);
1815 } // else default 0x0 unspecified
1816
1817 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1818
Hao Jiangd2afd052020-12-10 15:09:32 -08001819 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001820 if (sensorObject == sensorMap.end())
1821 {
1822 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001823 "constructSensorSdr: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001824 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001825 }
1826
1827 uint8_t entityId = 0;
1828 uint8_t entityInstance = 0x01;
1829
1830 // follow the association chain to get the parent board's entityid and
1831 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001832 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1833 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001834
1835 record.body.entity_id = entityId;
1836 record.body.entity_instance = entityInstance;
1837
Shakeeb Pasha93889722021-10-14 10:20:13 +05301838 double max = 0;
1839 double min = 0;
1840 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001841
1842 int16_t mValue = 0;
1843 int8_t rExp = 0;
1844 int16_t bValue = 0;
1845 int8_t bExp = 0;
1846 bool bSigned = false;
1847
1848 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1849 {
1850 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001851 "constructSensorSdr: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001852 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001853 }
1854
1855 // The record.body is a struct SensorDataFullRecordBody
1856 // from sensorhandler.hpp in phosphor-ipmi-host.
1857 // The meaning of these bits appears to come from
1858 // table 43.1 of the IPMI spec.
1859 // The above 5 sensor attributes are stuffed in as follows:
1860 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1861 // Byte 22-24 are for other purposes
1862 // Byte 25 = MMMMMMMM = LSB of M
1863 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1864 // Byte 27 = BBBBBBBB = LSB of B
1865 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1866 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1867 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1868
1869 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1870 record.body.m_lsb = mValue & 0xFF;
1871
1872 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1873 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1874
1875 // move the smallest bit of the MSB into place (bit 9)
1876 // the MSbs are bits 7:8 in m_msb_and_tolerance
1877 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1878
1879 record.body.b_lsb = bValue & 0xFF;
1880
1881 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1882 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1883
1884 // move the smallest bit of the MSB into place (bit 9)
1885 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1886 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1887
1888 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1889 uint8_t rExpBits = rExp & 0x07;
1890
1891 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1892 uint8_t bExpBits = bExp & 0x07;
1893
1894 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001895 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1896 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001897
1898 // Set the analog reading byte interpretation accordingly
1899 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1900
1901 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1902 // These seem redundant, but derivable from the above 5 attributes
1903 // Original comment said "todo fill out rest of units"
1904
1905 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001906 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001907 get_sdr::body::set_id_strlen(name.size(), &record.body);
1908 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Patrick Williams2f0a6d02023-08-17 12:54:08 -05001909 std::memcpy(record.body.id_string, name.c_str(),
1910 std::min(name.length() + 1, sizeof(record.body.id_string)));
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001911
Josh Lehana55c9532020-10-28 21:59:06 -07001912 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001913 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001914
Jie Yangf0a89942021-07-29 15:30:25 -07001915 bool sensorSettable = false;
1916 auto mutability =
1917 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1918 if (mutability != sensorMap.end())
1919 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001920 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1921 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001922 }
1923 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1924
1925 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001926 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001927
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001928 IPMIThresholds thresholdData;
1929 try
1930 {
1931 thresholdData = getIPMIThresholds(sensorMap);
1932 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001933 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001934 {
1935 phosphor::logging::log<phosphor::logging::level::ERR>(
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07001936 "constructSensorSdr: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001937 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001938 }
1939
1940 if (thresholdData.criticalHigh)
1941 {
1942 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1943 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1944 IPMISensorEventEnableThresholds::criticalThreshold);
1945 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1946 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1947 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1948 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1949 record.body.discrete_reading_setting_mask[0] |=
1950 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1951 }
1952 if (thresholdData.warningHigh)
1953 {
1954 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1955 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1956 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1957 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1958 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1959 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1960 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1961 record.body.discrete_reading_setting_mask[0] |=
1962 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1963 }
1964 if (thresholdData.criticalLow)
1965 {
1966 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1967 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1968 IPMISensorEventEnableThresholds::criticalThreshold);
1969 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1970 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1971 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1972 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1973 record.body.discrete_reading_setting_mask[0] |=
1974 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1975 }
1976 if (thresholdData.warningLow)
1977 {
1978 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1979 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1980 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1981 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1982 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1983 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1984 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1985 record.body.discrete_reading_setting_mask[0] |=
1986 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1987 }
1988
1989 // everything that is readable is setable
1990 record.body.discrete_reading_setting_mask[1] =
1991 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001992 return true;
1993}
1994
Scron Chang2703b022021-07-06 15:47:45 +08001995#ifdef FEATURE_HYBRID_SENSORS
1996// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001997void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001998 uint16_t recordID,
1999 ipmi::sensor::IdInfoMap::const_iterator sensor,
2000 get_sdr::SensorDataFullRecord& record)
2001{
2002 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2003
2004 record.body.entity_id = sensor->second.entityType;
2005 record.body.sensor_type = sensor->second.sensorType;
2006 record.body.event_reading_type = sensor->second.sensorReadingType;
2007 record.body.entity_instance = sensor->second.instance;
2008 if (ipmi::sensor::Mutability::Write ==
2009 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2010 {
2011 get_sdr::body::init_settable_state(true, &(record.body));
2012 }
2013
2014 auto id_string = sensor->second.sensorName;
2015
2016 if (id_string.empty())
2017 {
2018 id_string = sensor->second.sensorNameFunc(sensor->second);
2019 }
2020
2021 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2022 {
2023 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2024 &(record.body));
2025 }
2026 else
2027 {
2028 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2029 }
Paul Fertser51136982022-08-18 12:36:41 +00002030 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08002031 std::strncpy(record.body.id_string, id_string.c_str(),
2032 get_sdr::body::get_id_strlen(&(record.body)));
2033}
2034#endif
2035
Hao Jiange39d4d82021-04-16 17:02:40 -07002036// Construct type 3 SDR header and key (for VR and other discrete sensors)
2037void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2038 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07002039{
2040 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2041 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2042
2043 get_sdr::header::set_record_id(
2044 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2045
2046 record.header.sdr_version = ipmiSdrVersion;
2047 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2048 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2049 sizeof(get_sdr::SensorDataRecordHeader);
2050 record.key.owner_id = bmcI2CAddr;
2051 record.key.owner_lun = lun;
2052 record.key.sensor_number = sensornumber;
2053
2054 record.body.entity_id = 0x00;
2055 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07002056}
Willy Tu61992ad2021-03-29 15:33:20 -07002057
Hao Jiange39d4d82021-04-16 17:02:40 -07002058// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07002059bool constructVrSdr(ipmi::Context::ptr ctx,
2060 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2061 uint16_t sensorNum, uint16_t recordID,
2062 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07002063 get_sdr::SensorDataEventRecord& record)
2064{
Hao Jiange39d4d82021-04-16 17:02:40 -07002065 constructEventSdrHeaderKey(sensorNum, recordID, record);
2066
2067 DbusInterfaceMap sensorMap;
2068 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2069 {
2070 phosphor::logging::log<phosphor::logging::level::ERR>(
2071 "Failed to update sensor map for VR sensor",
2072 phosphor::logging::entry("SERVICE=%s", service.c_str()),
2073 phosphor::logging::entry("PATH=%s", path.c_str()));
2074 return false;
2075 }
Willy Tu61992ad2021-03-29 15:33:20 -07002076 // follow the association chain to get the parent board's entityid and
2077 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07002078 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2079 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07002080 record.body.entity_instance);
2081
2082 // Sensor type is hardcoded as a module/board type instead of parsing from
2083 // sensor path. This is because VR control is allocated in an independent
2084 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2085 // types.
2086 static constexpr const uint8_t module_board_type = 0x15;
2087 record.body.sensor_type = module_board_type;
2088 record.body.event_reading_type = 0x00;
2089
2090 record.body.sensor_record_sharing_1 = 0x00;
2091 record.body.sensor_record_sharing_2 = 0x00;
2092
2093 // populate sensor name from path
2094 auto name = sensor::parseSdrIdFromPath(path);
2095 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00002096 get_sdr::body::set_id_strlen(nameSize, &record.body);
2097 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07002098 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2099 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2100
2101 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08002102 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07002103
2104 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07002105}
2106
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002107uint16_t getNumberOfSensors()
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002108{
2109 return std::min(getSensorTree().size(), maxIPMISensors);
2110}
2111
Willy Tu4eca2512022-06-20 21:14:51 -07002112static int getSensorDataRecord(
2113 ipmi::Context::ptr ctx,
2114 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2115 std::vector<uint8_t>& recordData, uint16_t recordID,
2116 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002117{
selvaganapathi7b2e5502023-02-14 07:10:47 +05302118 recordData.clear();
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002119 size_t lastRecord = ipmi::getNumberOfSensors() +
2120 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2121 uint16_t nextRecord(recordID + 1);
2122
Willy Tu38e7a2b2021-03-29 15:09:56 -07002123 if (recordID == lastRecordIndex)
2124 {
2125 recordID = lastRecord;
2126 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002127 if (recordID == lastRecord)
2128 {
2129 nextRecord = lastRecordIndex;
2130 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002131 if (recordID > lastRecord)
2132 {
2133 phosphor::logging::log<phosphor::logging::level::ERR>(
2134 "getSensorDataRecord: recordID > lastRecord error");
2135 return GENERAL_ERROR;
2136 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002137 if (recordID >= ipmi::getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002138 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002139 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2140 recordData);
2141 err < 0)
Harvey Wu05d17c02021-09-15 08:46:59 +08002142 {
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002143 // phosphor::logging::log<phosphor::logging::level::ERR>(
2144 // "getSensorDataRecord: Error getting custom record");
2145 return lastRecordIndex;
Harvey Wu05d17c02021-09-15 08:46:59 +08002146 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002147 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002148 }
2149
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002150 // Perform a incremental scan of the SDR Record ID's and translate the
2151 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2152 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2153 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2154 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002155 std::string connection;
2156 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002157 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002158 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002159 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002160 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002161 // LUN 0 has one reserved sensor number. Compensate here by adding one
2162 // to the record ID
2163 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002164 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002165 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002166 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002167 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002168 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2169 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2170 // rules governing its use.
2171 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002172 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002173 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002174
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002175 auto status = getSensorConnection(ctx,
2176 static_cast<uint8_t>(sensNumFromRecID),
2177 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002178 if (status)
2179 {
2180 phosphor::logging::log<phosphor::logging::level::ERR>(
2181 "getSensorDataRecord: getSensorConnection error");
2182 return GENERAL_ERROR;
2183 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002184 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002185 // Return an error on LUN 2 assingments, and any sensor number beyond the
2186 // range of LUN 3
2187 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2188 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002189 {
2190 phosphor::logging::log<phosphor::logging::level::ERR>(
2191 "getSensorDataRecord: invalidSensorNumber");
2192 return GENERAL_ERROR;
2193 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002194 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2195 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2196
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002197 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2198 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002199 {
2200 phosphor::logging::log<phosphor::logging::level::ERR>(
2201 "getSensorDataRecord: sensor record mismatch");
2202 return GENERAL_ERROR;
2203 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002204
Willy Tu38e7a2b2021-03-29 15:09:56 -07002205 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002206 if (std::find(interfaces.begin(), interfaces.end(),
2207 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002208 {
Willy Tu11d68892022-01-20 10:37:34 -08002209 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002210
Hao Jiange39d4d82021-04-16 17:02:40 -07002211 // If the request doesn't read SDR body, construct only header and key
2212 // part to avoid additional DBus transaction.
2213 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2214 {
2215 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2216 }
Willy Tu4eca2512022-06-20 21:14:51 -07002217 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2218 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002219 {
2220 return GENERAL_ERROR;
2221 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002222
selvaganapathi7b2e5502023-02-14 07:10:47 +05302223 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2224 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002225
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002226 return nextRecord;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002227 }
Willy Tu61992ad2021-03-29 15:33:20 -07002228
Scron Chang2703b022021-07-06 15:47:45 +08002229#ifdef FEATURE_HYBRID_SENSORS
2230 if (auto sensor = findStaticSensor(path);
2231 sensor != ipmi::sensor::sensors.end() &&
2232 getSensorEventTypeFromPath(path) !=
2233 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2234 {
Willy Tu11d68892022-01-20 10:37:34 -08002235 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002236
2237 // If the request doesn't read SDR body, construct only header and key
2238 // part to avoid additional DBus transaction.
2239 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2240 {
2241 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2242 }
2243 else
2244 {
2245 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2246 }
2247
selvaganapathi7b2e5502023-02-14 07:10:47 +05302248 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2249 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002250
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002251 return nextRecord;
Scron Chang2703b022021-07-06 15:47:45 +08002252 }
2253#endif
2254
Willy Tu61992ad2021-03-29 15:33:20 -07002255 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002256 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2257 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002258 {
Willy Tu11d68892022-01-20 10:37:34 -08002259 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002260
Hao Jiange39d4d82021-04-16 17:02:40 -07002261 // If the request doesn't read SDR body, construct only header and key
2262 // part to avoid additional DBus transaction.
2263 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2264 {
2265 constructEventSdrHeaderKey(sensorNum, recordID, record);
2266 }
Willy Tu4eca2512022-06-20 21:14:51 -07002267 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2268 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002269 {
2270 return GENERAL_ERROR;
2271 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302272 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2273 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002274 }
2275
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002276 return nextRecord;
Willy Tude54f482021-01-26 15:59:09 -08002277}
2278
2279/** @brief implements the get SDR Info command
2280 * @param count - Operation
2281 *
2282 * @returns IPMI completion code plus response data
2283 * - sdrCount - sensor/SDR count
2284 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2285 */
2286static ipmi::RspType<uint8_t, // respcount
2287 uint8_t, // dynamic population flags
2288 uint32_t // last time a sensor was added
2289 >
2290 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2291 std::optional<uint8_t> count)
2292{
2293 auto& sensorTree = getSensorTree();
2294 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002295 uint16_t recordID = 0;
2296 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002297 // Sensors are dynamically allocated, and there is at least one LUN
2298 uint8_t lunsAndDynamicPopulation = 0x80;
2299 constexpr uint8_t getSdrCount = 0x01;
2300 constexpr uint8_t getSensorCount = 0x00;
2301
2302 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2303 {
2304 return ipmi::responseResponseError();
2305 }
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002306 uint16_t numSensors = ipmi::getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002307 if (count.value_or(0) == getSdrCount)
2308 {
Willy Tu4eca2512022-06-20 21:14:51 -07002309 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2310
Harvey Wu75893062023-03-22 17:17:31 +08002311 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002312 {
2313 recordID += maxSensorsPerLUN;
2314 }
Harvey Wu75893062023-03-22 17:17:31 +08002315 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002316 {
2317 recordID += maxSensorsPerLUN * 2;
2318 }
2319
Harvey Wu75893062023-03-22 17:17:31 +08002320 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2321 // assigned to the LUN
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002322 while (getSensorDataRecord(ctx,
2323 ipmiDecoratorPaths.value_or(
2324 std::unordered_set<std::string>()),
2325 record, recordID++) >= 0)
Willy Tude54f482021-01-26 15:59:09 -08002326 {
2327 get_sdr::SensorDataRecordHeader* hdr =
2328 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002329 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302330 if (!hdr)
2331 {
2332 continue;
2333 }
2334
2335 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002336 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002337 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002338 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002339 record.data());
2340 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002341 {
2342 sdrCount++;
2343 }
2344 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302345 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2346 {
2347 get_sdr::SensorDataCompactRecord* recordData =
2348 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2349 record.data());
2350 if (ctx->lun == recordData->key.owner_lun)
2351 {
2352 sdrCount++;
2353 }
2354 }
Harvey Wua3476272023-03-22 10:09:38 +08002355 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2356 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302357 {
2358 sdrCount++;
2359 }
Harvey Wua3476272023-03-22 10:09:38 +08002360
2361 // Because response count data is 1 byte, so sdrCount need to avoid
2362 // overflow.
2363 if (sdrCount == maxSensorsPerLUN)
2364 {
2365 break;
2366 }
Willy Tude54f482021-01-26 15:59:09 -08002367 }
2368 }
2369 else if (count.value_or(0) == getSensorCount)
2370 {
2371 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002372 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002373 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002374 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2375 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002376 }
Harvey Wu75893062023-03-22 17:17:31 +08002377 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002378 {
2379 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2380 ? maxSensorsPerLUN
2381 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2382 }
Harvey Wu75893062023-03-22 17:17:31 +08002383 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002384 {
2385 if (numSensors <= maxIPMISensors)
2386 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002387 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2388 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002389 }
2390 else
2391 {
2392 // error
2393 throw std::out_of_range(
2394 "Maximum number of IPMI sensors exceeded.");
2395 }
2396 }
2397 }
2398 else
2399 {
2400 return ipmi::responseInvalidFieldRequest();
2401 }
2402
2403 // Get Sensor count. This returns the number of sensors
2404 if (numSensors > 0)
2405 {
2406 lunsAndDynamicPopulation |= 1;
2407 }
2408 if (numSensors > maxSensorsPerLUN)
2409 {
2410 lunsAndDynamicPopulation |= 2;
2411 }
2412 if (numSensors >= (maxSensorsPerLUN * 2))
2413 {
2414 lunsAndDynamicPopulation |= 8;
2415 }
2416 if (numSensors > maxIPMISensors)
2417 {
2418 // error
2419 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2420 }
2421
2422 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2423 sdrLastAdd);
2424}
2425
2426/* end sensor commands */
2427
2428/* storage commands */
2429
2430ipmi::RspType<uint8_t, // sdr version
2431 uint16_t, // record count
2432 uint16_t, // free space
2433 uint32_t, // most recent addition
2434 uint32_t, // most recent erase
2435 uint8_t // operationSupport
2436 >
2437 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2438{
Willy Tude54f482021-01-26 15:59:09 -08002439 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Johnathan Mantey31c1ecd2024-06-20 11:10:41 -07002440 uint16_t recordCount = ipmi::getNumberOfSensors() +
2441 ipmi::sensor::getOtherSensorsCount(ctx);
Willy Tude54f482021-01-26 15:59:09 -08002442
2443 uint8_t operationSupport = static_cast<uint8_t>(
2444 SdrRepositoryInfoOps::overflow); // write not supported
2445
2446 operationSupport |=
2447 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2448 operationSupport |= static_cast<uint8_t>(
2449 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2450 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2451 unspecifiedFreeSpace, sdrLastAdd,
2452 sdrLastRemove, operationSupport);
2453}
2454
2455/** @brief implements the get SDR allocation info command
2456 *
2457 * @returns IPMI completion code plus response data
2458 * - allocUnits - Number of possible allocation units
2459 * - allocUnitSize - Allocation unit size in bytes.
2460 * - allocUnitFree - Number of free allocation units
2461 * - allocUnitLargestFree - Largest free block in allocation units
2462 * - maxRecordSize - Maximum record size in allocation units.
2463 */
2464ipmi::RspType<uint16_t, // allocUnits
2465 uint16_t, // allocUnitSize
2466 uint16_t, // allocUnitFree
2467 uint16_t, // allocUnitLargestFree
2468 uint8_t // maxRecordSize
2469 >
2470 ipmiStorageGetSDRAllocationInfo()
2471{
2472 // 0000h unspecified number of alloc units
2473 constexpr uint16_t allocUnits = 0;
2474
2475 constexpr uint16_t allocUnitFree = 0;
2476 constexpr uint16_t allocUnitLargestFree = 0;
2477 // only allow one block at a time
2478 constexpr uint8_t maxRecordSize = 1;
2479
2480 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2481 allocUnitLargestFree, maxRecordSize);
2482}
2483
2484/** @brief implements the reserve SDR command
2485 * @returns IPMI completion code plus response data
2486 * - sdrReservationID
2487 */
2488ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2489{
2490 sdrReservationID++;
2491 if (sdrReservationID == 0)
2492 {
2493 sdrReservationID++;
2494 }
2495
2496 return ipmi::responseSuccess(sdrReservationID);
2497}
2498
2499ipmi::RspType<uint16_t, // next record ID
2500 std::vector<uint8_t> // payload
2501 >
2502 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2503 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2504{
2505 // reservation required for partial reads with non zero offset into
2506 // record
2507 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2508 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002509 phosphor::logging::log<phosphor::logging::level::ERR>(
2510 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002511 return ipmi::responseInvalidReservationId();
2512 }
Harvey Wu05d17c02021-09-15 08:46:59 +08002513
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002514 auto& sensorTree = getSensorTree();
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002515 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002516 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002517 phosphor::logging::log<phosphor::logging::level::ERR>(
2518 "ipmiStorageGetSDR: getSensorSubtree error");
2519 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002520 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002521
Willy Tu4eca2512022-06-20 21:14:51 -07002522 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2523
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002524 std::vector<uint8_t> record;
Johnathan Mantey777cfaf2024-06-13 10:45:47 -07002525 int nextRecordId = getSensorDataRecord(
2526 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2527 record, recordID, offset + bytesToRead);
2528
2529 if (nextRecordId < 0)
Willy Tude54f482021-01-26 15:59:09 -08002530 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002531 phosphor::logging::log<phosphor::logging::level::ERR>(
2532 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002533 return ipmi::responseInvalidFieldRequest();
2534 }
Willy Tude54f482021-01-26 15:59:09 -08002535 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002536 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002537 if (!hdr)
2538 {
2539 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002540 "ipmiStorageGetSDR: record header is null");
2541 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002542 }
2543
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002544 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2545 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002546 if (offset >= sdrLength)
2547 {
2548 phosphor::logging::log<phosphor::logging::level::ERR>(
2549 "ipmiStorageGetSDR: offset is outside the record");
2550 return ipmi::responseParmOutOfRange();
2551 }
Willy Tude54f482021-01-26 15:59:09 -08002552 if (sdrLength < (offset + bytesToRead))
2553 {
2554 bytesToRead = sdrLength - offset;
2555 }
2556
2557 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2558 if (!respStart)
2559 {
2560 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002561 "ipmiStorageGetSDR: record is null");
2562 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002563 }
2564
2565 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002566
Willy Tude54f482021-01-26 15:59:09 -08002567 return ipmi::responseSuccess(nextRecordId, recordData);
2568}
adarshgrami042e9db2022-09-15 10:34:34 +05302569namespace dcmi
2570{
2571
Thang Tranb1416ef2023-08-02 13:57:09 +07002572std::tuple<uint8_t, // Total of instance sensors
2573 std::vector<sensorInfo> // The list of sensors
2574 >
2575 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2576 uint8_t entityInstance, uint8_t instanceStart)
2577{
2578 std::vector<sensorInfo> sensorList;
2579 uint8_t totalInstSensor = 0;
2580 auto match = ipmi::dcmi::validEntityId.find(entityId);
2581
2582 if (match == ipmi::dcmi::validEntityId.end())
2583 {
2584 return std::make_tuple(totalInstSensor, sensorList);
2585 }
2586
2587 auto& sensorTree = getSensorTree();
2588 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2589 {
2590 return std::make_tuple(totalInstSensor, sensorList);
2591 }
2592
2593 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2594
Willy Tu62e3ca82024-01-31 17:28:34 +00002595 size_t invalidSensorNumberErrCount = 0;
Thang Tranb1416ef2023-08-02 13:57:09 +07002596 for (const auto& sensor : sensorTree)
2597 {
2598 const std::string& sensorObjPath = sensor.first;
2599 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2600
2601 /*
2602 * In the DCMI specification, it only supports the sensor type is 0x01
2603 * (temperature type) for both Get Sensor Info and Get Temperature
2604 * Readings commands.
2605 */
2606 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2607 {
2608 continue;
2609 }
2610
2611 const auto& connection = sensor.second.begin()->first;
2612 DbusInterfaceMap sensorMap;
2613
2614 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2615 sensorMapSdrUpdatePeriod))
2616 {
2617 phosphor::logging::log<phosphor::logging::level::ERR>(
2618 "Failed to update sensor map for threshold sensor",
2619 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2620 phosphor::logging::entry("PATH=%s", sensorObjPath.c_str()));
2621 continue;
2622 }
2623
2624 uint8_t entityIdValue = 0;
2625 uint8_t entityInstanceValue = 0;
2626
2627 /*
2628 * Get the Entity ID, Entity Instance information which are configured
2629 * in the Entity-Manger.
2630 */
2631 updateIpmiFromAssociation(
2632 sensorObjPath,
2633 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2634 sensorMap, entityIdValue, entityInstanceValue);
2635
2636 if (entityIdValue == match->first || entityIdValue == match->second)
2637 {
2638 totalInstSensor++;
2639
2640 /*
2641 * When Entity Instance parameter is not 0, we only get the first
2642 * sensor whose Entity Instance number is equal input Entity
2643 * Instance parameter.
2644 */
2645 if (entityInstance)
2646 {
2647 if (!sensorList.empty())
2648 {
2649 continue;
2650 }
2651
2652 if (entityInstanceValue == entityInstance)
2653 {
2654 auto recordId = getSensorNumberFromPath(sensorObjPath);
Willy Tu62e3ca82024-01-31 17:28:34 +00002655 if (recordId == invalidSensorNumber)
Thang Tranb1416ef2023-08-02 13:57:09 +07002656 {
Willy Tu62e3ca82024-01-31 17:28:34 +00002657 ++invalidSensorNumberErrCount;
2658 continue;
Thang Tranb1416ef2023-08-02 13:57:09 +07002659 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002660 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2661 recordId, entityIdValue,
2662 entityInstanceValue);
2663 }
2664 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002665 else if (entityInstanceValue >= instanceStart)
2666 {
2667 auto recordId = getSensorNumberFromPath(sensorObjPath);
2668 if (recordId == invalidSensorNumber)
2669 {
2670 ++invalidSensorNumberErrCount;
2671 continue;
2672 }
2673 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2674 recordId, entityIdValue,
2675 entityInstanceValue);
2676 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002677 }
2678 }
Willy Tu62e3ca82024-01-31 17:28:34 +00002679 if (invalidSensorNumberErrCount != 0)
2680 {
2681 phosphor::logging::log<phosphor::logging::level::ERR>(
2682 std::format(
2683 "getSensorNumberFromPath returned invalidSensorNumber {} times",
2684 invalidSensorNumberErrCount)
2685 .data());
2686 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002687
2688 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2689 return first.entityInstance <= second.entityInstance;
2690 };
2691
2692 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2693
2694 return std::make_tuple(totalInstSensor, sensorList);
2695}
2696
Thang Tran3dad8262023-08-17 15:20:56 +07002697std::tuple<bool, // Reading result
2698 uint7_t, // Temp value
2699 bool> // Sign bit
2700 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2701{
2702 std::string service{};
2703 boost::system::error_code ec =
2704 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2705 if (ec.value())
2706 {
2707 return std::make_tuple(false, 0, false);
2708 }
2709
2710 ipmi::PropertyMap properties{};
2711 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2712 sensor::sensorInterface, properties);
2713 if (ec.value())
2714 {
2715 return std::make_tuple(false, 0, false);
2716 }
2717
2718 auto scaleIt = properties.find("Scale");
2719 double scaleVal = 0.0;
2720 if (scaleIt != properties.end())
2721 {
2722 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2723 }
2724
2725 auto tempValIt = properties.find("Value");
2726 double tempVal = 0.0;
2727 if (tempValIt == properties.end())
2728 {
2729 return std::make_tuple(false, 0, false);
2730 }
2731
2732 const double maxTemp = 127;
2733 double absTempVal = 0.0;
2734 bool signBit = false;
2735
2736 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2737 tempVal = std::pow(10, scaleVal) * tempVal;
2738 absTempVal = std::abs(tempVal);
2739 absTempVal = std::min(absTempVal, maxTemp);
2740 signBit = (tempVal < 0) ? true : false;
2741
2742 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2743}
2744
adarshgrami042e9db2022-09-15 10:34:34 +05302745ipmi::RspType<uint8_t, // No of instances for requested id
2746 uint8_t, // No of record ids in the response
2747 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2748 // IDs
2749 >
2750 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Thang Tranb1416ef2023-08-02 13:57:09 +07002751 uint8_t entityInstance, uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302752{
2753 auto match = ipmi::dcmi::validEntityId.find(entityId);
2754 if (match == ipmi::dcmi::validEntityId.end())
2755 {
2756 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2757
2758 return ipmi::responseInvalidFieldRequest();
2759 }
2760
2761 if (sensorType != ipmi::dcmi::temperatureSensorType)
2762 {
2763 log<level::ERR>("Invalid sensor type",
2764 entry("SENSOR_TYPE=%d", sensorType));
2765
2766 return ipmi::responseInvalidFieldRequest();
2767 }
adarshgrami042e9db2022-09-15 10:34:34 +05302768
2769 std::vector<uint16_t> sensorRec{};
Thang Tranb1416ef2023-08-02 13:57:09 +07002770 const auto& [totalSensorInst, sensorList] =
2771 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
adarshgrami042e9db2022-09-15 10:34:34 +05302772
Thang Tranb1416ef2023-08-02 13:57:09 +07002773 if (sensorList.empty())
adarshgrami042e9db2022-09-15 10:34:34 +05302774 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002775 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2776 }
adarshgrami042e9db2022-09-15 10:34:34 +05302777
Thang Tranb1416ef2023-08-02 13:57:09 +07002778 /*
2779 * As DCMI specification, the maximum number of Record Ids of response data
2780 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2781 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2782 * in response data.
2783 */
2784 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2785
2786 for (const auto& sensor : sensorList)
2787 {
2788 sensorRec.emplace_back(sensor.recordId);
2789 if (sensorRec.size() >= numOfRec)
adarshgrami042e9db2022-09-15 10:34:34 +05302790 {
Thang Tranb1416ef2023-08-02 13:57:09 +07002791 break;
adarshgrami042e9db2022-09-15 10:34:34 +05302792 }
2793 }
Thang Tranb1416ef2023-08-02 13:57:09 +07002794
2795 return ipmi::responseSuccess(
2796 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
adarshgrami042e9db2022-09-15 10:34:34 +05302797}
Thang Tran3dad8262023-08-17 15:20:56 +07002798
2799ipmi::RspType<uint8_t, // No of instances for requested id
2800 uint8_t, // No of record ids in the response
2801 std::vector< // Temperature Data
2802 std::tuple<uint7_t, // Temperature value
2803 bool, // Sign bit
2804 uint8_t // Entity Instance of sensor
2805 >>>
2806 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2807 uint8_t entityId, uint8_t entityInstance,
2808 uint8_t instanceStart)
2809{
2810 auto match = ipmi::dcmi::validEntityId.find(entityId);
2811 if (match == ipmi::dcmi::validEntityId.end())
2812 {
2813 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2814
2815 return ipmi::responseInvalidFieldRequest();
2816 }
2817
2818 if (sensorType != ipmi::dcmi::temperatureSensorType)
2819 {
2820 log<level::ERR>("Invalid sensor type",
2821 entry("SENSOR_TYPE=%d", sensorType));
2822
2823 return ipmi::responseInvalidFieldRequest();
2824 }
2825
2826 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2827 const auto& [totalSensorInst, sensorList] =
2828 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2829
2830 if (sensorList.empty())
2831 {
2832 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2833 }
2834
2835 /*
2836 * As DCMI specification, the maximum number of Record Ids of response data
2837 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2838 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2839 * in response data.
2840 */
2841 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2842
2843 for (const auto& sensor : sensorList)
2844 {
2845 const auto& [readResult, tempVal,
2846 signBit] = readTemp(ctx, sensor.objectPath);
2847
2848 if (readResult)
2849 {
2850 tempReadingVal.emplace_back(
2851 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2852
2853 if (tempReadingVal.size() >= numOfRec)
2854 {
2855 break;
2856 }
2857 }
2858 }
2859
2860 return ipmi::responseSuccess(totalSensorInst,
2861 static_cast<uint8_t>(tempReadingVal.size()),
2862 tempReadingVal);
2863}
2864
adarshgrami042e9db2022-09-15 10:34:34 +05302865} // namespace dcmi
2866
Willy Tude54f482021-01-26 15:59:09 -08002867/* end storage commands */
2868
2869void registerSensorFunctions()
2870{
2871 // <Platform Event>
2872 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2873 ipmi::sensor_event::cmdPlatformEvent,
2874 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2875
Willy Tudbafbce2021-03-29 00:37:05 -07002876 // <Set Sensor Reading and Event Status>
2877 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2878 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2879 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002880
Willy Tude54f482021-01-26 15:59:09 -08002881 // <Get Sensor Reading>
2882 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2883 ipmi::sensor_event::cmdGetSensorReading,
2884 ipmi::Privilege::User, ipmiSenGetSensorReading);
2885
2886 // <Get Sensor Threshold>
2887 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2888 ipmi::sensor_event::cmdGetSensorThreshold,
2889 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2890
2891 // <Set Sensor Threshold>
2892 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2893 ipmi::sensor_event::cmdSetSensorThreshold,
2894 ipmi::Privilege::Operator,
2895 ipmiSenSetSensorThresholds);
2896
2897 // <Get Sensor Event Enable>
2898 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2899 ipmi::sensor_event::cmdGetSensorEventEnable,
2900 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2901
2902 // <Get Sensor Event Status>
2903 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2904 ipmi::sensor_event::cmdGetSensorEventStatus,
2905 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2906
2907 // register all storage commands for both Sensor and Storage command
2908 // versions
2909
2910 // <Get SDR Repository Info>
2911 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2912 ipmi::storage::cmdGetSdrRepositoryInfo,
2913 ipmi::Privilege::User,
2914 ipmiStorageGetSDRRepositoryInfo);
2915
2916 // <Get Device SDR Info>
2917 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2918 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2919 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2920
2921 // <Get SDR Allocation Info>
2922 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2923 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2924 ipmi::Privilege::User,
2925 ipmiStorageGetSDRAllocationInfo);
2926
2927 // <Reserve SDR Repo>
2928 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2929 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2930 ipmi::Privilege::User, ipmiStorageReserveSDR);
2931
2932 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2933 ipmi::storage::cmdReserveSdrRepository,
2934 ipmi::Privilege::User, ipmiStorageReserveSDR);
2935
2936 // <Get Sdr>
2937 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2938 ipmi::sensor_event::cmdGetDeviceSdr,
2939 ipmi::Privilege::User, ipmiStorageGetSDR);
2940
2941 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2942 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2943 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302944 // <Get DCMI Sensor Info>
2945 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2946 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002947 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302948 ipmi::dcmi::getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07002949 // <Get Temperature Readings>
2950 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2951 ipmi::dcmi::cmdGetTemperatureReadings,
2952 ipmi::Privilege::User,
2953 ipmi::dcmi::getTempReadings);
Willy Tude54f482021-01-26 15:59:09 -08002954}
2955} // namespace ipmi