blob: b356f93cc3967284f2c50be5a15deac11677c102 [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"
Harvey Wu05d17c02021-09-15 08:46:59 +080022#include "entity_map_json.hpp"
Willy Tude54f482021-01-26 15:59:09 -080023
24#include <algorithm>
25#include <array>
26#include <boost/algorithm/string.hpp>
27#include <boost/container/flat_map.hpp>
28#include <chrono>
29#include <cmath>
30#include <cstring>
31#include <iostream>
32#include <ipmid/api.hpp>
33#include <ipmid/types.hpp>
34#include <ipmid/utils.hpp>
35#include <map>
36#include <memory>
37#include <optional>
38#include <phosphor-logging/log.hpp>
39#include <sdbusplus/bus.hpp>
40#include <stdexcept>
41#include <string>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000042#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080043#include <utility>
44#include <variant>
45
Scron Chang2703b022021-07-06 15:47:45 +080046#ifdef FEATURE_HYBRID_SENSORS
47
48#include "sensordatahandler.hpp"
49namespace ipmi
50{
51namespace sensor
52{
53extern const IdInfoMap sensors;
54} // namespace sensor
55} // namespace ipmi
56#endif
57
JeffLind950f412021-10-20 18:49:34 +080058constexpr std::array<const char*, 7> suffixes = {
59 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
60 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080061namespace ipmi
62{
Hao Jiangd48c9212021-02-03 15:45:06 -080063
64using phosphor::logging::entry;
65using phosphor::logging::level;
66using phosphor::logging::log;
67
Willy Tude54f482021-01-26 15:59:09 -080068static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070069static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080070
Willy Tu38e7a2b2021-03-29 15:09:56 -070071// BMC I2C address is generally at 0x20
72static constexpr uint8_t bmcI2CAddr = 0x20;
73
Willy Tude54f482021-01-26 15:59:09 -080074constexpr size_t maxSDRTotalSize =
75 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
76constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
77
78static uint16_t sdrReservationID;
79static uint32_t sdrLastAdd = noTimestamp;
80static uint32_t sdrLastRemove = noTimestamp;
81static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070082
83// The IPMI spec defines four Logical Units (LUN), each capable of supporting
84// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
85// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
86// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
87// location.
88static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
89
90static constexpr size_t lun0MaxSensorNum = 0xfe;
91static constexpr size_t lun1MaxSensorNum = 0x1fe;
92static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -080093static constexpr int GENERAL_ERROR = -1;
94
Willy Tude54f482021-01-26 15:59:09 -080095static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
96
97// Specify the comparison required to sort and find char* map objects
98struct CmpStr
99{
100 bool operator()(const char* a, const char* b) const
101 {
102 return std::strcmp(a, b) < 0;
103 }
104};
105const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
106 sensorUnits{{{"temperature", SensorUnits::degreesC},
107 {"voltage", SensorUnits::volts},
108 {"current", SensorUnits::amps},
109 {"fan_tach", SensorUnits::rpm},
110 {"power", SensorUnits::watts}}};
111
112void registerSensorFunctions() __attribute__((constructor));
113
Patrick Williams5d82f472022-07-22 19:26:53 -0500114static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800115 *getSdBus(),
116 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
117 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500118 [](sdbusplus::message_t&) {
Willy Tude54f482021-01-26 15:59:09 -0800119 getSensorTree().clear();
Willy Tu4eca2512022-06-20 21:14:51 -0700120 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
Willy Tude54f482021-01-26 15:59:09 -0800121 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
122 std::chrono::system_clock::now().time_since_epoch())
123 .count();
124 });
125
Patrick Williams5d82f472022-07-22 19:26:53 -0500126static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800127 *getSdBus(),
128 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
129 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500130 [](sdbusplus::message_t&) {
Willy Tude54f482021-01-26 15:59:09 -0800131 getSensorTree().clear();
Willy Tu4eca2512022-06-20 21:14:51 -0700132 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
Willy Tude54f482021-01-26 15:59:09 -0800133 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
134 std::chrono::system_clock::now().time_since_epoch())
135 .count();
136 });
137
138// this keeps track of deassertions for sensor event status command. A
139// deasertion can only happen if an assertion was seen first.
140static boost::container::flat_map<
141 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
142 thresholdDeassertMap;
143
Patrick Williams5d82f472022-07-22 19:26:53 -0500144static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800145 *getSdBus(),
146 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
147 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500148 [](sdbusplus::message_t& m) {
Willy Tude54f482021-01-26 15:59:09 -0800149 boost::container::flat_map<std::string, std::variant<bool, double>>
150 values;
151 m.read(std::string(), values);
152
153 auto findAssert =
154 std::find_if(values.begin(), values.end(), [](const auto& pair) {
155 return pair.first.find("Alarm") != std::string::npos;
156 });
157 if (findAssert != values.end())
158 {
159 auto ptr = std::get_if<bool>(&(findAssert->second));
160 if (ptr == nullptr)
161 {
162 phosphor::logging::log<phosphor::logging::level::ERR>(
163 "thresholdChanged: Assert non bool");
164 return;
165 }
166 if (*ptr)
167 {
168 phosphor::logging::log<phosphor::logging::level::INFO>(
169 "thresholdChanged: Assert",
170 phosphor::logging::entry("SENSOR=%s", m.get_path()));
171 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
172 }
173 else
174 {
175 auto& value =
176 thresholdDeassertMap[m.get_path()][findAssert->first];
177 if (value)
178 {
179 phosphor::logging::log<phosphor::logging::level::INFO>(
180 "thresholdChanged: deassert",
181 phosphor::logging::entry("SENSOR=%s", m.get_path()));
182 value = *ptr;
183 }
184 }
185 }
186 });
187
Hao Jiangd2afd052020-12-10 15:09:32 -0800188namespace sensor
189{
190static constexpr const char* vrInterface =
191 "xyz.openbmc_project.Control.VoltageRegulatorMode";
192static constexpr const char* sensorInterface =
193 "xyz.openbmc_project.Sensor.Value";
194} // namespace sensor
195
Willy Tude54f482021-01-26 15:59:09 -0800196static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
197 double& min)
198{
199 max = 127;
200 min = -128;
201
Hao Jiangd2afd052020-12-10 15:09:32 -0800202 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800203 auto critical =
204 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
205 auto warning =
206 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
207
208 if (sensorObject != sensorMap.end())
209 {
210 auto maxMap = sensorObject->second.find("MaxValue");
211 auto minMap = sensorObject->second.find("MinValue");
212
213 if (maxMap != sensorObject->second.end())
214 {
215 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
216 }
217 if (minMap != sensorObject->second.end())
218 {
219 min = std::visit(VariantToDoubleVisitor(), minMap->second);
220 }
221 }
222 if (critical != sensorMap.end())
223 {
224 auto lower = critical->second.find("CriticalLow");
225 auto upper = critical->second.find("CriticalHigh");
226 if (lower != critical->second.end())
227 {
228 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300229 if (std::isfinite(value))
230 {
231 min = std::min(value, min);
232 }
Willy Tude54f482021-01-26 15:59:09 -0800233 }
234 if (upper != critical->second.end())
235 {
236 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300237 if (std::isfinite(value))
238 {
239 max = std::max(value, max);
240 }
Willy Tude54f482021-01-26 15:59:09 -0800241 }
242 }
243 if (warning != sensorMap.end())
244 {
245
246 auto lower = warning->second.find("WarningLow");
247 auto upper = warning->second.find("WarningHigh");
248 if (lower != warning->second.end())
249 {
250 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300251 if (std::isfinite(value))
252 {
253 min = std::min(value, min);
254 }
Willy Tude54f482021-01-26 15:59:09 -0800255 }
256 if (upper != warning->second.end())
257 {
258 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300259 if (std::isfinite(value))
260 {
261 max = std::max(value, max);
262 }
Willy Tude54f482021-01-26 15:59:09 -0800263 }
264 }
265}
266
267static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700268 std::string sensorPath, DbusInterfaceMap& sensorMap,
269 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800270{
Scron Chang2703b022021-07-06 15:47:45 +0800271#ifdef FEATURE_HYBRID_SENSORS
272 if (auto sensor = findStaticSensor(sensorPath);
273 sensor != ipmi::sensor::sensors.end() &&
274 getSensorEventTypeFromPath(sensorPath) !=
275 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
276 {
277 // If the incoming sensor is a discrete sensor, it might fail in
278 // getManagedObjects(), return true, and use its own getFunc to get
279 // value.
280 return true;
281 }
282#endif
283
Willy Tude54f482021-01-26 15:59:09 -0800284 static boost::container::flat_map<
285 std::string, std::chrono::time_point<std::chrono::steady_clock>>
286 updateTimeMap;
287
288 auto updateFind = updateTimeMap.find(sensorConnection);
289 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
290 if (updateFind != updateTimeMap.end())
291 {
292 lastUpdate = updateFind->second;
293 }
294
295 auto now = std::chrono::steady_clock::now();
296
297 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700298 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800299 {
Willy Tude54f482021-01-26 15:59:09 -0800300 ObjectValueTree managedObjects;
301 boost::system::error_code ec = getManagedObjects(
302 ctx, sensorConnection.c_str(), "/", managedObjects);
303 if (ec)
304 {
305 phosphor::logging::log<phosphor::logging::level::ERR>(
306 "GetMangagedObjects for getSensorMap failed",
307 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
308
309 return false;
310 }
311
312 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700313 // Update time after finish building the map which allow the
314 // data to be cached for updatePeriod plus the build time.
315 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800316 }
317 auto connection = SensorCache.find(sensorConnection);
318 if (connection == SensorCache.end())
319 {
320 return false;
321 }
322 auto path = connection->second.find(sensorPath);
323 if (path == connection->second.end())
324 {
325 return false;
326 }
327 sensorMap = path->second;
328
329 return true;
330}
331
Hao Jiangd2afd052020-12-10 15:09:32 -0800332namespace sensor
333{
Hao Jiangd48c9212021-02-03 15:45:06 -0800334// Read VR profiles from sensor(daemon) interface
335static std::optional<std::vector<std::string>>
336 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800337{
338 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800339 auto supportedProperty = object.find("Supported");
340 if (supportedProperty == object.end() ||
341 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800342 {
343 phosphor::logging::log<phosphor::logging::level::ERR>(
344 "Missing the required Supported and Selected properties");
345 return std::nullopt;
346 }
347
348 const auto profilesPtr =
349 std::get_if<std::vector<std::string>>(&supportedProperty->second);
350
351 if (profilesPtr == nullptr)
352 {
353 phosphor::logging::log<phosphor::logging::level::ERR>(
354 "property is not array of string");
355 return std::nullopt;
356 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800357 return *profilesPtr;
358}
359
360// Calculate VR Mode from input IPMI discrete event bytes
361static std::optional<std::string>
362 calculateVRMode(uint15_t assertOffset,
363 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
364{
365 // get VR mode profiles from Supported Interface
366 auto profiles = getSupportedVrProfiles(VRObject);
367 if (!profiles)
368 {
369 return std::nullopt;
370 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800371
372 // interpret IPMI cmd bits into profiles' index
373 long unsigned int index = 0;
374 // only one bit should be set and the highest bit should not be used.
375 if (assertOffset == 0 || assertOffset == (1u << 15) ||
376 (assertOffset & (assertOffset - 1)))
377 {
378 phosphor::logging::log<phosphor::logging::level::ERR>(
379 "IPMI cmd format incorrect",
380
381 phosphor::logging::entry("BYTES=%#02x",
382 static_cast<uint16_t>(assertOffset)));
383 return std::nullopt;
384 }
385
386 while (assertOffset != 1)
387 {
388 assertOffset >>= 1;
389 index++;
390 }
391
Hao Jiangd48c9212021-02-03 15:45:06 -0800392 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800393 {
394 phosphor::logging::log<phosphor::logging::level::ERR>(
395 "profile index out of boundary");
396 return std::nullopt;
397 }
398
Hao Jiangd48c9212021-02-03 15:45:06 -0800399 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800400}
401
402// Calculate sensor value from IPMI reading byte
403static std::optional<double>
404 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
405 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
406{
407 if (valueObject.find("Value") == valueObject.end())
408 {
409 phosphor::logging::log<phosphor::logging::level::ERR>(
410 "Missing the required Value property");
411 return std::nullopt;
412 }
413
414 double max = 0;
415 double min = 0;
416 getSensorMaxMin(sensorMap, max, min);
417
418 int16_t mValue = 0;
419 int16_t bValue = 0;
420 int8_t rExp = 0;
421 int8_t bExp = 0;
422 bool bSigned = false;
423
424 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
425 {
426 return std::nullopt;
427 }
428
429 double value = bSigned ? ((int8_t)reading) : reading;
430
431 value *= ((double)mValue);
432 value += ((double)bValue) * std::pow(10.0, bExp);
433 value *= std::pow(10.0, rExp);
434
435 return value;
436}
437
Willy Tu38e7a2b2021-03-29 15:09:56 -0700438// Extract file name from sensor path as the sensors SDR ID. Simplify the name
439// if it is too long.
440std::string parseSdrIdFromPath(const std::string& path)
441{
442 std::string name;
443 size_t nameStart = path.rfind("/");
444 if (nameStart != std::string::npos)
445 {
446 name = path.substr(nameStart + 1, std::string::npos - nameStart);
447 }
448
Willy Tu38e7a2b2021-03-29 15:09:56 -0700449 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
450 {
451 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800452 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700453 {
JeffLind950f412021-10-20 18:49:34 +0800454 if (boost::ends_with(name, suffix))
455 {
456 boost::replace_all(name, suffix, "");
457 break;
458 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700459 }
Duke Du97014f52021-12-16 17:21:01 +0800460 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
461 {
462 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
463 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700464 }
JeffLind950f412021-10-20 18:49:34 +0800465 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700466 return name;
467}
468
Hao Jiangd48c9212021-02-03 15:45:06 -0800469bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
470 const std::string& path,
471 const ipmi::DbusInterfaceMap::mapped_type& object,
472 std::bitset<16>& assertions)
473{
474 auto profiles = sensor::getSupportedVrProfiles(object);
475 if (!profiles)
476 {
477 return false;
478 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700479 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800480
481 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700482 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800483 if (ec)
484 {
485 log<level::ERR>("Failed to get property",
486 entry("PROPERTY=%s", "Selected"),
487 entry("PATH=%s", path.c_str()),
488 entry("INTERFACE=%s", sensor::sensorInterface),
489 entry("WHAT=%s", ec.message().c_str()));
490 return false;
491 }
492
Willy Tu8366f0b2022-04-29 05:00:17 -0700493 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800494 if (itr == profiles->end())
495 {
496 using namespace phosphor::logging;
497 log<level::ERR>("VR mode doesn't match any of its profiles",
498 entry("PATH=%s", path.c_str()));
499 return false;
500 }
501 std::size_t index =
502 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
503
Willy Tubef102a2022-06-09 15:36:09 -0700504 // map index to response event assertion bit.
505 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800506 {
Willy Tubef102a2022-06-09 15:36:09 -0700507 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800508 }
509 else
510 {
511 log<level::ERR>("VR profile index reaches max assertion bit",
512 entry("PATH=%s", path.c_str()),
513 entry("INDEX=%uz", index));
514 return false;
515 }
516 if constexpr (debug)
517 {
518 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700519 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800520 }
521 return true;
522}
Hao Jiangd2afd052020-12-10 15:09:32 -0800523} // namespace sensor
524
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000525ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
526 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800527{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000528 constexpr const uint8_t validEnvmRev = 0x04;
529 constexpr const uint8_t lastSensorType = 0x2C;
530 constexpr const uint8_t oemReserved = 0xC0;
531
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700532 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000533 uint8_t evmRev = 0;
534 uint8_t sensorType = 0;
535 uint8_t sensorNum = 0;
536 uint8_t eventType = 0;
537 uint8_t eventData1 = 0;
538 std::optional<uint8_t> eventData2 = 0;
539 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700540 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000541 ipmi::ChannelInfo chInfo;
542
543 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
544 {
545 phosphor::logging::log<phosphor::logging::level::ERR>(
546 "Failed to get Channel Info",
547 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
548 return ipmi::responseUnspecifiedError();
549 }
550
551 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
552 ipmi::EChannelMediumType::systemInterface)
553 {
554
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700555 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000556 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700557 // Refer to IPMI Spec Table 32: SEL Event Records
558 generatorID = (ctx->channel << 12) // Channel
559 | (0x0 << 10) // Reserved
560 | (0x0 << 8) // 0x0 for sys-soft ID
561 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000562 }
563 else
564 {
565
566 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
567 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700568 // Refer to IPMI Spec Table 32: SEL Event Records
569 generatorID = (ctx->channel << 12) // Channel
570 | (0x0 << 10) // Reserved
571 | ((ctx->lun & 0x3) << 8) // Lun
572 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000573 }
574
575 if (!p.fullyUnpacked())
576 {
577 return ipmi::responseReqDataLenInvalid();
578 }
579
580 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
581 if (evmRev != validEnvmRev)
582 {
583 return ipmi::responseInvalidFieldRequest();
584 }
585 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
586 {
587 return ipmi::responseInvalidFieldRequest();
588 }
589
Willy Tude54f482021-01-26 15:59:09 -0800590 return ipmi::responseSuccess();
591}
592
Willy Tudbafbce2021-03-29 00:37:05 -0700593ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800594 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700595 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800596 bool, uint15_t, bool, uint8_t, uint8_t,
597 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700598{
599 std::string connection;
600 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700601 std::vector<std::string> interfaces;
602
603 ipmi::Cc status =
604 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700605 if (status)
606 {
607 return ipmi::response(status);
608 }
609
Hao Jiangd2afd052020-12-10 15:09:32 -0800610 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700611 if (std::find(interfaces.begin(), interfaces.end(),
612 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700613 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700614 DbusInterfaceMap sensorMap;
615 if (!getSensorMap(ctx, connection, path, sensorMap))
616 {
617 return ipmi::responseResponseError();
618 }
619 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800620 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700621 {
622 return ipmi::responseResponseError();
623 }
624
Jie Yangf0a89942021-07-29 15:30:25 -0700625 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800626 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
627 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700628 {
629 return ipmi::responseResponseError();
630 }
631
Hao Jiangd2afd052020-12-10 15:09:32 -0800632 auto value =
633 sensor::calculateValue(reading, sensorMap, sensorObject->second);
634 if (!value)
635 {
636 return ipmi::responseResponseError();
637 }
638
639 if constexpr (debug)
640 {
641 phosphor::logging::log<phosphor::logging::level::INFO>(
642 "IPMI SET_SENSOR",
643 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
644 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
645 phosphor::logging::entry("VALUE=%f", *value));
646 }
647
648 boost::system::error_code ec =
649 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
650 "Value", ipmi::Value(*value));
651
652 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500653 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800654 // callback functions for now (e.g. ipmiSetSensorReading).
655 if (ec)
656 {
657 using namespace phosphor::logging;
658 log<level::ERR>("Failed to set property",
659 entry("PROPERTY=%s", "Value"),
660 entry("PATH=%s", path.c_str()),
661 entry("INTERFACE=%s", sensor::sensorInterface),
662 entry("WHAT=%s", ec.message().c_str()));
663 return ipmi::responseResponseError();
664 }
665 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700666 }
667
Hao Jiange39d4d82021-04-16 17:02:40 -0700668 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
669 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700670 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700671 DbusInterfaceMap sensorMap;
672 if (!getSensorMap(ctx, connection, path, sensorMap))
673 {
674 return ipmi::responseResponseError();
675 }
676 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800677 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700678 {
679 return ipmi::responseResponseError();
680 }
681
Hao Jiangd2afd052020-12-10 15:09:32 -0800682 // VR sensors are treated as a special case and we will not check the
683 // write permission for VR sensors, since they always deemed writable
684 // and permission table are not applied to VR sensors.
685 auto vrMode =
686 sensor::calculateVRMode(assertOffset, sensorObject->second);
687 if (!vrMode)
688 {
689 return ipmi::responseResponseError();
690 }
691 boost::system::error_code ec = setDbusProperty(
692 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
693 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500694 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800695 // callback functions for now (e.g. ipmiSetSensorReading).
696 if (ec)
697 {
698 using namespace phosphor::logging;
699 log<level::ERR>("Failed to set property",
700 entry("PROPERTY=%s", "Selected"),
701 entry("PATH=%s", path.c_str()),
702 entry("INTERFACE=%s", sensor::sensorInterface),
703 entry("WHAT=%s", ec.message().c_str()));
704 return ipmi::responseResponseError();
705 }
706 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700707 }
708
Hao Jiangd2afd052020-12-10 15:09:32 -0800709 phosphor::logging::log<phosphor::logging::level::ERR>(
710 "unknown sensor type",
711 phosphor::logging::entry("PATH=%s", path.c_str()));
712 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700713}
714
Willy Tude54f482021-01-26 15:59:09 -0800715ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
716 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
717{
718 std::string connection;
719 std::string path;
720
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000721 if (sensnum == reservedSensorNumber)
722 {
723 return ipmi::responseInvalidFieldRequest();
724 }
725
Willy Tude54f482021-01-26 15:59:09 -0800726 auto status = getSensorConnection(ctx, sensnum, connection, path);
727 if (status)
728 {
729 return ipmi::response(status);
730 }
731
Scron Chang2703b022021-07-06 15:47:45 +0800732#ifdef FEATURE_HYBRID_SENSORS
733 if (auto sensor = findStaticSensor(path);
734 sensor != ipmi::sensor::sensors.end() &&
735 getSensorEventTypeFromPath(path) !=
736 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
737 {
738 if (ipmi::sensor::Mutability::Read !=
739 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
740 {
741 return ipmi::responseIllegalCommand();
742 }
743
744 uint8_t operation;
745 try
746 {
747 ipmi::sensor::GetSensorResponse getResponse =
748 sensor->second.getFunc(sensor->second);
749
750 if (getResponse.readingOrStateUnavailable)
751 {
752 operation |= static_cast<uint8_t>(
753 IPMISensorReadingByte2::readingStateUnavailable);
754 }
755 if (getResponse.scanningEnabled)
756 {
757 operation |= static_cast<uint8_t>(
758 IPMISensorReadingByte2::sensorScanningEnable);
759 }
760 if (getResponse.allEventMessagesEnabled)
761 {
762 operation |= static_cast<uint8_t>(
763 IPMISensorReadingByte2::eventMessagesEnable);
764 }
765 return ipmi::responseSuccess(
766 getResponse.reading, operation,
767 getResponse.thresholdLevelsStates,
768 getResponse.discreteReadingSensorStates);
769 }
770 catch (const std::exception& e)
771 {
772 operation |= static_cast<uint8_t>(
773 IPMISensorReadingByte2::readingStateUnavailable);
774 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
775 }
776 }
777#endif
778
Willy Tude54f482021-01-26 15:59:09 -0800779 DbusInterfaceMap sensorMap;
780 if (!getSensorMap(ctx, connection, path, sensorMap))
781 {
782 return ipmi::responseResponseError();
783 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800784 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800785
786 if (sensorObject == sensorMap.end() ||
787 sensorObject->second.find("Value") == sensorObject->second.end())
788 {
789 return ipmi::responseResponseError();
790 }
791 auto& valueVariant = sensorObject->second["Value"];
792 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
793
794 double max = 0;
795 double min = 0;
796 getSensorMaxMin(sensorMap, max, min);
797
798 int16_t mValue = 0;
799 int16_t bValue = 0;
800 int8_t rExp = 0;
801 int8_t bExp = 0;
802 bool bSigned = false;
803
804 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
805 {
806 return ipmi::responseResponseError();
807 }
808
809 uint8_t value =
810 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
811 uint8_t operation =
812 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
813 operation |=
814 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
815 bool notReading = std::isnan(reading);
816
817 if (!notReading)
818 {
819 auto availableObject =
820 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
821 if (availableObject != sensorMap.end())
822 {
823 auto findAvailable = availableObject->second.find("Available");
824 if (findAvailable != availableObject->second.end())
825 {
826 bool* available = std::get_if<bool>(&(findAvailable->second));
827 if (available && !(*available))
828 {
829 notReading = true;
830 }
831 }
832 }
833 }
834
835 if (notReading)
836 {
837 operation |= static_cast<uint8_t>(
838 IPMISensorReadingByte2::readingStateUnavailable);
839 }
840
Josh Lehana55c9532020-10-28 21:59:06 -0700841 if constexpr (details::enableInstrumentation)
842 {
843 int byteValue;
844 if (bSigned)
845 {
846 byteValue = static_cast<int>(static_cast<int8_t>(value));
847 }
848 else
849 {
850 byteValue = static_cast<int>(static_cast<uint8_t>(value));
851 }
852
853 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800854 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
855 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700856 {
857 // This is the first reading, show the coefficients
858 double step = (max - min) / 255.0;
859 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800860 << details::sdrStatsTable.getName((ctx->lun << 8) |
861 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700862 << ": Range min=" << min << " max=" << max
863 << ", step=" << step
864 << ", Coefficients mValue=" << static_cast<int>(mValue)
865 << " rExp=" << static_cast<int>(rExp)
866 << " bValue=" << static_cast<int>(bValue)
867 << " bExp=" << static_cast<int>(bExp)
868 << " bSigned=" << static_cast<int>(bSigned) << "\n";
869 }
870 }
871
Willy Tude54f482021-01-26 15:59:09 -0800872 uint8_t thresholds = 0;
873
874 auto warningObject =
875 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
876 if (warningObject != sensorMap.end())
877 {
878 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
879 auto alarmLow = warningObject->second.find("WarningAlarmLow");
880 if (alarmHigh != warningObject->second.end())
881 {
882 if (std::get<bool>(alarmHigh->second))
883 {
884 thresholds |= static_cast<uint8_t>(
885 IPMISensorReadingByte3::upperNonCritical);
886 }
887 }
888 if (alarmLow != warningObject->second.end())
889 {
890 if (std::get<bool>(alarmLow->second))
891 {
892 thresholds |= static_cast<uint8_t>(
893 IPMISensorReadingByte3::lowerNonCritical);
894 }
895 }
896 }
897
898 auto criticalObject =
899 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
900 if (criticalObject != sensorMap.end())
901 {
902 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
903 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
904 if (alarmHigh != criticalObject->second.end())
905 {
906 if (std::get<bool>(alarmHigh->second))
907 {
908 thresholds |=
909 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
910 }
911 }
912 if (alarmLow != criticalObject->second.end())
913 {
914 if (std::get<bool>(alarmLow->second))
915 {
916 thresholds |=
917 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
918 }
919 }
920 }
921
922 // no discrete as of today so optional byte is never returned
923 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
924}
925
926/** @brief implements the Set Sensor threshold command
927 * @param sensorNumber - sensor number
928 * @param lowerNonCriticalThreshMask
929 * @param lowerCriticalThreshMask
930 * @param lowerNonRecovThreshMask
931 * @param upperNonCriticalThreshMask
932 * @param upperCriticalThreshMask
933 * @param upperNonRecovThreshMask
934 * @param reserved
935 * @param lowerNonCritical - lower non-critical threshold
936 * @param lowerCritical - Lower critical threshold
937 * @param lowerNonRecoverable - Lower non recovarable threshold
938 * @param upperNonCritical - Upper non-critical threshold
939 * @param upperCritical - Upper critical
940 * @param upperNonRecoverable - Upper Non-recoverable
941 *
942 * @returns IPMI completion code
943 */
944ipmi::RspType<> ipmiSenSetSensorThresholds(
945 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
946 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
947 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
948 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800949 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800950 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800951 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800952{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000953 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800954 {
955 return ipmi::responseInvalidFieldRequest();
956 }
957
958 // lower nc and upper nc not suppported on any sensor
959 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
960 {
961 return ipmi::responseInvalidFieldRequest();
962 }
963
964 // if none of the threshold mask are set, nothing to do
965 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
966 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
967 upperCriticalThreshMask | upperNonRecovThreshMask))
968 {
969 return ipmi::responseSuccess();
970 }
971
972 std::string connection;
973 std::string path;
974
975 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
976 if (status)
977 {
978 return ipmi::response(status);
979 }
980 DbusInterfaceMap sensorMap;
981 if (!getSensorMap(ctx, connection, path, sensorMap))
982 {
983 return ipmi::responseResponseError();
984 }
985
986 double max = 0;
987 double min = 0;
988 getSensorMaxMin(sensorMap, max, min);
989
990 int16_t mValue = 0;
991 int16_t bValue = 0;
992 int8_t rExp = 0;
993 int8_t bExp = 0;
994 bool bSigned = false;
995
996 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
997 {
998 return ipmi::responseResponseError();
999 }
1000
1001 // store a vector of property name, value to set, and interface
1002 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1003
1004 // define the indexes of the tuple
1005 constexpr uint8_t propertyName = 0;
1006 constexpr uint8_t thresholdValue = 1;
1007 constexpr uint8_t interface = 2;
1008 // verifiy all needed fields are present
1009 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1010 {
1011 auto findThreshold =
1012 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1013 if (findThreshold == sensorMap.end())
1014 {
1015 return ipmi::responseInvalidFieldRequest();
1016 }
1017 if (lowerCriticalThreshMask)
1018 {
1019 auto findLower = findThreshold->second.find("CriticalLow");
1020 if (findLower == findThreshold->second.end())
1021 {
1022 return ipmi::responseInvalidFieldRequest();
1023 }
1024 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1025 findThreshold->first);
1026 }
1027 if (upperCriticalThreshMask)
1028 {
1029 auto findUpper = findThreshold->second.find("CriticalHigh");
1030 if (findUpper == findThreshold->second.end())
1031 {
1032 return ipmi::responseInvalidFieldRequest();
1033 }
1034 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1035 findThreshold->first);
1036 }
1037 }
1038 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1039 {
1040 auto findThreshold =
1041 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1042 if (findThreshold == sensorMap.end())
1043 {
1044 return ipmi::responseInvalidFieldRequest();
1045 }
1046 if (lowerNonCriticalThreshMask)
1047 {
1048 auto findLower = findThreshold->second.find("WarningLow");
1049 if (findLower == findThreshold->second.end())
1050 {
1051 return ipmi::responseInvalidFieldRequest();
1052 }
1053 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1054 findThreshold->first);
1055 }
1056 if (upperNonCriticalThreshMask)
1057 {
1058 auto findUpper = findThreshold->second.find("WarningHigh");
1059 if (findUpper == findThreshold->second.end())
1060 {
1061 return ipmi::responseInvalidFieldRequest();
1062 }
1063 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1064 findThreshold->first);
1065 }
1066 }
1067 for (const auto& property : thresholdsToSet)
1068 {
1069 // from section 36.3 in the IPMI Spec, assume all linear
1070 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1071 (bValue * std::pow(10.0, bExp))) *
1072 std::pow(10.0, rExp);
1073 setDbusProperty(
1074 *getSdBus(), connection, path, std::get<interface>(property),
1075 std::get<propertyName>(property), ipmi::Value(valueToSet));
1076 }
1077 return ipmi::responseSuccess();
1078}
1079
1080IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1081{
1082 IPMIThresholds resp;
1083 auto warningInterface =
1084 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1085 auto criticalInterface =
1086 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1087
1088 if ((warningInterface != sensorMap.end()) ||
1089 (criticalInterface != sensorMap.end()))
1090 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001091 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001092
1093 if (sensorPair == sensorMap.end())
1094 {
1095 // should not have been able to find a sensor not implementing
1096 // the sensor object
1097 throw std::runtime_error("Invalid sensor map");
1098 }
1099
1100 double max = 0;
1101 double min = 0;
1102 getSensorMaxMin(sensorMap, max, min);
1103
1104 int16_t mValue = 0;
1105 int16_t bValue = 0;
1106 int8_t rExp = 0;
1107 int8_t bExp = 0;
1108 bool bSigned = false;
1109
1110 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1111 {
1112 throw std::runtime_error("Invalid sensor atrributes");
1113 }
1114 if (warningInterface != sensorMap.end())
1115 {
1116 auto& warningMap = warningInterface->second;
1117
1118 auto warningHigh = warningMap.find("WarningHigh");
1119 auto warningLow = warningMap.find("WarningLow");
1120
1121 if (warningHigh != warningMap.end())
1122 {
1123
1124 double value =
1125 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001126 if (std::isfinite(value))
1127 {
1128 resp.warningHigh = scaleIPMIValueFromDouble(
1129 value, mValue, rExp, bValue, bExp, bSigned);
1130 }
Willy Tude54f482021-01-26 15:59:09 -08001131 }
1132 if (warningLow != warningMap.end())
1133 {
1134 double value =
1135 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001136 if (std::isfinite(value))
1137 {
1138 resp.warningLow = scaleIPMIValueFromDouble(
1139 value, mValue, rExp, bValue, bExp, bSigned);
1140 }
Willy Tude54f482021-01-26 15:59:09 -08001141 }
1142 }
1143 if (criticalInterface != sensorMap.end())
1144 {
1145 auto& criticalMap = criticalInterface->second;
1146
1147 auto criticalHigh = criticalMap.find("CriticalHigh");
1148 auto criticalLow = criticalMap.find("CriticalLow");
1149
1150 if (criticalHigh != criticalMap.end())
1151 {
1152 double value =
1153 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001154 if (std::isfinite(value))
1155 {
1156 resp.criticalHigh = scaleIPMIValueFromDouble(
1157 value, mValue, rExp, bValue, bExp, bSigned);
1158 }
Willy Tude54f482021-01-26 15:59:09 -08001159 }
1160 if (criticalLow != criticalMap.end())
1161 {
1162 double value =
1163 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001164 if (std::isfinite(value))
1165 {
1166 resp.criticalLow = scaleIPMIValueFromDouble(
1167 value, mValue, rExp, bValue, bExp, bSigned);
1168 }
Willy Tude54f482021-01-26 15:59:09 -08001169 }
1170 }
1171 }
1172 return resp;
1173}
1174
1175ipmi::RspType<uint8_t, // readable
1176 uint8_t, // lowerNCrit
1177 uint8_t, // lowerCrit
1178 uint8_t, // lowerNrecoverable
1179 uint8_t, // upperNC
1180 uint8_t, // upperCrit
1181 uint8_t> // upperNRecoverable
1182 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1183{
1184 std::string connection;
1185 std::string path;
1186
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001187 if (sensorNumber == reservedSensorNumber)
1188 {
1189 return ipmi::responseInvalidFieldRequest();
1190 }
1191
Willy Tude54f482021-01-26 15:59:09 -08001192 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1193 if (status)
1194 {
1195 return ipmi::response(status);
1196 }
1197
1198 DbusInterfaceMap sensorMap;
1199 if (!getSensorMap(ctx, connection, path, sensorMap))
1200 {
1201 return ipmi::responseResponseError();
1202 }
1203
1204 IPMIThresholds thresholdData;
1205 try
1206 {
1207 thresholdData = getIPMIThresholds(sensorMap);
1208 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001209 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001210 {
1211 return ipmi::responseResponseError();
1212 }
1213
1214 uint8_t readable = 0;
1215 uint8_t lowerNC = 0;
1216 uint8_t lowerCritical = 0;
1217 uint8_t lowerNonRecoverable = 0;
1218 uint8_t upperNC = 0;
1219 uint8_t upperCritical = 0;
1220 uint8_t upperNonRecoverable = 0;
1221
1222 if (thresholdData.warningHigh)
1223 {
1224 readable |=
1225 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1226 upperNC = *thresholdData.warningHigh;
1227 }
1228 if (thresholdData.warningLow)
1229 {
1230 readable |=
1231 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1232 lowerNC = *thresholdData.warningLow;
1233 }
1234
1235 if (thresholdData.criticalHigh)
1236 {
1237 readable |=
1238 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1239 upperCritical = *thresholdData.criticalHigh;
1240 }
1241 if (thresholdData.criticalLow)
1242 {
1243 readable |=
1244 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1245 lowerCritical = *thresholdData.criticalLow;
1246 }
1247
1248 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1249 lowerNonRecoverable, upperNC, upperCritical,
1250 upperNonRecoverable);
1251}
1252
1253/** @brief implements the get Sensor event enable command
1254 * @param sensorNumber - sensor number
1255 *
1256 * @returns IPMI completion code plus response data
1257 * - enabled - Sensor Event messages
1258 * - assertionEnabledLsb - Assertion event messages
1259 * - assertionEnabledMsb - Assertion event messages
1260 * - deassertionEnabledLsb - Deassertion event messages
1261 * - deassertionEnabledMsb - Deassertion event messages
1262 */
1263
1264ipmi::RspType<uint8_t, // enabled
1265 uint8_t, // assertionEnabledLsb
1266 uint8_t, // assertionEnabledMsb
1267 uint8_t, // deassertionEnabledLsb
1268 uint8_t> // deassertionEnabledMsb
1269 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1270{
1271 std::string connection;
1272 std::string path;
1273
1274 uint8_t enabled = 0;
1275 uint8_t assertionEnabledLsb = 0;
1276 uint8_t assertionEnabledMsb = 0;
1277 uint8_t deassertionEnabledLsb = 0;
1278 uint8_t deassertionEnabledMsb = 0;
1279
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001280 if (sensorNum == reservedSensorNumber)
1281 {
1282 return ipmi::responseInvalidFieldRequest();
1283 }
1284
Willy Tude54f482021-01-26 15:59:09 -08001285 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1286 if (status)
1287 {
1288 return ipmi::response(status);
1289 }
1290
Scron Chang2703b022021-07-06 15:47:45 +08001291#ifdef FEATURE_HYBRID_SENSORS
1292 if (auto sensor = findStaticSensor(path);
1293 sensor != ipmi::sensor::sensors.end() &&
1294 getSensorEventTypeFromPath(path) !=
1295 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1296 {
1297 enabled = static_cast<uint8_t>(
1298 IPMISensorEventEnableByte2::sensorScanningEnable);
1299 uint16_t assertionEnabled = 0;
1300 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1301 ->second.begin()
1302 ->second.second)
1303 {
1304 assertionEnabled |= (1 << offsetValMap.first);
1305 }
1306 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1307 assertionEnabledMsb =
1308 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1309
1310 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1311 assertionEnabledMsb, deassertionEnabledLsb,
1312 deassertionEnabledMsb);
1313 }
1314#endif
1315
Willy Tude54f482021-01-26 15:59:09 -08001316 DbusInterfaceMap sensorMap;
1317 if (!getSensorMap(ctx, connection, path, sensorMap))
1318 {
1319 return ipmi::responseResponseError();
1320 }
1321
1322 auto warningInterface =
1323 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1324 auto criticalInterface =
1325 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1326 if ((warningInterface != sensorMap.end()) ||
1327 (criticalInterface != sensorMap.end()))
1328 {
1329 enabled = static_cast<uint8_t>(
1330 IPMISensorEventEnableByte2::sensorScanningEnable);
1331 if (warningInterface != sensorMap.end())
1332 {
1333 auto& warningMap = warningInterface->second;
1334
1335 auto warningHigh = warningMap.find("WarningHigh");
1336 auto warningLow = warningMap.find("WarningLow");
1337 if (warningHigh != warningMap.end())
1338 {
1339 assertionEnabledLsb |= static_cast<uint8_t>(
1340 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1341 deassertionEnabledLsb |= static_cast<uint8_t>(
1342 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1343 }
1344 if (warningLow != warningMap.end())
1345 {
1346 assertionEnabledLsb |= static_cast<uint8_t>(
1347 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1348 deassertionEnabledLsb |= static_cast<uint8_t>(
1349 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1350 }
1351 }
1352 if (criticalInterface != sensorMap.end())
1353 {
1354 auto& criticalMap = criticalInterface->second;
1355
1356 auto criticalHigh = criticalMap.find("CriticalHigh");
1357 auto criticalLow = criticalMap.find("CriticalLow");
1358
1359 if (criticalHigh != criticalMap.end())
1360 {
1361 assertionEnabledMsb |= static_cast<uint8_t>(
1362 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1363 deassertionEnabledMsb |= static_cast<uint8_t>(
1364 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1365 }
1366 if (criticalLow != criticalMap.end())
1367 {
1368 assertionEnabledLsb |= static_cast<uint8_t>(
1369 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1370 deassertionEnabledLsb |= static_cast<uint8_t>(
1371 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1372 }
1373 }
1374 }
1375
1376 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1377 assertionEnabledMsb, deassertionEnabledLsb,
1378 deassertionEnabledMsb);
1379}
1380
1381/** @brief implements the get Sensor event status command
1382 * @param sensorNumber - sensor number, FFh = reserved
1383 *
1384 * @returns IPMI completion code plus response data
1385 * - sensorEventStatus - Sensor Event messages state
1386 * - assertions - Assertion event messages
1387 * - deassertions - Deassertion event messages
1388 */
1389ipmi::RspType<uint8_t, // sensorEventStatus
1390 std::bitset<16>, // assertions
1391 std::bitset<16> // deassertion
1392 >
1393 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1394{
1395 if (sensorNum == reservedSensorNumber)
1396 {
1397 return ipmi::responseInvalidFieldRequest();
1398 }
1399
1400 std::string connection;
1401 std::string path;
1402 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1403 if (status)
1404 {
1405 phosphor::logging::log<phosphor::logging::level::ERR>(
1406 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1407 phosphor::logging::entry("SENSOR=%d", sensorNum));
1408 return ipmi::response(status);
1409 }
1410
Scron Chang2703b022021-07-06 15:47:45 +08001411#ifdef FEATURE_HYBRID_SENSORS
1412 if (auto sensor = findStaticSensor(path);
1413 sensor != ipmi::sensor::sensors.end() &&
1414 getSensorEventTypeFromPath(path) !=
1415 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1416 {
1417 auto response = ipmi::sensor::get::mapDbusToAssertion(
1418 sensor->second, path, sensor->second.sensorInterface);
1419 std::bitset<16> assertions;
1420 // deassertions are not used.
1421 std::bitset<16> deassertions = 0;
1422 uint8_t sensorEventStatus;
1423 if (response.readingOrStateUnavailable)
1424 {
1425 sensorEventStatus |= static_cast<uint8_t>(
1426 IPMISensorReadingByte2::readingStateUnavailable);
1427 }
1428 if (response.scanningEnabled)
1429 {
1430 sensorEventStatus |= static_cast<uint8_t>(
1431 IPMISensorReadingByte2::sensorScanningEnable);
1432 }
1433 if (response.allEventMessagesEnabled)
1434 {
1435 sensorEventStatus |= static_cast<uint8_t>(
1436 IPMISensorReadingByte2::eventMessagesEnable);
1437 }
1438 assertions |= response.discreteReadingSensorStates << 8;
1439 assertions |= response.thresholdLevelsStates;
1440 return ipmi::responseSuccess(sensorEventStatus, assertions,
1441 deassertions);
1442 }
1443#endif
1444
Willy Tude54f482021-01-26 15:59:09 -08001445 DbusInterfaceMap sensorMap;
1446 if (!getSensorMap(ctx, connection, path, sensorMap))
1447 {
1448 phosphor::logging::log<phosphor::logging::level::ERR>(
1449 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1450 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1451 return ipmi::responseResponseError();
1452 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001453
1454 uint8_t sensorEventStatus =
1455 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1456 std::bitset<16> assertions = 0;
1457 std::bitset<16> deassertions = 0;
1458
1459 // handle VR typed sensor
1460 auto vrInterface = sensorMap.find(sensor::vrInterface);
1461 if (vrInterface != sensorMap.end())
1462 {
1463 if (!sensor::getVrEventStatus(ctx, connection, path,
1464 vrInterface->second, assertions))
1465 {
1466 return ipmi::responseResponseError();
1467 }
1468
1469 // both Event Message and Sensor Scanning are disable for VR.
1470 sensorEventStatus = 0;
1471 return ipmi::responseSuccess(sensorEventStatus, assertions,
1472 deassertions);
1473 }
1474
Willy Tude54f482021-01-26 15:59:09 -08001475 auto warningInterface =
1476 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1477 auto criticalInterface =
1478 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1479
Willy Tude54f482021-01-26 15:59:09 -08001480 std::optional<bool> criticalDeassertHigh =
1481 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1482 std::optional<bool> criticalDeassertLow =
1483 thresholdDeassertMap[path]["CriticalAlarmLow"];
1484 std::optional<bool> warningDeassertHigh =
1485 thresholdDeassertMap[path]["WarningAlarmHigh"];
1486 std::optional<bool> warningDeassertLow =
1487 thresholdDeassertMap[path]["WarningAlarmLow"];
1488
Willy Tude54f482021-01-26 15:59:09 -08001489 if (criticalDeassertHigh && !*criticalDeassertHigh)
1490 {
1491 deassertions.set(static_cast<size_t>(
1492 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1493 }
1494 if (criticalDeassertLow && !*criticalDeassertLow)
1495 {
1496 deassertions.set(static_cast<size_t>(
1497 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1498 }
1499 if (warningDeassertHigh && !*warningDeassertHigh)
1500 {
1501 deassertions.set(static_cast<size_t>(
1502 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1503 }
1504 if (warningDeassertLow && !*warningDeassertLow)
1505 {
1506 deassertions.set(static_cast<size_t>(
1507 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1508 }
1509 if ((warningInterface != sensorMap.end()) ||
1510 (criticalInterface != sensorMap.end()))
1511 {
1512 sensorEventStatus = static_cast<size_t>(
1513 IPMISensorEventEnableByte2::eventMessagesEnable);
1514 if (warningInterface != sensorMap.end())
1515 {
1516 auto& warningMap = warningInterface->second;
1517
1518 auto warningHigh = warningMap.find("WarningAlarmHigh");
1519 auto warningLow = warningMap.find("WarningAlarmLow");
1520 auto warningHighAlarm = false;
1521 auto warningLowAlarm = false;
1522
1523 if (warningHigh != warningMap.end())
1524 {
1525 warningHighAlarm = std::get<bool>(warningHigh->second);
1526 }
1527 if (warningLow != warningMap.end())
1528 {
1529 warningLowAlarm = std::get<bool>(warningLow->second);
1530 }
1531 if (warningHighAlarm)
1532 {
1533 assertions.set(
1534 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1535 upperNonCriticalGoingHigh));
1536 }
1537 if (warningLowAlarm)
1538 {
1539 assertions.set(
1540 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1541 lowerNonCriticalGoingLow));
1542 }
1543 }
1544 if (criticalInterface != sensorMap.end())
1545 {
1546 auto& criticalMap = criticalInterface->second;
1547
1548 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1549 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1550 auto criticalHighAlarm = false;
1551 auto criticalLowAlarm = false;
1552
1553 if (criticalHigh != criticalMap.end())
1554 {
1555 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1556 }
1557 if (criticalLow != criticalMap.end())
1558 {
1559 criticalLowAlarm = std::get<bool>(criticalLow->second);
1560 }
1561 if (criticalHighAlarm)
1562 {
1563 assertions.set(
1564 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1565 upperCriticalGoingHigh));
1566 }
1567 if (criticalLowAlarm)
1568 {
1569 assertions.set(static_cast<size_t>(
1570 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1571 }
1572 }
1573 }
1574
1575 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1576}
1577
Willy Tu38e7a2b2021-03-29 15:09:56 -07001578// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001579void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1580 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001581{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001582 get_sdr::header::set_record_id(
1583 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1584
Willy Tu38e7a2b2021-03-29 15:09:56 -07001585 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1586 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1587
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001588 record.header.sdr_version = ipmiSdrVersion;
1589 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1590 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1591 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001592 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001593 record.key.owner_lun = lun;
1594 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001595}
Willy Tu4eca2512022-06-20 21:14:51 -07001596bool constructSensorSdr(
1597 ipmi::Context::ptr ctx,
1598 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1599 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1600 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001601{
Hao Jiange39d4d82021-04-16 17:02:40 -07001602 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1603
1604 DbusInterfaceMap sensorMap;
1605 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1606 {
1607 phosphor::logging::log<phosphor::logging::level::ERR>(
1608 "Failed to update sensor map for threshold sensor",
1609 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1610 phosphor::logging::entry("PATH=%s", path.c_str()));
1611 return false;
1612 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001613
1614 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1615 record.body.sensor_type = getSensorTypeFromPath(path);
1616 std::string type = getSensorTypeStringFromPath(path);
1617 auto typeCstr = type.c_str();
1618 auto findUnits = sensorUnits.find(typeCstr);
1619 if (findUnits != sensorUnits.end())
1620 {
1621 record.body.sensor_units_2_base =
1622 static_cast<uint8_t>(findUnits->second);
1623 } // else default 0x0 unspecified
1624
1625 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1626
Hao Jiangd2afd052020-12-10 15:09:32 -08001627 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001628 if (sensorObject == sensorMap.end())
1629 {
1630 phosphor::logging::log<phosphor::logging::level::ERR>(
1631 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001632 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001633 }
1634
1635 uint8_t entityId = 0;
1636 uint8_t entityInstance = 0x01;
1637
1638 // follow the association chain to get the parent board's entityid and
1639 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001640 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1641 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001642
1643 record.body.entity_id = entityId;
1644 record.body.entity_instance = entityInstance;
1645
Shakeeb Pasha93889722021-10-14 10:20:13 +05301646 double max = 0;
1647 double min = 0;
1648 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001649
1650 int16_t mValue = 0;
1651 int8_t rExp = 0;
1652 int16_t bValue = 0;
1653 int8_t bExp = 0;
1654 bool bSigned = false;
1655
1656 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1657 {
1658 phosphor::logging::log<phosphor::logging::level::ERR>(
1659 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001660 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001661 }
1662
1663 // The record.body is a struct SensorDataFullRecordBody
1664 // from sensorhandler.hpp in phosphor-ipmi-host.
1665 // The meaning of these bits appears to come from
1666 // table 43.1 of the IPMI spec.
1667 // The above 5 sensor attributes are stuffed in as follows:
1668 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1669 // Byte 22-24 are for other purposes
1670 // Byte 25 = MMMMMMMM = LSB of M
1671 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1672 // Byte 27 = BBBBBBBB = LSB of B
1673 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1674 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1675 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1676
1677 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1678 record.body.m_lsb = mValue & 0xFF;
1679
1680 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1681 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1682
1683 // move the smallest bit of the MSB into place (bit 9)
1684 // the MSbs are bits 7:8 in m_msb_and_tolerance
1685 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1686
1687 record.body.b_lsb = bValue & 0xFF;
1688
1689 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1690 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1691
1692 // move the smallest bit of the MSB into place (bit 9)
1693 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1694 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1695
1696 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1697 uint8_t rExpBits = rExp & 0x07;
1698
1699 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1700 uint8_t bExpBits = bExp & 0x07;
1701
1702 // move rExp and bExp into place
1703 record.body.r_b_exponents =
1704 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1705
1706 // Set the analog reading byte interpretation accordingly
1707 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1708
1709 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1710 // These seem redundant, but derivable from the above 5 attributes
1711 // Original comment said "todo fill out rest of units"
1712
1713 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001714 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001715 record.body.id_string_info = name.size();
1716 std::strncpy(record.body.id_string, name.c_str(),
1717 sizeof(record.body.id_string));
1718
Josh Lehana55c9532020-10-28 21:59:06 -07001719 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001720 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001721
Jie Yangf0a89942021-07-29 15:30:25 -07001722 bool sensorSettable = false;
1723 auto mutability =
1724 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1725 if (mutability != sensorMap.end())
1726 {
1727 sensorSettable =
1728 mappedVariant<bool>(mutability->second, "Mutable", false);
1729 }
1730 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1731
1732 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001733 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001734
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001735 IPMIThresholds thresholdData;
1736 try
1737 {
1738 thresholdData = getIPMIThresholds(sensorMap);
1739 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001740 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001741 {
1742 phosphor::logging::log<phosphor::logging::level::ERR>(
1743 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001744 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001745 }
1746
1747 if (thresholdData.criticalHigh)
1748 {
1749 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1750 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::criticalThreshold);
1752 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1753 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1754 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1755 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1756 record.body.discrete_reading_setting_mask[0] |=
1757 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1758 }
1759 if (thresholdData.warningHigh)
1760 {
1761 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1762 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1764 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1765 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1766 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1767 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1768 record.body.discrete_reading_setting_mask[0] |=
1769 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1770 }
1771 if (thresholdData.criticalLow)
1772 {
1773 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1774 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1775 IPMISensorEventEnableThresholds::criticalThreshold);
1776 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1777 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1778 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1779 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1780 record.body.discrete_reading_setting_mask[0] |=
1781 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1782 }
1783 if (thresholdData.warningLow)
1784 {
1785 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1786 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1788 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1789 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1790 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1791 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1792 record.body.discrete_reading_setting_mask[0] |=
1793 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1794 }
1795
1796 // everything that is readable is setable
1797 record.body.discrete_reading_setting_mask[1] =
1798 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001799 return true;
1800}
1801
Scron Chang2703b022021-07-06 15:47:45 +08001802#ifdef FEATURE_HYBRID_SENSORS
1803// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001804void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001805 uint16_t recordID,
1806 ipmi::sensor::IdInfoMap::const_iterator sensor,
1807 get_sdr::SensorDataFullRecord& record)
1808{
1809 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1810
1811 record.body.entity_id = sensor->second.entityType;
1812 record.body.sensor_type = sensor->second.sensorType;
1813 record.body.event_reading_type = sensor->second.sensorReadingType;
1814 record.body.entity_instance = sensor->second.instance;
1815 if (ipmi::sensor::Mutability::Write ==
1816 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1817 {
1818 get_sdr::body::init_settable_state(true, &(record.body));
1819 }
1820
1821 auto id_string = sensor->second.sensorName;
1822
1823 if (id_string.empty())
1824 {
1825 id_string = sensor->second.sensorNameFunc(sensor->second);
1826 }
1827
1828 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1829 {
1830 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1831 &(record.body));
1832 }
1833 else
1834 {
1835 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1836 }
1837 std::strncpy(record.body.id_string, id_string.c_str(),
1838 get_sdr::body::get_id_strlen(&(record.body)));
1839}
1840#endif
1841
Hao Jiange39d4d82021-04-16 17:02:40 -07001842// Construct type 3 SDR header and key (for VR and other discrete sensors)
1843void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1844 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001845{
1846 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1847 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1848
1849 get_sdr::header::set_record_id(
1850 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1851
1852 record.header.sdr_version = ipmiSdrVersion;
1853 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1854 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1855 sizeof(get_sdr::SensorDataRecordHeader);
1856 record.key.owner_id = bmcI2CAddr;
1857 record.key.owner_lun = lun;
1858 record.key.sensor_number = sensornumber;
1859
1860 record.body.entity_id = 0x00;
1861 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001862}
Willy Tu61992ad2021-03-29 15:33:20 -07001863
Hao Jiange39d4d82021-04-16 17:02:40 -07001864// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001865bool constructVrSdr(ipmi::Context::ptr ctx,
1866 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1867 uint16_t sensorNum, uint16_t recordID,
1868 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001869 get_sdr::SensorDataEventRecord& record)
1870{
Hao Jiange39d4d82021-04-16 17:02:40 -07001871 constructEventSdrHeaderKey(sensorNum, recordID, record);
1872
1873 DbusInterfaceMap sensorMap;
1874 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1875 {
1876 phosphor::logging::log<phosphor::logging::level::ERR>(
1877 "Failed to update sensor map for VR sensor",
1878 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1879 phosphor::logging::entry("PATH=%s", path.c_str()));
1880 return false;
1881 }
Willy Tu61992ad2021-03-29 15:33:20 -07001882 // follow the association chain to get the parent board's entityid and
1883 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001884 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1885 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001886 record.body.entity_instance);
1887
1888 // Sensor type is hardcoded as a module/board type instead of parsing from
1889 // sensor path. This is because VR control is allocated in an independent
1890 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1891 // types.
1892 static constexpr const uint8_t module_board_type = 0x15;
1893 record.body.sensor_type = module_board_type;
1894 record.body.event_reading_type = 0x00;
1895
1896 record.body.sensor_record_sharing_1 = 0x00;
1897 record.body.sensor_record_sharing_2 = 0x00;
1898
1899 // populate sensor name from path
1900 auto name = sensor::parseSdrIdFromPath(path);
1901 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1902 record.body.id_string_info = nameSize;
1903 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1904 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1905
1906 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001907 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001908
1909 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001910}
1911
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001912static inline uint16_t getNumberOfSensors()
1913{
1914 return std::min(getSensorTree().size(), maxIPMISensors);
1915}
1916
Willy Tu4eca2512022-06-20 21:14:51 -07001917static int getSensorDataRecord(
1918 ipmi::Context::ptr ctx,
1919 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1920 std::vector<uint8_t>& recordData, uint16_t recordID,
1921 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001922{
1923 size_t fruCount = 0;
1924 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1925 if (ret != ipmi::ccSuccess)
1926 {
1927 phosphor::logging::log<phosphor::logging::level::ERR>(
1928 "getSensorDataRecord: getFruSdrCount error");
1929 return GENERAL_ERROR;
1930 }
1931
Harvey Wu05d17c02021-09-15 08:46:59 +08001932 const auto& entityRecords =
1933 ipmi::sensor::EntityInfoMapContainer::getContainer()
1934 ->getIpmiEntityRecords();
1935 size_t entityCount = entityRecords.size();
1936
1937 size_t lastRecord = getNumberOfSensors() + fruCount +
1938 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001939 if (recordID == lastRecordIndex)
1940 {
1941 recordID = lastRecord;
1942 }
1943 if (recordID > lastRecord)
1944 {
1945 phosphor::logging::log<phosphor::logging::level::ERR>(
1946 "getSensorDataRecord: recordID > lastRecord error");
1947 return GENERAL_ERROR;
1948 }
1949
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001950 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001951 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001952 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001953
Harvey Wu05d17c02021-09-15 08:46:59 +08001954 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1955 {
1956 // handle type 8 entity map records
1957 ipmi::sensor::EntityInfoMap::const_iterator entity =
1958 entityRecords.find(static_cast<uint8_t>(
1959 sdrIndex - fruCount - ipmi::storage::type12Count));
1960 if (entity == entityRecords.end())
1961 {
1962 return IPMI_CC_SENSOR_INVALID;
1963 }
1964 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1965 }
1966 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001967 {
1968 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001969 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001970 if (type12Index >= ipmi::storage::type12Count)
1971 {
1972 phosphor::logging::log<phosphor::logging::level::ERR>(
1973 "getSensorDataRecord: type12Index error");
1974 return GENERAL_ERROR;
1975 }
1976 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1977 }
1978 else
1979 {
1980 // handle fru records
1981 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001982 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001983 if (ret != IPMI_CC_OK)
1984 {
1985 return GENERAL_ERROR;
1986 }
1987 data.header.record_id_msb = recordID >> 8;
1988 data.header.record_id_lsb = recordID & 0xFF;
1989 recordData.insert(recordData.end(), (uint8_t*)&data,
1990 ((uint8_t*)&data) + sizeof(data));
1991 }
1992
1993 return 0;
1994 }
1995
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001996 // Perform a incremental scan of the SDR Record ID's and translate the
1997 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1998 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1999 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2000 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002001 std::string connection;
2002 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002003 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002004 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002005 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002006 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002007 // LUN 0 has one reserved sensor number. Compensate here by adding one
2008 // to the record ID
2009 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002010 ctx->lun = 1;
2011 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002012 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002013 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002014 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2015 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2016 // rules governing its use.
2017 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002018 ctx->lun = 3;
2019 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002020
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002021 auto status =
2022 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2023 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002024 if (status)
2025 {
2026 phosphor::logging::log<phosphor::logging::level::ERR>(
2027 "getSensorDataRecord: getSensorConnection error");
2028 return GENERAL_ERROR;
2029 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002030 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002031 // Return an error on LUN 2 assingments, and any sensor number beyond the
2032 // range of LUN 3
2033 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2034 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002035 {
2036 phosphor::logging::log<phosphor::logging::level::ERR>(
2037 "getSensorDataRecord: invalidSensorNumber");
2038 return GENERAL_ERROR;
2039 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002040 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2041 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2042
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002043 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2044 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002045 {
2046 phosphor::logging::log<phosphor::logging::level::ERR>(
2047 "getSensorDataRecord: sensor record mismatch");
2048 return GENERAL_ERROR;
2049 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002050
Willy Tu38e7a2b2021-03-29 15:09:56 -07002051 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002052 if (std::find(interfaces.begin(), interfaces.end(),
2053 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002054 {
Willy Tu11d68892022-01-20 10:37:34 -08002055 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002056
Hao Jiange39d4d82021-04-16 17:02:40 -07002057 // If the request doesn't read SDR body, construct only header and key
2058 // part to avoid additional DBus transaction.
2059 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2060 {
2061 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2062 }
Willy Tu4eca2512022-06-20 21:14:51 -07002063 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2064 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002065 {
2066 return GENERAL_ERROR;
2067 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002068
Willy Tu38e7a2b2021-03-29 15:09:56 -07002069 recordData.insert(recordData.end(), (uint8_t*)&record,
2070 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002071
2072 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002073 }
Willy Tu61992ad2021-03-29 15:33:20 -07002074
Scron Chang2703b022021-07-06 15:47:45 +08002075#ifdef FEATURE_HYBRID_SENSORS
2076 if (auto sensor = findStaticSensor(path);
2077 sensor != ipmi::sensor::sensors.end() &&
2078 getSensorEventTypeFromPath(path) !=
2079 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2080 {
Willy Tu11d68892022-01-20 10:37:34 -08002081 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002082
2083 // If the request doesn't read SDR body, construct only header and key
2084 // part to avoid additional DBus transaction.
2085 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2086 {
2087 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2088 }
2089 else
2090 {
2091 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2092 }
2093
2094 recordData.insert(recordData.end(), (uint8_t*)&record,
2095 ((uint8_t*)&record) + sizeof(record));
2096
2097 return 0;
2098 }
2099#endif
2100
Willy Tu61992ad2021-03-29 15:33:20 -07002101 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002102 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2103 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002104 {
Willy Tu11d68892022-01-20 10:37:34 -08002105 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002106
Hao Jiange39d4d82021-04-16 17:02:40 -07002107 // If the request doesn't read SDR body, construct only header and key
2108 // part to avoid additional DBus transaction.
2109 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2110 {
2111 constructEventSdrHeaderKey(sensorNum, recordID, record);
2112 }
Willy Tu4eca2512022-06-20 21:14:51 -07002113 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2114 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002115 {
2116 return GENERAL_ERROR;
2117 }
Willy Tu61992ad2021-03-29 15:33:20 -07002118 recordData.insert(recordData.end(), (uint8_t*)&record,
2119 ((uint8_t*)&record) + sizeof(record));
2120 }
2121
Willy Tude54f482021-01-26 15:59:09 -08002122 return 0;
2123}
2124
2125/** @brief implements the get SDR Info command
2126 * @param count - Operation
2127 *
2128 * @returns IPMI completion code plus response data
2129 * - sdrCount - sensor/SDR count
2130 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2131 */
2132static ipmi::RspType<uint8_t, // respcount
2133 uint8_t, // dynamic population flags
2134 uint32_t // last time a sensor was added
2135 >
2136 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2137 std::optional<uint8_t> count)
2138{
2139 auto& sensorTree = getSensorTree();
2140 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002141 uint16_t recordID = 0;
2142 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002143 // Sensors are dynamically allocated, and there is at least one LUN
2144 uint8_t lunsAndDynamicPopulation = 0x80;
2145 constexpr uint8_t getSdrCount = 0x01;
2146 constexpr uint8_t getSensorCount = 0x00;
2147
2148 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2149 {
2150 return ipmi::responseResponseError();
2151 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002152 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002153 if (count.value_or(0) == getSdrCount)
2154 {
Willy Tu4eca2512022-06-20 21:14:51 -07002155 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2156
Willy Tude54f482021-01-26 15:59:09 -08002157 // Count the number of Type 1 SDR entries assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002158 while (!getSensorDataRecord(
2159 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2160 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002161 {
2162 get_sdr::SensorDataRecordHeader* hdr =
2163 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002164 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002165 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2166 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002167 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002168 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002169 record.data());
2170 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002171 {
2172 sdrCount++;
2173 }
2174 }
2175 }
2176 }
2177 else if (count.value_or(0) == getSensorCount)
2178 {
2179 // Return the number of sensors attached to the LUN
2180 if ((ctx->lun == 0) && (numSensors > 0))
2181 {
2182 sdrCount =
2183 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2184 }
2185 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2186 {
2187 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2188 ? maxSensorsPerLUN
2189 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2190 }
2191 else if (ctx->lun == 3)
2192 {
2193 if (numSensors <= maxIPMISensors)
2194 {
2195 sdrCount =
2196 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2197 }
2198 else
2199 {
2200 // error
2201 throw std::out_of_range(
2202 "Maximum number of IPMI sensors exceeded.");
2203 }
2204 }
2205 }
2206 else
2207 {
2208 return ipmi::responseInvalidFieldRequest();
2209 }
2210
2211 // Get Sensor count. This returns the number of sensors
2212 if (numSensors > 0)
2213 {
2214 lunsAndDynamicPopulation |= 1;
2215 }
2216 if (numSensors > maxSensorsPerLUN)
2217 {
2218 lunsAndDynamicPopulation |= 2;
2219 }
2220 if (numSensors >= (maxSensorsPerLUN * 2))
2221 {
2222 lunsAndDynamicPopulation |= 8;
2223 }
2224 if (numSensors > maxIPMISensors)
2225 {
2226 // error
2227 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2228 }
2229
2230 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2231 sdrLastAdd);
2232}
2233
2234/* end sensor commands */
2235
2236/* storage commands */
2237
2238ipmi::RspType<uint8_t, // sdr version
2239 uint16_t, // record count
2240 uint16_t, // free space
2241 uint32_t, // most recent addition
2242 uint32_t, // most recent erase
2243 uint8_t // operationSupport
2244 >
2245 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2246{
2247 auto& sensorTree = getSensorTree();
2248 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002249 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002250 {
2251 return ipmi::responseResponseError();
2252 }
2253
2254 size_t fruCount = 0;
2255 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2256 if (ret != ipmi::ccSuccess)
2257 {
2258 return ipmi::response(ret);
2259 }
2260
2261 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002262 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002263
2264 uint8_t operationSupport = static_cast<uint8_t>(
2265 SdrRepositoryInfoOps::overflow); // write not supported
2266
2267 operationSupport |=
2268 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2269 operationSupport |= static_cast<uint8_t>(
2270 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2271 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2272 unspecifiedFreeSpace, sdrLastAdd,
2273 sdrLastRemove, operationSupport);
2274}
2275
2276/** @brief implements the get SDR allocation info command
2277 *
2278 * @returns IPMI completion code plus response data
2279 * - allocUnits - Number of possible allocation units
2280 * - allocUnitSize - Allocation unit size in bytes.
2281 * - allocUnitFree - Number of free allocation units
2282 * - allocUnitLargestFree - Largest free block in allocation units
2283 * - maxRecordSize - Maximum record size in allocation units.
2284 */
2285ipmi::RspType<uint16_t, // allocUnits
2286 uint16_t, // allocUnitSize
2287 uint16_t, // allocUnitFree
2288 uint16_t, // allocUnitLargestFree
2289 uint8_t // maxRecordSize
2290 >
2291 ipmiStorageGetSDRAllocationInfo()
2292{
2293 // 0000h unspecified number of alloc units
2294 constexpr uint16_t allocUnits = 0;
2295
2296 constexpr uint16_t allocUnitFree = 0;
2297 constexpr uint16_t allocUnitLargestFree = 0;
2298 // only allow one block at a time
2299 constexpr uint8_t maxRecordSize = 1;
2300
2301 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2302 allocUnitLargestFree, maxRecordSize);
2303}
2304
2305/** @brief implements the reserve SDR command
2306 * @returns IPMI completion code plus response data
2307 * - sdrReservationID
2308 */
2309ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2310{
2311 sdrReservationID++;
2312 if (sdrReservationID == 0)
2313 {
2314 sdrReservationID++;
2315 }
2316
2317 return ipmi::responseSuccess(sdrReservationID);
2318}
2319
2320ipmi::RspType<uint16_t, // next record ID
2321 std::vector<uint8_t> // payload
2322 >
2323 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2324 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2325{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002326 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002327 // reservation required for partial reads with non zero offset into
2328 // record
2329 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2330 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002331 phosphor::logging::log<phosphor::logging::level::ERR>(
2332 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002333 return ipmi::responseInvalidReservationId();
2334 }
Willy Tude54f482021-01-26 15:59:09 -08002335 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2336 if (ret != ipmi::ccSuccess)
2337 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002338 phosphor::logging::log<phosphor::logging::level::ERR>(
2339 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002340 return ipmi::response(ret);
2341 }
2342
Harvey Wu05d17c02021-09-15 08:46:59 +08002343 const auto& entityRecords =
2344 ipmi::sensor::EntityInfoMapContainer::getContainer()
2345 ->getIpmiEntityRecords();
2346 int entityCount = entityRecords.size();
2347
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002348 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002349 size_t lastRecord = getNumberOfSensors() + fruCount +
2350 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002351 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2352
2353 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002354 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002355 phosphor::logging::log<phosphor::logging::level::ERR>(
2356 "ipmiStorageGetSDR: getSensorSubtree error");
2357 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002358 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002359
Willy Tu4eca2512022-06-20 21:14:51 -07002360 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2361
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002362 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002363 if (getSensorDataRecord(
2364 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2365 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002366 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002367 phosphor::logging::log<phosphor::logging::level::ERR>(
2368 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002369 return ipmi::responseInvalidFieldRequest();
2370 }
Willy Tude54f482021-01-26 15:59:09 -08002371 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002372 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002373 if (!hdr)
2374 {
2375 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002376 "ipmiStorageGetSDR: record header is null");
2377 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002378 }
2379
2380 size_t sdrLength =
2381 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2382 if (sdrLength < (offset + bytesToRead))
2383 {
2384 bytesToRead = sdrLength - offset;
2385 }
2386
2387 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2388 if (!respStart)
2389 {
2390 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002391 "ipmiStorageGetSDR: record is null");
2392 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002393 }
2394
2395 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002396
Willy Tude54f482021-01-26 15:59:09 -08002397 return ipmi::responseSuccess(nextRecordId, recordData);
2398}
2399/* end storage commands */
2400
2401void registerSensorFunctions()
2402{
2403 // <Platform Event>
2404 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2405 ipmi::sensor_event::cmdPlatformEvent,
2406 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2407
Willy Tudbafbce2021-03-29 00:37:05 -07002408 // <Set Sensor Reading and Event Status>
2409 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2410 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2411 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002412
Willy Tude54f482021-01-26 15:59:09 -08002413 // <Get Sensor Reading>
2414 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2415 ipmi::sensor_event::cmdGetSensorReading,
2416 ipmi::Privilege::User, ipmiSenGetSensorReading);
2417
2418 // <Get Sensor Threshold>
2419 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2420 ipmi::sensor_event::cmdGetSensorThreshold,
2421 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2422
2423 // <Set Sensor Threshold>
2424 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2425 ipmi::sensor_event::cmdSetSensorThreshold,
2426 ipmi::Privilege::Operator,
2427 ipmiSenSetSensorThresholds);
2428
2429 // <Get Sensor Event Enable>
2430 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2431 ipmi::sensor_event::cmdGetSensorEventEnable,
2432 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2433
2434 // <Get Sensor Event Status>
2435 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2436 ipmi::sensor_event::cmdGetSensorEventStatus,
2437 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2438
2439 // register all storage commands for both Sensor and Storage command
2440 // versions
2441
2442 // <Get SDR Repository Info>
2443 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2444 ipmi::storage::cmdGetSdrRepositoryInfo,
2445 ipmi::Privilege::User,
2446 ipmiStorageGetSDRRepositoryInfo);
2447
2448 // <Get Device SDR Info>
2449 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2450 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2451 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2452
2453 // <Get SDR Allocation Info>
2454 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2455 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2456 ipmi::Privilege::User,
2457 ipmiStorageGetSDRAllocationInfo);
2458
2459 // <Reserve SDR Repo>
2460 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2461 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2462 ipmi::Privilege::User, ipmiStorageReserveSDR);
2463
2464 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2465 ipmi::storage::cmdReserveSdrRepository,
2466 ipmi::Privilege::User, ipmiStorageReserveSDR);
2467
2468 // <Get Sdr>
2469 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2470 ipmi::sensor_event::cmdGetDeviceSdr,
2471 ipmi::Privilege::User, ipmiStorageGetSDR);
2472
2473 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2474 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2475 ipmiStorageGetSDR);
2476}
2477} // namespace ipmi