blob: d47114be8571373cdc60636d335e1091818b727d [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
17#include "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
Willy Tude54f482021-01-26 15:59:09 -080023#include <boost/algorithm/string.hpp>
24#include <boost/container/flat_map.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050025#include <ipmid/api.hpp>
Vernon Mauery9cf08382023-04-28 14:00:11 -070026#include <ipmid/entity_map_json.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050027#include <ipmid/types.hpp>
28#include <ipmid/utils.hpp>
29#include <phosphor-logging/log.hpp>
30#include <sdbusplus/bus.hpp>
31#include <user_channel/channel_layer.hpp>
32
33#include <algorithm>
34#include <array>
Willy Tude54f482021-01-26 15:59:09 -080035#include <chrono>
36#include <cmath>
37#include <cstring>
38#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080039#include <map>
40#include <memory>
41#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080042#include <stdexcept>
43#include <string>
44#include <utility>
45#include <variant>
46
Scron Chang2703b022021-07-06 15:47:45 +080047#ifdef FEATURE_HYBRID_SENSORS
48
49#include "sensordatahandler.hpp"
50namespace ipmi
51{
52namespace sensor
53{
54extern const IdInfoMap sensors;
55} // namespace sensor
56} // namespace ipmi
57#endif
adarshgrami042e9db2022-09-15 10:34:34 +053058namespace ipmi
59{
60namespace dcmi
61{
62// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
63static const std::map<uint8_t, uint8_t> validEntityId{
64 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
65 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
66constexpr uint8_t temperatureSensorType = 0x01;
67constexpr uint8_t maxRecords = 8;
68} // namespace dcmi
69} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080070constexpr std::array<const char*, 7> suffixes = {
71 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
72 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080073namespace ipmi
74{
Hao Jiangd48c9212021-02-03 15:45:06 -080075
76using phosphor::logging::entry;
77using phosphor::logging::level;
78using phosphor::logging::log;
79
Willy Tude54f482021-01-26 15:59:09 -080080static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070081static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080082
Willy Tu38e7a2b2021-03-29 15:09:56 -070083// BMC I2C address is generally at 0x20
84static constexpr uint8_t bmcI2CAddr = 0x20;
85
Willy Tude54f482021-01-26 15:59:09 -080086constexpr size_t maxSDRTotalSize =
87 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
88constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
89
90static uint16_t sdrReservationID;
91static uint32_t sdrLastAdd = noTimestamp;
92static uint32_t sdrLastRemove = noTimestamp;
93static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070094
95// The IPMI spec defines four Logical Units (LUN), each capable of supporting
96// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
97// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +080098// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -070099// location.
100static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
101
Harvey Wu75893062023-03-22 17:17:31 +0800102static constexpr uint8_t lun0 = 0x0;
103static constexpr uint8_t lun1 = 0x1;
104static constexpr uint8_t lun3 = 0x3;
105
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700106static constexpr size_t lun0MaxSensorNum = 0xfe;
107static constexpr size_t lun1MaxSensorNum = 0x1fe;
108static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800109static constexpr int GENERAL_ERROR = -1;
110
Willy Tude54f482021-01-26 15:59:09 -0800111static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
112
113// Specify the comparison required to sort and find char* map objects
114struct CmpStr
115{
116 bool operator()(const char* a, const char* b) const
117 {
118 return std::strcmp(a, b) < 0;
119 }
120};
121const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
122 sensorUnits{{{"temperature", SensorUnits::degreesC},
123 {"voltage", SensorUnits::volts},
124 {"current", SensorUnits::amps},
125 {"fan_tach", SensorUnits::rpm},
126 {"power", SensorUnits::watts}}};
127
128void registerSensorFunctions() __attribute__((constructor));
129
Patrick Williams5d82f472022-07-22 19:26:53 -0500130static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800131 *getSdBus(),
132 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
133 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500134 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500135 getSensorTree().clear();
136 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
137 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
138 std::chrono::system_clock::now().time_since_epoch())
139 .count();
Willy Tude54f482021-01-26 15:59:09 -0800140 });
141
Patrick Williams5d82f472022-07-22 19:26:53 -0500142static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800143 *getSdBus(),
144 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
145 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500146 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500147 getSensorTree().clear();
148 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
149 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
150 std::chrono::system_clock::now().time_since_epoch())
151 .count();
Willy Tude54f482021-01-26 15:59:09 -0800152 });
153
154// this keeps track of deassertions for sensor event status command. A
155// deasertion can only happen if an assertion was seen first.
156static boost::container::flat_map<
157 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
158 thresholdDeassertMap;
159
Patrick Williams5d82f472022-07-22 19:26:53 -0500160static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800161 *getSdBus(),
162 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
163 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500164 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500165 boost::container::flat_map<std::string, std::variant<bool, double>> values;
166 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800167
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500168 auto findAssert = std::find_if(values.begin(), values.end(),
169 [](const auto& pair) {
170 return pair.first.find("Alarm") != std::string::npos;
171 });
172 if (findAssert != values.end())
173 {
174 auto ptr = std::get_if<bool>(&(findAssert->second));
175 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800176 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500177 phosphor::logging::log<phosphor::logging::level::ERR>(
178 "thresholdChanged: Assert non bool");
179 return;
180 }
181 if (*ptr)
182 {
183 phosphor::logging::log<phosphor::logging::level::INFO>(
184 "thresholdChanged: Assert",
185 phosphor::logging::entry("SENSOR=%s", m.get_path()));
186 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
187 }
188 else
189 {
190 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
191 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800192 {
193 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500194 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800195 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500196 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800197 }
198 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500199 }
Willy Tude54f482021-01-26 15:59:09 -0800200 });
201
Hao Jiangd2afd052020-12-10 15:09:32 -0800202namespace sensor
203{
204static constexpr const char* vrInterface =
205 "xyz.openbmc_project.Control.VoltageRegulatorMode";
206static constexpr const char* sensorInterface =
207 "xyz.openbmc_project.Sensor.Value";
208} // namespace sensor
209
Willy Tude54f482021-01-26 15:59:09 -0800210static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
211 double& min)
212{
213 max = 127;
214 min = -128;
215
Hao Jiangd2afd052020-12-10 15:09:32 -0800216 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800217 auto critical =
218 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
219 auto warning =
220 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
221
222 if (sensorObject != sensorMap.end())
223 {
224 auto maxMap = sensorObject->second.find("MaxValue");
225 auto minMap = sensorObject->second.find("MinValue");
226
227 if (maxMap != sensorObject->second.end())
228 {
229 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
230 }
231 if (minMap != sensorObject->second.end())
232 {
233 min = std::visit(VariantToDoubleVisitor(), minMap->second);
234 }
235 }
236 if (critical != sensorMap.end())
237 {
238 auto lower = critical->second.find("CriticalLow");
239 auto upper = critical->second.find("CriticalHigh");
240 if (lower != critical->second.end())
241 {
242 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300243 if (std::isfinite(value))
244 {
245 min = std::min(value, min);
246 }
Willy Tude54f482021-01-26 15:59:09 -0800247 }
248 if (upper != critical->second.end())
249 {
250 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300251 if (std::isfinite(value))
252 {
253 max = std::max(value, max);
254 }
Willy Tude54f482021-01-26 15:59:09 -0800255 }
256 }
257 if (warning != sensorMap.end())
258 {
Willy Tude54f482021-01-26 15:59:09 -0800259 auto lower = warning->second.find("WarningLow");
260 auto upper = warning->second.find("WarningHigh");
261 if (lower != warning->second.end())
262 {
263 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300264 if (std::isfinite(value))
265 {
266 min = std::min(value, min);
267 }
Willy Tude54f482021-01-26 15:59:09 -0800268 }
269 if (upper != warning->second.end())
270 {
271 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300272 if (std::isfinite(value))
273 {
274 max = std::max(value, max);
275 }
Willy Tude54f482021-01-26 15:59:09 -0800276 }
277 }
278}
279
280static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700281 std::string sensorPath, DbusInterfaceMap& sensorMap,
282 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800283{
Scron Chang2703b022021-07-06 15:47:45 +0800284#ifdef FEATURE_HYBRID_SENSORS
285 if (auto sensor = findStaticSensor(sensorPath);
286 sensor != ipmi::sensor::sensors.end() &&
287 getSensorEventTypeFromPath(sensorPath) !=
288 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
289 {
290 // If the incoming sensor is a discrete sensor, it might fail in
291 // getManagedObjects(), return true, and use its own getFunc to get
292 // value.
293 return true;
294 }
295#endif
296
Willy Tude54f482021-01-26 15:59:09 -0800297 static boost::container::flat_map<
298 std::string, std::chrono::time_point<std::chrono::steady_clock>>
299 updateTimeMap;
300
301 auto updateFind = updateTimeMap.find(sensorConnection);
302 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
303 if (updateFind != updateTimeMap.end())
304 {
305 lastUpdate = updateFind->second;
306 }
307
308 auto now = std::chrono::steady_clock::now();
309
310 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700311 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800312 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800313 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800314
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800315 // Object managers for different kinds of OpenBMC DBus interfaces.
316 // Documented in the phosphor-dbus-interfaces repository.
317 const char* paths[] = {
318 "/xyz/openbmc_project/sensors",
319 "/xyz/openbmc_project/vr",
320 };
321 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
322 ObjectValueTree allManagedObjects;
323
324 for (size_t i = 0; i < num_paths; i++)
325 {
326 ObjectValueTree managedObjects;
327 boost::system::error_code ec = getManagedObjects(
328 ctx, sensorConnection.c_str(), paths[i], managedObjects);
329 if (ec)
330 {
331 phosphor::logging::log<phosphor::logging::level::ERR>(
332 "GetMangagedObjects for getSensorMap failed",
333 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
334
335 continue;
336 }
337 allManagedObjects.merge(managedObjects);
338 found = true;
339 }
340
341 if (!found)
342 {
Willy Tude54f482021-01-26 15:59:09 -0800343 return false;
344 }
345
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800346 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700347 // Update time after finish building the map which allow the
348 // data to be cached for updatePeriod plus the build time.
349 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800350 }
351 auto connection = SensorCache.find(sensorConnection);
352 if (connection == SensorCache.end())
353 {
354 return false;
355 }
356 auto path = connection->second.find(sensorPath);
357 if (path == connection->second.end())
358 {
359 return false;
360 }
361 sensorMap = path->second;
362
363 return true;
364}
365
Hao Jiangd2afd052020-12-10 15:09:32 -0800366namespace sensor
367{
Hao Jiangd48c9212021-02-03 15:45:06 -0800368// Read VR profiles from sensor(daemon) interface
369static std::optional<std::vector<std::string>>
370 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800371{
372 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800373 auto supportedProperty = object.find("Supported");
374 if (supportedProperty == object.end() ||
375 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800376 {
377 phosphor::logging::log<phosphor::logging::level::ERR>(
378 "Missing the required Supported and Selected properties");
379 return std::nullopt;
380 }
381
382 const auto profilesPtr =
383 std::get_if<std::vector<std::string>>(&supportedProperty->second);
384
385 if (profilesPtr == nullptr)
386 {
387 phosphor::logging::log<phosphor::logging::level::ERR>(
388 "property is not array of string");
389 return std::nullopt;
390 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800391 return *profilesPtr;
392}
393
394// Calculate VR Mode from input IPMI discrete event bytes
395static std::optional<std::string>
396 calculateVRMode(uint15_t assertOffset,
397 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
398{
399 // get VR mode profiles from Supported Interface
400 auto profiles = getSupportedVrProfiles(VRObject);
401 if (!profiles)
402 {
403 return std::nullopt;
404 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800405
406 // interpret IPMI cmd bits into profiles' index
407 long unsigned int index = 0;
408 // only one bit should be set and the highest bit should not be used.
409 if (assertOffset == 0 || assertOffset == (1u << 15) ||
410 (assertOffset & (assertOffset - 1)))
411 {
412 phosphor::logging::log<phosphor::logging::level::ERR>(
413 "IPMI cmd format incorrect",
414
415 phosphor::logging::entry("BYTES=%#02x",
416 static_cast<uint16_t>(assertOffset)));
417 return std::nullopt;
418 }
419
420 while (assertOffset != 1)
421 {
422 assertOffset >>= 1;
423 index++;
424 }
425
Hao Jiangd48c9212021-02-03 15:45:06 -0800426 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800427 {
428 phosphor::logging::log<phosphor::logging::level::ERR>(
429 "profile index out of boundary");
430 return std::nullopt;
431 }
432
Hao Jiangd48c9212021-02-03 15:45:06 -0800433 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800434}
435
436// Calculate sensor value from IPMI reading byte
437static std::optional<double>
438 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
439 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
440{
441 if (valueObject.find("Value") == valueObject.end())
442 {
443 phosphor::logging::log<phosphor::logging::level::ERR>(
444 "Missing the required Value property");
445 return std::nullopt;
446 }
447
448 double max = 0;
449 double min = 0;
450 getSensorMaxMin(sensorMap, max, min);
451
452 int16_t mValue = 0;
453 int16_t bValue = 0;
454 int8_t rExp = 0;
455 int8_t bExp = 0;
456 bool bSigned = false;
457
458 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
459 {
460 return std::nullopt;
461 }
462
463 double value = bSigned ? ((int8_t)reading) : reading;
464
465 value *= ((double)mValue);
466 value += ((double)bValue) * std::pow(10.0, bExp);
467 value *= std::pow(10.0, rExp);
468
469 return value;
470}
471
Willy Tu38e7a2b2021-03-29 15:09:56 -0700472// Extract file name from sensor path as the sensors SDR ID. Simplify the name
473// if it is too long.
474std::string parseSdrIdFromPath(const std::string& path)
475{
476 std::string name;
477 size_t nameStart = path.rfind("/");
478 if (nameStart != std::string::npos)
479 {
480 name = path.substr(nameStart + 1, std::string::npos - nameStart);
481 }
482
Willy Tu38e7a2b2021-03-29 15:09:56 -0700483 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
484 {
485 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800486 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700487 {
JeffLind950f412021-10-20 18:49:34 +0800488 if (boost::ends_with(name, suffix))
489 {
490 boost::replace_all(name, suffix, "");
491 break;
492 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700493 }
Duke Du97014f52021-12-16 17:21:01 +0800494 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
495 {
496 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
497 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700498 }
499 return name;
500}
501
Hao Jiangd48c9212021-02-03 15:45:06 -0800502bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
503 const std::string& path,
504 const ipmi::DbusInterfaceMap::mapped_type& object,
505 std::bitset<16>& assertions)
506{
507 auto profiles = sensor::getSupportedVrProfiles(object);
508 if (!profiles)
509 {
510 return false;
511 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700512 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800513
514 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700515 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800516 if (ec)
517 {
518 log<level::ERR>("Failed to get property",
519 entry("PROPERTY=%s", "Selected"),
520 entry("PATH=%s", path.c_str()),
521 entry("INTERFACE=%s", sensor::sensorInterface),
522 entry("WHAT=%s", ec.message().c_str()));
523 return false;
524 }
525
Willy Tu8366f0b2022-04-29 05:00:17 -0700526 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800527 if (itr == profiles->end())
528 {
529 using namespace phosphor::logging;
530 log<level::ERR>("VR mode doesn't match any of its profiles",
531 entry("PATH=%s", path.c_str()));
532 return false;
533 }
534 std::size_t index =
535 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
536
Willy Tubef102a2022-06-09 15:36:09 -0700537 // map index to response event assertion bit.
538 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800539 {
Willy Tubef102a2022-06-09 15:36:09 -0700540 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800541 }
542 else
543 {
544 log<level::ERR>("VR profile index reaches max assertion bit",
545 entry("PATH=%s", path.c_str()),
546 entry("INDEX=%uz", index));
547 return false;
548 }
549 if constexpr (debug)
550 {
551 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700552 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800553 }
554 return true;
555}
Hao Jiangd2afd052020-12-10 15:09:32 -0800556} // namespace sensor
557
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000558ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
559 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800560{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000561 constexpr const uint8_t validEnvmRev = 0x04;
562 constexpr const uint8_t lastSensorType = 0x2C;
563 constexpr const uint8_t oemReserved = 0xC0;
564
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700565 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000566 uint8_t evmRev = 0;
567 uint8_t sensorType = 0;
568 uint8_t sensorNum = 0;
569 uint8_t eventType = 0;
570 uint8_t eventData1 = 0;
571 std::optional<uint8_t> eventData2 = 0;
572 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700573 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000574 ipmi::ChannelInfo chInfo;
575
576 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
577 {
578 phosphor::logging::log<phosphor::logging::level::ERR>(
579 "Failed to get Channel Info",
580 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
581 return ipmi::responseUnspecifiedError();
582 }
583
584 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
585 ipmi::EChannelMediumType::systemInterface)
586 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700587 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000588 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700589 // Refer to IPMI Spec Table 32: SEL Event Records
590 generatorID = (ctx->channel << 12) // Channel
591 | (0x0 << 10) // Reserved
592 | (0x0 << 8) // 0x0 for sys-soft ID
593 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000594 }
595 else
596 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000597 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
598 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700599 // Refer to IPMI Spec Table 32: SEL Event Records
600 generatorID = (ctx->channel << 12) // Channel
601 | (0x0 << 10) // Reserved
602 | ((ctx->lun & 0x3) << 8) // Lun
603 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000604 }
605
606 if (!p.fullyUnpacked())
607 {
608 return ipmi::responseReqDataLenInvalid();
609 }
610
611 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
612 if (evmRev != validEnvmRev)
613 {
614 return ipmi::responseInvalidFieldRequest();
615 }
616 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
617 {
618 return ipmi::responseInvalidFieldRequest();
619 }
620
Willy Tude54f482021-01-26 15:59:09 -0800621 return ipmi::responseSuccess();
622}
623
Willy Tudbafbce2021-03-29 00:37:05 -0700624ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800625 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700626 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800627 bool, uint15_t, bool, uint8_t, uint8_t,
628 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700629{
630 std::string connection;
631 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700632 std::vector<std::string> interfaces;
633
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500634 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
635 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700636 if (status)
637 {
638 return ipmi::response(status);
639 }
640
Hao Jiangd2afd052020-12-10 15:09:32 -0800641 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700642 if (std::find(interfaces.begin(), interfaces.end(),
643 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700644 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700645 DbusInterfaceMap sensorMap;
646 if (!getSensorMap(ctx, connection, path, sensorMap))
647 {
648 return ipmi::responseResponseError();
649 }
650 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800651 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700652 {
653 return ipmi::responseResponseError();
654 }
655
Jie Yangf0a89942021-07-29 15:30:25 -0700656 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800657 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
658 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700659 {
660 return ipmi::responseResponseError();
661 }
662
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500663 auto value = sensor::calculateValue(reading, sensorMap,
664 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800665 if (!value)
666 {
667 return ipmi::responseResponseError();
668 }
669
670 if constexpr (debug)
671 {
672 phosphor::logging::log<phosphor::logging::level::INFO>(
673 "IPMI SET_SENSOR",
674 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
675 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
676 phosphor::logging::entry("VALUE=%f", *value));
677 }
678
679 boost::system::error_code ec =
680 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
681 "Value", ipmi::Value(*value));
682
683 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500684 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800685 // callback functions for now (e.g. ipmiSetSensorReading).
686 if (ec)
687 {
688 using namespace phosphor::logging;
689 log<level::ERR>("Failed to set property",
690 entry("PROPERTY=%s", "Value"),
691 entry("PATH=%s", path.c_str()),
692 entry("INTERFACE=%s", sensor::sensorInterface),
693 entry("WHAT=%s", ec.message().c_str()));
694 return ipmi::responseResponseError();
695 }
696 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700697 }
698
Hao Jiange39d4d82021-04-16 17:02:40 -0700699 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
700 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700701 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700702 DbusInterfaceMap sensorMap;
703 if (!getSensorMap(ctx, connection, path, sensorMap))
704 {
705 return ipmi::responseResponseError();
706 }
707 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800708 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700709 {
710 return ipmi::responseResponseError();
711 }
712
Hao Jiangd2afd052020-12-10 15:09:32 -0800713 // VR sensors are treated as a special case and we will not check the
714 // write permission for VR sensors, since they always deemed writable
715 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500716 auto vrMode = sensor::calculateVRMode(assertOffset,
717 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800718 if (!vrMode)
719 {
720 return ipmi::responseResponseError();
721 }
722 boost::system::error_code ec = setDbusProperty(
723 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
724 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500725 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800726 // callback functions for now (e.g. ipmiSetSensorReading).
727 if (ec)
728 {
729 using namespace phosphor::logging;
730 log<level::ERR>("Failed to set property",
731 entry("PROPERTY=%s", "Selected"),
732 entry("PATH=%s", path.c_str()),
733 entry("INTERFACE=%s", sensor::sensorInterface),
734 entry("WHAT=%s", ec.message().c_str()));
735 return ipmi::responseResponseError();
736 }
737 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700738 }
739
Hao Jiangd2afd052020-12-10 15:09:32 -0800740 phosphor::logging::log<phosphor::logging::level::ERR>(
741 "unknown sensor type",
742 phosphor::logging::entry("PATH=%s", path.c_str()));
743 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700744}
745
Willy Tude54f482021-01-26 15:59:09 -0800746ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
747 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
748{
749 std::string connection;
750 std::string path;
751
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000752 if (sensnum == reservedSensorNumber)
753 {
754 return ipmi::responseInvalidFieldRequest();
755 }
756
Willy Tude54f482021-01-26 15:59:09 -0800757 auto status = getSensorConnection(ctx, sensnum, connection, path);
758 if (status)
759 {
760 return ipmi::response(status);
761 }
762
Scron Chang2703b022021-07-06 15:47:45 +0800763#ifdef FEATURE_HYBRID_SENSORS
764 if (auto sensor = findStaticSensor(path);
765 sensor != ipmi::sensor::sensors.end() &&
766 getSensorEventTypeFromPath(path) !=
767 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
768 {
769 if (ipmi::sensor::Mutability::Read !=
770 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
771 {
772 return ipmi::responseIllegalCommand();
773 }
774
775 uint8_t operation;
776 try
777 {
778 ipmi::sensor::GetSensorResponse getResponse =
779 sensor->second.getFunc(sensor->second);
780
781 if (getResponse.readingOrStateUnavailable)
782 {
783 operation |= static_cast<uint8_t>(
784 IPMISensorReadingByte2::readingStateUnavailable);
785 }
786 if (getResponse.scanningEnabled)
787 {
788 operation |= static_cast<uint8_t>(
789 IPMISensorReadingByte2::sensorScanningEnable);
790 }
791 if (getResponse.allEventMessagesEnabled)
792 {
793 operation |= static_cast<uint8_t>(
794 IPMISensorReadingByte2::eventMessagesEnable);
795 }
796 return ipmi::responseSuccess(
797 getResponse.reading, operation,
798 getResponse.thresholdLevelsStates,
799 getResponse.discreteReadingSensorStates);
800 }
801 catch (const std::exception& e)
802 {
803 operation |= static_cast<uint8_t>(
804 IPMISensorReadingByte2::readingStateUnavailable);
805 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
806 }
807 }
808#endif
809
Willy Tude54f482021-01-26 15:59:09 -0800810 DbusInterfaceMap sensorMap;
811 if (!getSensorMap(ctx, connection, path, sensorMap))
812 {
813 return ipmi::responseResponseError();
814 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800815 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800816
817 if (sensorObject == sensorMap.end() ||
818 sensorObject->second.find("Value") == sensorObject->second.end())
819 {
820 return ipmi::responseResponseError();
821 }
822 auto& valueVariant = sensorObject->second["Value"];
823 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
824
825 double max = 0;
826 double min = 0;
827 getSensorMaxMin(sensorMap, max, min);
828
829 int16_t mValue = 0;
830 int16_t bValue = 0;
831 int8_t rExp = 0;
832 int8_t bExp = 0;
833 bool bSigned = false;
834
835 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
836 {
837 return ipmi::responseResponseError();
838 }
839
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500840 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
841 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800842 uint8_t operation =
843 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
844 operation |=
845 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
846 bool notReading = std::isnan(reading);
847
848 if (!notReading)
849 {
850 auto availableObject =
851 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
852 if (availableObject != sensorMap.end())
853 {
854 auto findAvailable = availableObject->second.find("Available");
855 if (findAvailable != availableObject->second.end())
856 {
857 bool* available = std::get_if<bool>(&(findAvailable->second));
858 if (available && !(*available))
859 {
860 notReading = true;
861 }
862 }
863 }
864 }
865
866 if (notReading)
867 {
868 operation |= static_cast<uint8_t>(
869 IPMISensorReadingByte2::readingStateUnavailable);
870 }
871
Josh Lehana55c9532020-10-28 21:59:06 -0700872 if constexpr (details::enableInstrumentation)
873 {
874 int byteValue;
875 if (bSigned)
876 {
877 byteValue = static_cast<int>(static_cast<int8_t>(value));
878 }
879 else
880 {
881 byteValue = static_cast<int>(static_cast<uint8_t>(value));
882 }
883
884 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800885 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
886 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700887 {
888 // This is the first reading, show the coefficients
889 double step = (max - min) / 255.0;
890 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800891 << details::sdrStatsTable.getName((ctx->lun << 8) |
892 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700893 << ": Range min=" << min << " max=" << max
894 << ", step=" << step
895 << ", Coefficients mValue=" << static_cast<int>(mValue)
896 << " rExp=" << static_cast<int>(rExp)
897 << " bValue=" << static_cast<int>(bValue)
898 << " bExp=" << static_cast<int>(bExp)
899 << " bSigned=" << static_cast<int>(bSigned) << "\n";
900 }
901 }
902
Willy Tude54f482021-01-26 15:59:09 -0800903 uint8_t thresholds = 0;
904
905 auto warningObject =
906 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
907 if (warningObject != sensorMap.end())
908 {
909 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
910 auto alarmLow = warningObject->second.find("WarningAlarmLow");
911 if (alarmHigh != warningObject->second.end())
912 {
913 if (std::get<bool>(alarmHigh->second))
914 {
915 thresholds |= static_cast<uint8_t>(
916 IPMISensorReadingByte3::upperNonCritical);
917 }
918 }
919 if (alarmLow != warningObject->second.end())
920 {
921 if (std::get<bool>(alarmLow->second))
922 {
923 thresholds |= static_cast<uint8_t>(
924 IPMISensorReadingByte3::lowerNonCritical);
925 }
926 }
927 }
928
929 auto criticalObject =
930 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
931 if (criticalObject != sensorMap.end())
932 {
933 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
934 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
935 if (alarmHigh != criticalObject->second.end())
936 {
937 if (std::get<bool>(alarmHigh->second))
938 {
939 thresholds |=
940 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
941 }
942 }
943 if (alarmLow != criticalObject->second.end())
944 {
945 if (std::get<bool>(alarmLow->second))
946 {
947 thresholds |=
948 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
949 }
950 }
951 }
952
953 // no discrete as of today so optional byte is never returned
954 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
955}
956
957/** @brief implements the Set Sensor threshold command
958 * @param sensorNumber - sensor number
959 * @param lowerNonCriticalThreshMask
960 * @param lowerCriticalThreshMask
961 * @param lowerNonRecovThreshMask
962 * @param upperNonCriticalThreshMask
963 * @param upperCriticalThreshMask
964 * @param upperNonRecovThreshMask
965 * @param reserved
966 * @param lowerNonCritical - lower non-critical threshold
967 * @param lowerCritical - Lower critical threshold
968 * @param lowerNonRecoverable - Lower non recovarable threshold
969 * @param upperNonCritical - Upper non-critical threshold
970 * @param upperCritical - Upper critical
971 * @param upperNonRecoverable - Upper Non-recoverable
972 *
973 * @returns IPMI completion code
974 */
975ipmi::RspType<> ipmiSenSetSensorThresholds(
976 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
977 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
978 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
979 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800980 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800981 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800982 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800983{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000984 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800985 {
986 return ipmi::responseInvalidFieldRequest();
987 }
988
989 // lower nc and upper nc not suppported on any sensor
990 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
991 {
992 return ipmi::responseInvalidFieldRequest();
993 }
994
995 // if none of the threshold mask are set, nothing to do
996 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
997 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
998 upperCriticalThreshMask | upperNonRecovThreshMask))
999 {
1000 return ipmi::responseSuccess();
1001 }
1002
1003 std::string connection;
1004 std::string path;
1005
1006 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1007 if (status)
1008 {
1009 return ipmi::response(status);
1010 }
1011 DbusInterfaceMap sensorMap;
1012 if (!getSensorMap(ctx, connection, path, sensorMap))
1013 {
1014 return ipmi::responseResponseError();
1015 }
1016
1017 double max = 0;
1018 double min = 0;
1019 getSensorMaxMin(sensorMap, max, min);
1020
1021 int16_t mValue = 0;
1022 int16_t bValue = 0;
1023 int8_t rExp = 0;
1024 int8_t bExp = 0;
1025 bool bSigned = false;
1026
1027 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1028 {
1029 return ipmi::responseResponseError();
1030 }
1031
1032 // store a vector of property name, value to set, and interface
1033 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1034
1035 // define the indexes of the tuple
1036 constexpr uint8_t propertyName = 0;
1037 constexpr uint8_t thresholdValue = 1;
1038 constexpr uint8_t interface = 2;
1039 // verifiy all needed fields are present
1040 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1041 {
1042 auto findThreshold =
1043 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1044 if (findThreshold == sensorMap.end())
1045 {
1046 return ipmi::responseInvalidFieldRequest();
1047 }
1048 if (lowerCriticalThreshMask)
1049 {
1050 auto findLower = findThreshold->second.find("CriticalLow");
1051 if (findLower == findThreshold->second.end())
1052 {
1053 return ipmi::responseInvalidFieldRequest();
1054 }
1055 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1056 findThreshold->first);
1057 }
1058 if (upperCriticalThreshMask)
1059 {
1060 auto findUpper = findThreshold->second.find("CriticalHigh");
1061 if (findUpper == findThreshold->second.end())
1062 {
1063 return ipmi::responseInvalidFieldRequest();
1064 }
1065 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1066 findThreshold->first);
1067 }
1068 }
1069 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1070 {
1071 auto findThreshold =
1072 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1073 if (findThreshold == sensorMap.end())
1074 {
1075 return ipmi::responseInvalidFieldRequest();
1076 }
1077 if (lowerNonCriticalThreshMask)
1078 {
1079 auto findLower = findThreshold->second.find("WarningLow");
1080 if (findLower == findThreshold->second.end())
1081 {
1082 return ipmi::responseInvalidFieldRequest();
1083 }
1084 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1085 findThreshold->first);
1086 }
1087 if (upperNonCriticalThreshMask)
1088 {
1089 auto findUpper = findThreshold->second.find("WarningHigh");
1090 if (findUpper == findThreshold->second.end())
1091 {
1092 return ipmi::responseInvalidFieldRequest();
1093 }
1094 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1095 findThreshold->first);
1096 }
1097 }
1098 for (const auto& property : thresholdsToSet)
1099 {
1100 // from section 36.3 in the IPMI Spec, assume all linear
1101 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1102 (bValue * std::pow(10.0, bExp))) *
1103 std::pow(10.0, rExp);
1104 setDbusProperty(
1105 *getSdBus(), connection, path, std::get<interface>(property),
1106 std::get<propertyName>(property), ipmi::Value(valueToSet));
1107 }
1108 return ipmi::responseSuccess();
1109}
1110
1111IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1112{
1113 IPMIThresholds resp;
1114 auto warningInterface =
1115 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1116 auto criticalInterface =
1117 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1118
1119 if ((warningInterface != sensorMap.end()) ||
1120 (criticalInterface != sensorMap.end()))
1121 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001122 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001123
1124 if (sensorPair == sensorMap.end())
1125 {
1126 // should not have been able to find a sensor not implementing
1127 // the sensor object
1128 throw std::runtime_error("Invalid sensor map");
1129 }
1130
1131 double max = 0;
1132 double min = 0;
1133 getSensorMaxMin(sensorMap, max, min);
1134
1135 int16_t mValue = 0;
1136 int16_t bValue = 0;
1137 int8_t rExp = 0;
1138 int8_t bExp = 0;
1139 bool bSigned = false;
1140
1141 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1142 {
1143 throw std::runtime_error("Invalid sensor atrributes");
1144 }
1145 if (warningInterface != sensorMap.end())
1146 {
1147 auto& warningMap = warningInterface->second;
1148
1149 auto warningHigh = warningMap.find("WarningHigh");
1150 auto warningLow = warningMap.find("WarningLow");
1151
1152 if (warningHigh != warningMap.end())
1153 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001154 double value = std::visit(VariantToDoubleVisitor(),
1155 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001156 if (std::isfinite(value))
1157 {
1158 resp.warningHigh = scaleIPMIValueFromDouble(
1159 value, mValue, rExp, bValue, bExp, bSigned);
1160 }
Willy Tude54f482021-01-26 15:59:09 -08001161 }
1162 if (warningLow != warningMap.end())
1163 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001164 double value = std::visit(VariantToDoubleVisitor(),
1165 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001166 if (std::isfinite(value))
1167 {
1168 resp.warningLow = scaleIPMIValueFromDouble(
1169 value, mValue, rExp, bValue, bExp, bSigned);
1170 }
Willy Tude54f482021-01-26 15:59:09 -08001171 }
1172 }
1173 if (criticalInterface != sensorMap.end())
1174 {
1175 auto& criticalMap = criticalInterface->second;
1176
1177 auto criticalHigh = criticalMap.find("CriticalHigh");
1178 auto criticalLow = criticalMap.find("CriticalLow");
1179
1180 if (criticalHigh != criticalMap.end())
1181 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001182 double value = std::visit(VariantToDoubleVisitor(),
1183 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001184 if (std::isfinite(value))
1185 {
1186 resp.criticalHigh = scaleIPMIValueFromDouble(
1187 value, mValue, rExp, bValue, bExp, bSigned);
1188 }
Willy Tude54f482021-01-26 15:59:09 -08001189 }
1190 if (criticalLow != criticalMap.end())
1191 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001192 double value = std::visit(VariantToDoubleVisitor(),
1193 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001194 if (std::isfinite(value))
1195 {
1196 resp.criticalLow = scaleIPMIValueFromDouble(
1197 value, mValue, rExp, bValue, bExp, bSigned);
1198 }
Willy Tude54f482021-01-26 15:59:09 -08001199 }
1200 }
1201 }
1202 return resp;
1203}
1204
1205ipmi::RspType<uint8_t, // readable
1206 uint8_t, // lowerNCrit
1207 uint8_t, // lowerCrit
1208 uint8_t, // lowerNrecoverable
1209 uint8_t, // upperNC
1210 uint8_t, // upperCrit
1211 uint8_t> // upperNRecoverable
1212 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1213{
1214 std::string connection;
1215 std::string path;
1216
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001217 if (sensorNumber == reservedSensorNumber)
1218 {
1219 return ipmi::responseInvalidFieldRequest();
1220 }
1221
Willy Tude54f482021-01-26 15:59:09 -08001222 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1223 if (status)
1224 {
1225 return ipmi::response(status);
1226 }
1227
1228 DbusInterfaceMap sensorMap;
1229 if (!getSensorMap(ctx, connection, path, sensorMap))
1230 {
1231 return ipmi::responseResponseError();
1232 }
1233
1234 IPMIThresholds thresholdData;
1235 try
1236 {
1237 thresholdData = getIPMIThresholds(sensorMap);
1238 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001239 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001240 {
1241 return ipmi::responseResponseError();
1242 }
1243
1244 uint8_t readable = 0;
1245 uint8_t lowerNC = 0;
1246 uint8_t lowerCritical = 0;
1247 uint8_t lowerNonRecoverable = 0;
1248 uint8_t upperNC = 0;
1249 uint8_t upperCritical = 0;
1250 uint8_t upperNonRecoverable = 0;
1251
1252 if (thresholdData.warningHigh)
1253 {
1254 readable |=
1255 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1256 upperNC = *thresholdData.warningHigh;
1257 }
1258 if (thresholdData.warningLow)
1259 {
1260 readable |=
1261 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1262 lowerNC = *thresholdData.warningLow;
1263 }
1264
1265 if (thresholdData.criticalHigh)
1266 {
1267 readable |=
1268 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1269 upperCritical = *thresholdData.criticalHigh;
1270 }
1271 if (thresholdData.criticalLow)
1272 {
1273 readable |=
1274 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1275 lowerCritical = *thresholdData.criticalLow;
1276 }
1277
1278 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1279 lowerNonRecoverable, upperNC, upperCritical,
1280 upperNonRecoverable);
1281}
1282
1283/** @brief implements the get Sensor event enable command
1284 * @param sensorNumber - sensor number
1285 *
1286 * @returns IPMI completion code plus response data
1287 * - enabled - Sensor Event messages
1288 * - assertionEnabledLsb - Assertion event messages
1289 * - assertionEnabledMsb - Assertion event messages
1290 * - deassertionEnabledLsb - Deassertion event messages
1291 * - deassertionEnabledMsb - Deassertion event messages
1292 */
1293
1294ipmi::RspType<uint8_t, // enabled
1295 uint8_t, // assertionEnabledLsb
1296 uint8_t, // assertionEnabledMsb
1297 uint8_t, // deassertionEnabledLsb
1298 uint8_t> // deassertionEnabledMsb
1299 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1300{
1301 std::string connection;
1302 std::string path;
1303
1304 uint8_t enabled = 0;
1305 uint8_t assertionEnabledLsb = 0;
1306 uint8_t assertionEnabledMsb = 0;
1307 uint8_t deassertionEnabledLsb = 0;
1308 uint8_t deassertionEnabledMsb = 0;
1309
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001310 if (sensorNum == reservedSensorNumber)
1311 {
1312 return ipmi::responseInvalidFieldRequest();
1313 }
1314
Willy Tude54f482021-01-26 15:59:09 -08001315 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1316 if (status)
1317 {
1318 return ipmi::response(status);
1319 }
1320
Scron Chang2703b022021-07-06 15:47:45 +08001321#ifdef FEATURE_HYBRID_SENSORS
1322 if (auto sensor = findStaticSensor(path);
1323 sensor != ipmi::sensor::sensors.end() &&
1324 getSensorEventTypeFromPath(path) !=
1325 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1326 {
1327 enabled = static_cast<uint8_t>(
1328 IPMISensorEventEnableByte2::sensorScanningEnable);
1329 uint16_t assertionEnabled = 0;
1330 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1331 ->second.begin()
1332 ->second.second)
1333 {
1334 assertionEnabled |= (1 << offsetValMap.first);
1335 }
1336 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1337 assertionEnabledMsb =
1338 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1339
1340 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1341 assertionEnabledMsb, deassertionEnabledLsb,
1342 deassertionEnabledMsb);
1343 }
1344#endif
1345
Willy Tude54f482021-01-26 15:59:09 -08001346 DbusInterfaceMap sensorMap;
1347 if (!getSensorMap(ctx, connection, path, sensorMap))
1348 {
1349 return ipmi::responseResponseError();
1350 }
1351
1352 auto warningInterface =
1353 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1354 auto criticalInterface =
1355 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1356 if ((warningInterface != sensorMap.end()) ||
1357 (criticalInterface != sensorMap.end()))
1358 {
1359 enabled = static_cast<uint8_t>(
1360 IPMISensorEventEnableByte2::sensorScanningEnable);
1361 if (warningInterface != sensorMap.end())
1362 {
1363 auto& warningMap = warningInterface->second;
1364
1365 auto warningHigh = warningMap.find("WarningHigh");
1366 auto warningLow = warningMap.find("WarningLow");
1367 if (warningHigh != warningMap.end())
1368 {
1369 assertionEnabledLsb |= static_cast<uint8_t>(
1370 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1371 deassertionEnabledLsb |= static_cast<uint8_t>(
1372 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1373 }
1374 if (warningLow != warningMap.end())
1375 {
1376 assertionEnabledLsb |= static_cast<uint8_t>(
1377 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1378 deassertionEnabledLsb |= static_cast<uint8_t>(
1379 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1380 }
1381 }
1382 if (criticalInterface != sensorMap.end())
1383 {
1384 auto& criticalMap = criticalInterface->second;
1385
1386 auto criticalHigh = criticalMap.find("CriticalHigh");
1387 auto criticalLow = criticalMap.find("CriticalLow");
1388
1389 if (criticalHigh != criticalMap.end())
1390 {
1391 assertionEnabledMsb |= static_cast<uint8_t>(
1392 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1393 deassertionEnabledMsb |= static_cast<uint8_t>(
1394 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1395 }
1396 if (criticalLow != criticalMap.end())
1397 {
1398 assertionEnabledLsb |= static_cast<uint8_t>(
1399 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1400 deassertionEnabledLsb |= static_cast<uint8_t>(
1401 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1402 }
1403 }
1404 }
1405
1406 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1407 assertionEnabledMsb, deassertionEnabledLsb,
1408 deassertionEnabledMsb);
1409}
1410
1411/** @brief implements the get Sensor event status command
1412 * @param sensorNumber - sensor number, FFh = reserved
1413 *
1414 * @returns IPMI completion code plus response data
1415 * - sensorEventStatus - Sensor Event messages state
1416 * - assertions - Assertion event messages
1417 * - deassertions - Deassertion event messages
1418 */
1419ipmi::RspType<uint8_t, // sensorEventStatus
1420 std::bitset<16>, // assertions
1421 std::bitset<16> // deassertion
1422 >
1423 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1424{
1425 if (sensorNum == reservedSensorNumber)
1426 {
1427 return ipmi::responseInvalidFieldRequest();
1428 }
1429
1430 std::string connection;
1431 std::string path;
1432 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1433 if (status)
1434 {
1435 phosphor::logging::log<phosphor::logging::level::ERR>(
1436 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1437 phosphor::logging::entry("SENSOR=%d", sensorNum));
1438 return ipmi::response(status);
1439 }
1440
Scron Chang2703b022021-07-06 15:47:45 +08001441#ifdef FEATURE_HYBRID_SENSORS
1442 if (auto sensor = findStaticSensor(path);
1443 sensor != ipmi::sensor::sensors.end() &&
1444 getSensorEventTypeFromPath(path) !=
1445 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1446 {
1447 auto response = ipmi::sensor::get::mapDbusToAssertion(
1448 sensor->second, path, sensor->second.sensorInterface);
1449 std::bitset<16> assertions;
1450 // deassertions are not used.
1451 std::bitset<16> deassertions = 0;
1452 uint8_t sensorEventStatus;
1453 if (response.readingOrStateUnavailable)
1454 {
1455 sensorEventStatus |= static_cast<uint8_t>(
1456 IPMISensorReadingByte2::readingStateUnavailable);
1457 }
1458 if (response.scanningEnabled)
1459 {
1460 sensorEventStatus |= static_cast<uint8_t>(
1461 IPMISensorReadingByte2::sensorScanningEnable);
1462 }
1463 if (response.allEventMessagesEnabled)
1464 {
1465 sensorEventStatus |= static_cast<uint8_t>(
1466 IPMISensorReadingByte2::eventMessagesEnable);
1467 }
1468 assertions |= response.discreteReadingSensorStates << 8;
1469 assertions |= response.thresholdLevelsStates;
1470 return ipmi::responseSuccess(sensorEventStatus, assertions,
1471 deassertions);
1472 }
1473#endif
1474
Willy Tude54f482021-01-26 15:59:09 -08001475 DbusInterfaceMap sensorMap;
1476 if (!getSensorMap(ctx, connection, path, sensorMap))
1477 {
1478 phosphor::logging::log<phosphor::logging::level::ERR>(
1479 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1480 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1481 return ipmi::responseResponseError();
1482 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001483
1484 uint8_t sensorEventStatus =
1485 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1486 std::bitset<16> assertions = 0;
1487 std::bitset<16> deassertions = 0;
1488
1489 // handle VR typed sensor
1490 auto vrInterface = sensorMap.find(sensor::vrInterface);
1491 if (vrInterface != sensorMap.end())
1492 {
1493 if (!sensor::getVrEventStatus(ctx, connection, path,
1494 vrInterface->second, assertions))
1495 {
1496 return ipmi::responseResponseError();
1497 }
1498
1499 // both Event Message and Sensor Scanning are disable for VR.
1500 sensorEventStatus = 0;
1501 return ipmi::responseSuccess(sensorEventStatus, assertions,
1502 deassertions);
1503 }
1504
Willy Tude54f482021-01-26 15:59:09 -08001505 auto warningInterface =
1506 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1507 auto criticalInterface =
1508 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1509
Willy Tude54f482021-01-26 15:59:09 -08001510 std::optional<bool> criticalDeassertHigh =
1511 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1512 std::optional<bool> criticalDeassertLow =
1513 thresholdDeassertMap[path]["CriticalAlarmLow"];
1514 std::optional<bool> warningDeassertHigh =
1515 thresholdDeassertMap[path]["WarningAlarmHigh"];
1516 std::optional<bool> warningDeassertLow =
1517 thresholdDeassertMap[path]["WarningAlarmLow"];
1518
Willy Tude54f482021-01-26 15:59:09 -08001519 if (criticalDeassertHigh && !*criticalDeassertHigh)
1520 {
1521 deassertions.set(static_cast<size_t>(
1522 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1523 }
1524 if (criticalDeassertLow && !*criticalDeassertLow)
1525 {
1526 deassertions.set(static_cast<size_t>(
1527 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1528 }
1529 if (warningDeassertHigh && !*warningDeassertHigh)
1530 {
1531 deassertions.set(static_cast<size_t>(
1532 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1533 }
1534 if (warningDeassertLow && !*warningDeassertLow)
1535 {
1536 deassertions.set(static_cast<size_t>(
1537 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1538 }
1539 if ((warningInterface != sensorMap.end()) ||
1540 (criticalInterface != sensorMap.end()))
1541 {
1542 sensorEventStatus = static_cast<size_t>(
1543 IPMISensorEventEnableByte2::eventMessagesEnable);
1544 if (warningInterface != sensorMap.end())
1545 {
1546 auto& warningMap = warningInterface->second;
1547
1548 auto warningHigh = warningMap.find("WarningAlarmHigh");
1549 auto warningLow = warningMap.find("WarningAlarmLow");
1550 auto warningHighAlarm = false;
1551 auto warningLowAlarm = false;
1552
1553 if (warningHigh != warningMap.end())
1554 {
1555 warningHighAlarm = std::get<bool>(warningHigh->second);
1556 }
1557 if (warningLow != warningMap.end())
1558 {
1559 warningLowAlarm = std::get<bool>(warningLow->second);
1560 }
1561 if (warningHighAlarm)
1562 {
1563 assertions.set(
1564 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1565 upperNonCriticalGoingHigh));
1566 }
1567 if (warningLowAlarm)
1568 {
1569 assertions.set(
1570 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1571 lowerNonCriticalGoingLow));
1572 }
1573 }
1574 if (criticalInterface != sensorMap.end())
1575 {
1576 auto& criticalMap = criticalInterface->second;
1577
1578 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1579 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1580 auto criticalHighAlarm = false;
1581 auto criticalLowAlarm = false;
1582
1583 if (criticalHigh != criticalMap.end())
1584 {
1585 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1586 }
1587 if (criticalLow != criticalMap.end())
1588 {
1589 criticalLowAlarm = std::get<bool>(criticalLow->second);
1590 }
1591 if (criticalHighAlarm)
1592 {
1593 assertions.set(
1594 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1595 upperCriticalGoingHigh));
1596 }
1597 if (criticalLowAlarm)
1598 {
1599 assertions.set(static_cast<size_t>(
1600 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1601 }
1602 }
1603 }
1604
1605 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1606}
1607
Willy Tu38e7a2b2021-03-29 15:09:56 -07001608// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001609void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1610 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001611{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001612 get_sdr::header::set_record_id(
1613 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1614
Willy Tu38e7a2b2021-03-29 15:09:56 -07001615 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1616 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1617
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001618 record.header.sdr_version = ipmiSdrVersion;
1619 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1620 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1621 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001622 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001623 record.key.owner_lun = lun;
1624 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001625}
Willy Tu4eca2512022-06-20 21:14:51 -07001626bool constructSensorSdr(
1627 ipmi::Context::ptr ctx,
1628 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1629 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1630 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001631{
Hao Jiange39d4d82021-04-16 17:02:40 -07001632 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1633
1634 DbusInterfaceMap sensorMap;
1635 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1636 {
1637 phosphor::logging::log<phosphor::logging::level::ERR>(
1638 "Failed to update sensor map for threshold sensor",
1639 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1640 phosphor::logging::entry("PATH=%s", path.c_str()));
1641 return false;
1642 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001643
1644 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1645 record.body.sensor_type = getSensorTypeFromPath(path);
1646 std::string type = getSensorTypeStringFromPath(path);
1647 auto typeCstr = type.c_str();
1648 auto findUnits = sensorUnits.find(typeCstr);
1649 if (findUnits != sensorUnits.end())
1650 {
1651 record.body.sensor_units_2_base =
1652 static_cast<uint8_t>(findUnits->second);
1653 } // else default 0x0 unspecified
1654
1655 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1656
Hao Jiangd2afd052020-12-10 15:09:32 -08001657 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001658 if (sensorObject == sensorMap.end())
1659 {
1660 phosphor::logging::log<phosphor::logging::level::ERR>(
1661 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001662 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001663 }
1664
1665 uint8_t entityId = 0;
1666 uint8_t entityInstance = 0x01;
1667
1668 // follow the association chain to get the parent board's entityid and
1669 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001670 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1671 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001672
1673 record.body.entity_id = entityId;
1674 record.body.entity_instance = entityInstance;
1675
Shakeeb Pasha93889722021-10-14 10:20:13 +05301676 double max = 0;
1677 double min = 0;
1678 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001679
1680 int16_t mValue = 0;
1681 int8_t rExp = 0;
1682 int16_t bValue = 0;
1683 int8_t bExp = 0;
1684 bool bSigned = false;
1685
1686 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1687 {
1688 phosphor::logging::log<phosphor::logging::level::ERR>(
1689 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001690 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001691 }
1692
1693 // The record.body is a struct SensorDataFullRecordBody
1694 // from sensorhandler.hpp in phosphor-ipmi-host.
1695 // The meaning of these bits appears to come from
1696 // table 43.1 of the IPMI spec.
1697 // The above 5 sensor attributes are stuffed in as follows:
1698 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1699 // Byte 22-24 are for other purposes
1700 // Byte 25 = MMMMMMMM = LSB of M
1701 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1702 // Byte 27 = BBBBBBBB = LSB of B
1703 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1704 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1705 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1706
1707 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1708 record.body.m_lsb = mValue & 0xFF;
1709
1710 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1711 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1712
1713 // move the smallest bit of the MSB into place (bit 9)
1714 // the MSbs are bits 7:8 in m_msb_and_tolerance
1715 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1716
1717 record.body.b_lsb = bValue & 0xFF;
1718
1719 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1720 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1721
1722 // move the smallest bit of the MSB into place (bit 9)
1723 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1724 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1725
1726 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1727 uint8_t rExpBits = rExp & 0x07;
1728
1729 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1730 uint8_t bExpBits = bExp & 0x07;
1731
1732 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001733 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1734 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001735
1736 // Set the analog reading byte interpretation accordingly
1737 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1738
1739 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1740 // These seem redundant, but derivable from the above 5 attributes
1741 // Original comment said "todo fill out rest of units"
1742
1743 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001744 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001745 get_sdr::body::set_id_strlen(name.size(), &record.body);
1746 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001747 std::strncpy(record.body.id_string, name.c_str(),
1748 sizeof(record.body.id_string));
1749
Josh Lehana55c9532020-10-28 21:59:06 -07001750 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001751 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001752
Jie Yangf0a89942021-07-29 15:30:25 -07001753 bool sensorSettable = false;
1754 auto mutability =
1755 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1756 if (mutability != sensorMap.end())
1757 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001758 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1759 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001760 }
1761 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1762
1763 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001764 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001765
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001766 IPMIThresholds thresholdData;
1767 try
1768 {
1769 thresholdData = getIPMIThresholds(sensorMap);
1770 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001771 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001772 {
1773 phosphor::logging::log<phosphor::logging::level::ERR>(
1774 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001775 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001776 }
1777
1778 if (thresholdData.criticalHigh)
1779 {
1780 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1781 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1782 IPMISensorEventEnableThresholds::criticalThreshold);
1783 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1784 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1785 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1786 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1787 record.body.discrete_reading_setting_mask[0] |=
1788 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1789 }
1790 if (thresholdData.warningHigh)
1791 {
1792 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1793 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1794 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1795 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1796 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1797 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1798 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1799 record.body.discrete_reading_setting_mask[0] |=
1800 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1801 }
1802 if (thresholdData.criticalLow)
1803 {
1804 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1805 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1806 IPMISensorEventEnableThresholds::criticalThreshold);
1807 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1808 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1809 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1810 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1811 record.body.discrete_reading_setting_mask[0] |=
1812 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1813 }
1814 if (thresholdData.warningLow)
1815 {
1816 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1817 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1818 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1819 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1820 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1821 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1822 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1823 record.body.discrete_reading_setting_mask[0] |=
1824 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1825 }
1826
1827 // everything that is readable is setable
1828 record.body.discrete_reading_setting_mask[1] =
1829 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001830 return true;
1831}
1832
Scron Chang2703b022021-07-06 15:47:45 +08001833#ifdef FEATURE_HYBRID_SENSORS
1834// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001835void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001836 uint16_t recordID,
1837 ipmi::sensor::IdInfoMap::const_iterator sensor,
1838 get_sdr::SensorDataFullRecord& record)
1839{
1840 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1841
1842 record.body.entity_id = sensor->second.entityType;
1843 record.body.sensor_type = sensor->second.sensorType;
1844 record.body.event_reading_type = sensor->second.sensorReadingType;
1845 record.body.entity_instance = sensor->second.instance;
1846 if (ipmi::sensor::Mutability::Write ==
1847 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1848 {
1849 get_sdr::body::init_settable_state(true, &(record.body));
1850 }
1851
1852 auto id_string = sensor->second.sensorName;
1853
1854 if (id_string.empty())
1855 {
1856 id_string = sensor->second.sensorNameFunc(sensor->second);
1857 }
1858
1859 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1860 {
1861 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1862 &(record.body));
1863 }
1864 else
1865 {
1866 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1867 }
Paul Fertser51136982022-08-18 12:36:41 +00001868 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08001869 std::strncpy(record.body.id_string, id_string.c_str(),
1870 get_sdr::body::get_id_strlen(&(record.body)));
1871}
1872#endif
1873
Hao Jiange39d4d82021-04-16 17:02:40 -07001874// Construct type 3 SDR header and key (for VR and other discrete sensors)
1875void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1876 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001877{
1878 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1879 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1880
1881 get_sdr::header::set_record_id(
1882 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1883
1884 record.header.sdr_version = ipmiSdrVersion;
1885 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1886 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1887 sizeof(get_sdr::SensorDataRecordHeader);
1888 record.key.owner_id = bmcI2CAddr;
1889 record.key.owner_lun = lun;
1890 record.key.sensor_number = sensornumber;
1891
1892 record.body.entity_id = 0x00;
1893 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001894}
Willy Tu61992ad2021-03-29 15:33:20 -07001895
Hao Jiange39d4d82021-04-16 17:02:40 -07001896// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001897bool constructVrSdr(ipmi::Context::ptr ctx,
1898 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1899 uint16_t sensorNum, uint16_t recordID,
1900 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001901 get_sdr::SensorDataEventRecord& record)
1902{
Hao Jiange39d4d82021-04-16 17:02:40 -07001903 constructEventSdrHeaderKey(sensorNum, recordID, record);
1904
1905 DbusInterfaceMap sensorMap;
1906 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1907 {
1908 phosphor::logging::log<phosphor::logging::level::ERR>(
1909 "Failed to update sensor map for VR sensor",
1910 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1911 phosphor::logging::entry("PATH=%s", path.c_str()));
1912 return false;
1913 }
Willy Tu61992ad2021-03-29 15:33:20 -07001914 // follow the association chain to get the parent board's entityid and
1915 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001916 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1917 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001918 record.body.entity_instance);
1919
1920 // Sensor type is hardcoded as a module/board type instead of parsing from
1921 // sensor path. This is because VR control is allocated in an independent
1922 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1923 // types.
1924 static constexpr const uint8_t module_board_type = 0x15;
1925 record.body.sensor_type = module_board_type;
1926 record.body.event_reading_type = 0x00;
1927
1928 record.body.sensor_record_sharing_1 = 0x00;
1929 record.body.sensor_record_sharing_2 = 0x00;
1930
1931 // populate sensor name from path
1932 auto name = sensor::parseSdrIdFromPath(path);
1933 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00001934 get_sdr::body::set_id_strlen(nameSize, &record.body);
1935 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07001936 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1937 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1938
1939 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001940 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001941
1942 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001943}
1944
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001945static inline uint16_t getNumberOfSensors()
1946{
1947 return std::min(getSensorTree().size(), maxIPMISensors);
1948}
1949
Willy Tu4eca2512022-06-20 21:14:51 -07001950static int getSensorDataRecord(
1951 ipmi::Context::ptr ctx,
1952 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1953 std::vector<uint8_t>& recordData, uint16_t recordID,
1954 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001955{
1956 size_t fruCount = 0;
1957 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1958 if (ret != ipmi::ccSuccess)
1959 {
1960 phosphor::logging::log<phosphor::logging::level::ERR>(
1961 "getSensorDataRecord: getFruSdrCount error");
1962 return GENERAL_ERROR;
1963 }
1964
Harvey Wu05d17c02021-09-15 08:46:59 +08001965 const auto& entityRecords =
1966 ipmi::sensor::EntityInfoMapContainer::getContainer()
1967 ->getIpmiEntityRecords();
1968 size_t entityCount = entityRecords.size();
1969
selvaganapathi7b2e5502023-02-14 07:10:47 +05301970 recordData.clear();
Harvey Wu05d17c02021-09-15 08:46:59 +08001971 size_t lastRecord = getNumberOfSensors() + fruCount +
1972 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001973 if (recordID == lastRecordIndex)
1974 {
1975 recordID = lastRecord;
1976 }
1977 if (recordID > lastRecord)
1978 {
1979 phosphor::logging::log<phosphor::logging::level::ERR>(
1980 "getSensorDataRecord: recordID > lastRecord error");
1981 return GENERAL_ERROR;
1982 }
1983
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001984 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001985 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001986 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001987
Harvey Wu05d17c02021-09-15 08:46:59 +08001988 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1989 {
1990 // handle type 8 entity map records
1991 ipmi::sensor::EntityInfoMap::const_iterator entity =
1992 entityRecords.find(static_cast<uint8_t>(
1993 sdrIndex - fruCount - ipmi::storage::type12Count));
1994 if (entity == entityRecords.end())
1995 {
1996 return IPMI_CC_SENSOR_INVALID;
1997 }
1998 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1999 }
2000 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07002001 {
2002 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08002003 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002004 if (type12Index >= ipmi::storage::type12Count)
2005 {
2006 phosphor::logging::log<phosphor::logging::level::ERR>(
2007 "getSensorDataRecord: type12Index error");
2008 return GENERAL_ERROR;
2009 }
2010 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
2011 }
2012 else
2013 {
2014 // handle fru records
2015 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08002016 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002017 if (ret != IPMI_CC_OK)
2018 {
2019 return GENERAL_ERROR;
2020 }
2021 data.header.record_id_msb = recordID >> 8;
2022 data.header.record_id_lsb = recordID & 0xFF;
selvaganapathi7b2e5502023-02-14 07:10:47 +05302023 recordData.insert(recordData.end(),
2024 reinterpret_cast<uint8_t*>(&data),
2025 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tu38e7a2b2021-03-29 15:09:56 -07002026 }
2027
2028 return 0;
2029 }
2030
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002031 // Perform a incremental scan of the SDR Record ID's and translate the
2032 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2033 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2034 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2035 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002036 std::string connection;
2037 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002038 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002039 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002040 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002041 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002042 // LUN 0 has one reserved sensor number. Compensate here by adding one
2043 // to the record ID
2044 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002045 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002046 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002047 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002048 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002049 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2050 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2051 // rules governing its use.
2052 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002053 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002054 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002055
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002056 auto status = getSensorConnection(ctx,
2057 static_cast<uint8_t>(sensNumFromRecID),
2058 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002059 if (status)
2060 {
2061 phosphor::logging::log<phosphor::logging::level::ERR>(
2062 "getSensorDataRecord: getSensorConnection error");
2063 return GENERAL_ERROR;
2064 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002065 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002066 // Return an error on LUN 2 assingments, and any sensor number beyond the
2067 // range of LUN 3
2068 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2069 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002070 {
2071 phosphor::logging::log<phosphor::logging::level::ERR>(
2072 "getSensorDataRecord: invalidSensorNumber");
2073 return GENERAL_ERROR;
2074 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002075 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2076 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2077
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002078 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2079 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002080 {
2081 phosphor::logging::log<phosphor::logging::level::ERR>(
2082 "getSensorDataRecord: sensor record mismatch");
2083 return GENERAL_ERROR;
2084 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002085
Willy Tu38e7a2b2021-03-29 15:09:56 -07002086 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002087 if (std::find(interfaces.begin(), interfaces.end(),
2088 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002089 {
Willy Tu11d68892022-01-20 10:37:34 -08002090 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002091
Hao Jiange39d4d82021-04-16 17:02:40 -07002092 // If the request doesn't read SDR body, construct only header and key
2093 // part to avoid additional DBus transaction.
2094 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2095 {
2096 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2097 }
Willy Tu4eca2512022-06-20 21:14:51 -07002098 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2099 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002100 {
2101 return GENERAL_ERROR;
2102 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002103
selvaganapathi7b2e5502023-02-14 07:10:47 +05302104 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2105 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002106
2107 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002108 }
Willy Tu61992ad2021-03-29 15:33:20 -07002109
Scron Chang2703b022021-07-06 15:47:45 +08002110#ifdef FEATURE_HYBRID_SENSORS
2111 if (auto sensor = findStaticSensor(path);
2112 sensor != ipmi::sensor::sensors.end() &&
2113 getSensorEventTypeFromPath(path) !=
2114 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2115 {
Willy Tu11d68892022-01-20 10:37:34 -08002116 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002117
2118 // If the request doesn't read SDR body, construct only header and key
2119 // part to avoid additional DBus transaction.
2120 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2121 {
2122 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2123 }
2124 else
2125 {
2126 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2127 }
2128
selvaganapathi7b2e5502023-02-14 07:10:47 +05302129 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2130 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002131
2132 return 0;
2133 }
2134#endif
2135
Willy Tu61992ad2021-03-29 15:33:20 -07002136 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002137 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2138 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002139 {
Willy Tu11d68892022-01-20 10:37:34 -08002140 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002141
Hao Jiange39d4d82021-04-16 17:02:40 -07002142 // If the request doesn't read SDR body, construct only header and key
2143 // part to avoid additional DBus transaction.
2144 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2145 {
2146 constructEventSdrHeaderKey(sensorNum, recordID, record);
2147 }
Willy Tu4eca2512022-06-20 21:14:51 -07002148 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2149 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002150 {
2151 return GENERAL_ERROR;
2152 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302153 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2154 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002155 }
2156
Willy Tude54f482021-01-26 15:59:09 -08002157 return 0;
2158}
2159
2160/** @brief implements the get SDR Info command
2161 * @param count - Operation
2162 *
2163 * @returns IPMI completion code plus response data
2164 * - sdrCount - sensor/SDR count
2165 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2166 */
2167static ipmi::RspType<uint8_t, // respcount
2168 uint8_t, // dynamic population flags
2169 uint32_t // last time a sensor was added
2170 >
2171 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2172 std::optional<uint8_t> count)
2173{
2174 auto& sensorTree = getSensorTree();
2175 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002176 uint16_t recordID = 0;
2177 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002178 // Sensors are dynamically allocated, and there is at least one LUN
2179 uint8_t lunsAndDynamicPopulation = 0x80;
2180 constexpr uint8_t getSdrCount = 0x01;
2181 constexpr uint8_t getSensorCount = 0x00;
2182
2183 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2184 {
2185 return ipmi::responseResponseError();
2186 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002187 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002188 if (count.value_or(0) == getSdrCount)
2189 {
Willy Tu4eca2512022-06-20 21:14:51 -07002190 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2191
Harvey Wu75893062023-03-22 17:17:31 +08002192 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002193 {
2194 recordID += maxSensorsPerLUN;
2195 }
Harvey Wu75893062023-03-22 17:17:31 +08002196 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002197 {
2198 recordID += maxSensorsPerLUN * 2;
2199 }
2200
Harvey Wu75893062023-03-22 17:17:31 +08002201 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2202 // assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002203 while (!getSensorDataRecord(
2204 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2205 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002206 {
2207 get_sdr::SensorDataRecordHeader* hdr =
2208 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002209 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302210 if (!hdr)
2211 {
2212 continue;
2213 }
2214
2215 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002216 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002217 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002218 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002219 record.data());
2220 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002221 {
2222 sdrCount++;
2223 }
2224 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302225 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2226 {
2227 get_sdr::SensorDataCompactRecord* recordData =
2228 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2229 record.data());
2230 if (ctx->lun == recordData->key.owner_lun)
2231 {
2232 sdrCount++;
2233 }
2234 }
Harvey Wua3476272023-03-22 10:09:38 +08002235 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2236 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302237 {
2238 sdrCount++;
2239 }
Harvey Wua3476272023-03-22 10:09:38 +08002240
2241 // Because response count data is 1 byte, so sdrCount need to avoid
2242 // overflow.
2243 if (sdrCount == maxSensorsPerLUN)
2244 {
2245 break;
2246 }
Willy Tude54f482021-01-26 15:59:09 -08002247 }
2248 }
2249 else if (count.value_or(0) == getSensorCount)
2250 {
2251 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002252 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002253 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002254 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2255 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002256 }
Harvey Wu75893062023-03-22 17:17:31 +08002257 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002258 {
2259 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2260 ? maxSensorsPerLUN
2261 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2262 }
Harvey Wu75893062023-03-22 17:17:31 +08002263 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002264 {
2265 if (numSensors <= maxIPMISensors)
2266 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002267 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2268 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002269 }
2270 else
2271 {
2272 // error
2273 throw std::out_of_range(
2274 "Maximum number of IPMI sensors exceeded.");
2275 }
2276 }
2277 }
2278 else
2279 {
2280 return ipmi::responseInvalidFieldRequest();
2281 }
2282
2283 // Get Sensor count. This returns the number of sensors
2284 if (numSensors > 0)
2285 {
2286 lunsAndDynamicPopulation |= 1;
2287 }
2288 if (numSensors > maxSensorsPerLUN)
2289 {
2290 lunsAndDynamicPopulation |= 2;
2291 }
2292 if (numSensors >= (maxSensorsPerLUN * 2))
2293 {
2294 lunsAndDynamicPopulation |= 8;
2295 }
2296 if (numSensors > maxIPMISensors)
2297 {
2298 // error
2299 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2300 }
2301
2302 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2303 sdrLastAdd);
2304}
2305
2306/* end sensor commands */
2307
2308/* storage commands */
2309
2310ipmi::RspType<uint8_t, // sdr version
2311 uint16_t, // record count
2312 uint16_t, // free space
2313 uint32_t, // most recent addition
2314 uint32_t, // most recent erase
2315 uint8_t // operationSupport
2316 >
2317 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2318{
2319 auto& sensorTree = getSensorTree();
2320 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002321 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002322 {
2323 return ipmi::responseResponseError();
2324 }
2325
2326 size_t fruCount = 0;
2327 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2328 if (ret != ipmi::ccSuccess)
2329 {
2330 return ipmi::response(ret);
2331 }
2332
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002333 uint16_t recordCount = getNumberOfSensors() + fruCount +
2334 ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002335
2336 uint8_t operationSupport = static_cast<uint8_t>(
2337 SdrRepositoryInfoOps::overflow); // write not supported
2338
2339 operationSupport |=
2340 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2341 operationSupport |= static_cast<uint8_t>(
2342 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2343 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2344 unspecifiedFreeSpace, sdrLastAdd,
2345 sdrLastRemove, operationSupport);
2346}
2347
2348/** @brief implements the get SDR allocation info command
2349 *
2350 * @returns IPMI completion code plus response data
2351 * - allocUnits - Number of possible allocation units
2352 * - allocUnitSize - Allocation unit size in bytes.
2353 * - allocUnitFree - Number of free allocation units
2354 * - allocUnitLargestFree - Largest free block in allocation units
2355 * - maxRecordSize - Maximum record size in allocation units.
2356 */
2357ipmi::RspType<uint16_t, // allocUnits
2358 uint16_t, // allocUnitSize
2359 uint16_t, // allocUnitFree
2360 uint16_t, // allocUnitLargestFree
2361 uint8_t // maxRecordSize
2362 >
2363 ipmiStorageGetSDRAllocationInfo()
2364{
2365 // 0000h unspecified number of alloc units
2366 constexpr uint16_t allocUnits = 0;
2367
2368 constexpr uint16_t allocUnitFree = 0;
2369 constexpr uint16_t allocUnitLargestFree = 0;
2370 // only allow one block at a time
2371 constexpr uint8_t maxRecordSize = 1;
2372
2373 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2374 allocUnitLargestFree, maxRecordSize);
2375}
2376
2377/** @brief implements the reserve SDR command
2378 * @returns IPMI completion code plus response data
2379 * - sdrReservationID
2380 */
2381ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2382{
2383 sdrReservationID++;
2384 if (sdrReservationID == 0)
2385 {
2386 sdrReservationID++;
2387 }
2388
2389 return ipmi::responseSuccess(sdrReservationID);
2390}
2391
2392ipmi::RspType<uint16_t, // next record ID
2393 std::vector<uint8_t> // payload
2394 >
2395 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2396 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2397{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002398 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002399 // reservation required for partial reads with non zero offset into
2400 // record
2401 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2402 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002403 phosphor::logging::log<phosphor::logging::level::ERR>(
2404 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002405 return ipmi::responseInvalidReservationId();
2406 }
Willy Tude54f482021-01-26 15:59:09 -08002407 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2408 if (ret != ipmi::ccSuccess)
2409 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002410 phosphor::logging::log<phosphor::logging::level::ERR>(
2411 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002412 return ipmi::response(ret);
2413 }
2414
Harvey Wu05d17c02021-09-15 08:46:59 +08002415 const auto& entityRecords =
2416 ipmi::sensor::EntityInfoMapContainer::getContainer()
2417 ->getIpmiEntityRecords();
2418 int entityCount = entityRecords.size();
2419
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002420 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002421 size_t lastRecord = getNumberOfSensors() + fruCount +
2422 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002423 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2424
2425 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002426 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002427 phosphor::logging::log<phosphor::logging::level::ERR>(
2428 "ipmiStorageGetSDR: getSensorSubtree error");
2429 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002430 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002431
Willy Tu4eca2512022-06-20 21:14:51 -07002432 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2433
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002434 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002435 if (getSensorDataRecord(
2436 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2437 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002438 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002439 phosphor::logging::log<phosphor::logging::level::ERR>(
2440 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002441 return ipmi::responseInvalidFieldRequest();
2442 }
Willy Tude54f482021-01-26 15:59:09 -08002443 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002444 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002445 if (!hdr)
2446 {
2447 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002448 "ipmiStorageGetSDR: record header is null");
2449 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002450 }
2451
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002452 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2453 hdr->record_length;
Willy Tude54f482021-01-26 15:59:09 -08002454 if (sdrLength < (offset + bytesToRead))
2455 {
2456 bytesToRead = sdrLength - offset;
2457 }
2458
2459 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2460 if (!respStart)
2461 {
2462 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002463 "ipmiStorageGetSDR: record is null");
2464 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002465 }
2466
2467 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002468
Willy Tude54f482021-01-26 15:59:09 -08002469 return ipmi::responseSuccess(nextRecordId, recordData);
2470}
adarshgrami042e9db2022-09-15 10:34:34 +05302471namespace dcmi
2472{
2473
2474ipmi::RspType<uint8_t, // No of instances for requested id
2475 uint8_t, // No of record ids in the response
2476 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2477 // IDs
2478 >
2479 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Willy Tu0679e4b2022-11-11 14:34:33 -08002480 uint8_t entityInstance,
2481 [[maybe_unused]] uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302482{
2483 auto match = ipmi::dcmi::validEntityId.find(entityId);
2484 if (match == ipmi::dcmi::validEntityId.end())
2485 {
2486 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2487
2488 return ipmi::responseInvalidFieldRequest();
2489 }
2490
2491 if (sensorType != ipmi::dcmi::temperatureSensorType)
2492 {
2493 log<level::ERR>("Invalid sensor type",
2494 entry("SENSOR_TYPE=%d", sensorType));
2495
2496 return ipmi::responseInvalidFieldRequest();
2497 }
2498 auto& sensorTree = getSensorTree();
2499 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2500 {
2501 return ipmi::responseUnspecifiedError();
2502 }
2503
2504 std::vector<uint16_t> sensorRec{};
2505 uint8_t numInstances = 0;
2506
Willy Tu0679e4b2022-11-11 14:34:33 -08002507 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
adarshgrami042e9db2022-09-15 10:34:34 +05302508 for (const auto& sensor : sensorTree)
2509 {
2510 auto sensorTypeValue = getSensorTypeFromPath(sensor.first);
2511 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2512 {
2513 continue;
2514 }
2515 const auto& connection = sensor.second.begin()->first;
2516
2517 DbusInterfaceMap sensorMap;
2518 if (!getSensorMap(ctx, connection, sensor.first, sensorMap,
2519 sensorMapSdrUpdatePeriod))
2520 {
2521 phosphor::logging::log<phosphor::logging::level::ERR>(
2522 "Failed to update sensor map for threshold sensor",
2523 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2524 phosphor::logging::entry("PATH=%s", sensor.first.c_str()));
2525 continue;
2526 }
2527 uint8_t entityIdValue = 0;
2528 uint8_t entityInstanceValue = 0;
Willy Tu0679e4b2022-11-11 14:34:33 -08002529 updateIpmiFromAssociation(
2530 sensor.first,
2531 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2532 sensorMap, entityIdValue, entityInstanceValue);
adarshgrami042e9db2022-09-15 10:34:34 +05302533 if (!entityInstance)
2534 {
2535 if (entityIdValue == match->first || entityIdValue == match->second)
2536 {
2537 auto recordId = getSensorNumberFromPath(sensor.first);
2538 if (recordId != invalidSensorNumber)
2539 {
2540 numInstances++;
2541 if (numInstances <= ipmi::dcmi::maxRecords)
2542 {
2543 sensorRec.push_back(recordId);
2544 }
2545 }
2546 }
2547 }
2548 else
2549 {
2550 if (entityIdValue == match->first || entityIdValue == match->second)
2551 {
2552 if (entityInstance == entityInstanceValue)
2553 {
2554 auto recordId = getSensorNumberFromPath(sensor.first);
2555 if ((recordId != invalidSensorNumber) && sensorRec.empty())
2556 {
2557 sensorRec.push_back(recordId);
2558 }
2559 }
2560 numInstances++;
2561 }
2562 }
2563 }
2564 if (sensorRec.empty())
2565 {
2566 return ipmi::responseSensorInvalid();
2567 }
2568 uint8_t numRecords = sensorRec.size();
2569 return ipmi::responseSuccess(numInstances, numRecords, sensorRec);
2570}
2571} // namespace dcmi
2572
Willy Tude54f482021-01-26 15:59:09 -08002573/* end storage commands */
2574
2575void registerSensorFunctions()
2576{
2577 // <Platform Event>
2578 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2579 ipmi::sensor_event::cmdPlatformEvent,
2580 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2581
Willy Tudbafbce2021-03-29 00:37:05 -07002582 // <Set Sensor Reading and Event Status>
2583 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2584 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2585 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002586
Willy Tude54f482021-01-26 15:59:09 -08002587 // <Get Sensor Reading>
2588 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2589 ipmi::sensor_event::cmdGetSensorReading,
2590 ipmi::Privilege::User, ipmiSenGetSensorReading);
2591
2592 // <Get Sensor Threshold>
2593 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2594 ipmi::sensor_event::cmdGetSensorThreshold,
2595 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2596
2597 // <Set Sensor Threshold>
2598 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2599 ipmi::sensor_event::cmdSetSensorThreshold,
2600 ipmi::Privilege::Operator,
2601 ipmiSenSetSensorThresholds);
2602
2603 // <Get Sensor Event Enable>
2604 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2605 ipmi::sensor_event::cmdGetSensorEventEnable,
2606 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2607
2608 // <Get Sensor Event Status>
2609 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2610 ipmi::sensor_event::cmdGetSensorEventStatus,
2611 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2612
2613 // register all storage commands for both Sensor and Storage command
2614 // versions
2615
2616 // <Get SDR Repository Info>
2617 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2618 ipmi::storage::cmdGetSdrRepositoryInfo,
2619 ipmi::Privilege::User,
2620 ipmiStorageGetSDRRepositoryInfo);
2621
2622 // <Get Device SDR Info>
2623 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2624 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2625 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2626
2627 // <Get SDR Allocation Info>
2628 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2629 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2630 ipmi::Privilege::User,
2631 ipmiStorageGetSDRAllocationInfo);
2632
2633 // <Reserve SDR Repo>
2634 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2635 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2636 ipmi::Privilege::User, ipmiStorageReserveSDR);
2637
2638 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2639 ipmi::storage::cmdReserveSdrRepository,
2640 ipmi::Privilege::User, ipmiStorageReserveSDR);
2641
2642 // <Get Sdr>
2643 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2644 ipmi::sensor_event::cmdGetDeviceSdr,
2645 ipmi::Privilege::User, ipmiStorageGetSDR);
2646
2647 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2648 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2649 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302650 // <Get DCMI Sensor Info>
2651 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2652 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002653 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302654 ipmi::dcmi::getSensorInfo);
Willy Tude54f482021-01-26 15:59:09 -08002655}
2656} // namespace ipmi