blob: 39e61f09972c3e85d0184c080959e2fad48ae234 [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
Willy Tude54f482021-01-26 15:59:09 -080023#include <boost/algorithm/string.hpp>
24#include <boost/container/flat_map.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050025#include <ipmid/api.hpp>
Vernon Mauery9cf08382023-04-28 14:00:11 -070026#include <ipmid/entity_map_json.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050027#include <ipmid/types.hpp>
28#include <ipmid/utils.hpp>
29#include <phosphor-logging/log.hpp>
30#include <sdbusplus/bus.hpp>
31#include <user_channel/channel_layer.hpp>
32
33#include <algorithm>
34#include <array>
Willy Tude54f482021-01-26 15:59:09 -080035#include <chrono>
36#include <cmath>
37#include <cstring>
38#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080039#include <map>
40#include <memory>
41#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080042#include <stdexcept>
43#include <string>
44#include <utility>
45#include <variant>
46
Scron Chang2703b022021-07-06 15:47:45 +080047#ifdef FEATURE_HYBRID_SENSORS
48
49#include "sensordatahandler.hpp"
50namespace ipmi
51{
52namespace sensor
53{
54extern const IdInfoMap sensors;
55} // namespace sensor
56} // namespace ipmi
57#endif
adarshgrami042e9db2022-09-15 10:34:34 +053058namespace ipmi
59{
60namespace dcmi
61{
62// Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
63static const std::map<uint8_t, uint8_t> validEntityId{
64 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
65 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
66constexpr uint8_t temperatureSensorType = 0x01;
67constexpr uint8_t maxRecords = 8;
68} // namespace dcmi
69} // namespace ipmi
JeffLind950f412021-10-20 18:49:34 +080070constexpr std::array<const char*, 7> suffixes = {
71 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
72 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080073namespace ipmi
74{
Hao Jiangd48c9212021-02-03 15:45:06 -080075
76using phosphor::logging::entry;
77using phosphor::logging::level;
78using phosphor::logging::log;
79
Willy Tude54f482021-01-26 15:59:09 -080080static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070081static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080082
Willy Tu38e7a2b2021-03-29 15:09:56 -070083// BMC I2C address is generally at 0x20
84static constexpr uint8_t bmcI2CAddr = 0x20;
85
Willy Tude54f482021-01-26 15:59:09 -080086constexpr size_t maxSDRTotalSize =
87 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
88constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
89
90static uint16_t sdrReservationID;
91static uint32_t sdrLastAdd = noTimestamp;
92static uint32_t sdrLastRemove = noTimestamp;
93static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070094
95// The IPMI spec defines four Logical Units (LUN), each capable of supporting
96// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
97// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
Harvey Wu75893062023-03-22 17:17:31 +080098// number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
Johnathan Mantey6619ae42021-08-06 11:21:10 -070099// location.
100static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
101
Harvey Wu75893062023-03-22 17:17:31 +0800102static constexpr uint8_t lun0 = 0x0;
103static constexpr uint8_t lun1 = 0x1;
104static constexpr uint8_t lun3 = 0x3;
105
Johnathan Mantey6619ae42021-08-06 11:21:10 -0700106static constexpr size_t lun0MaxSensorNum = 0xfe;
107static constexpr size_t lun1MaxSensorNum = 0x1fe;
108static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -0800109static constexpr int GENERAL_ERROR = -1;
110
Willy Tude54f482021-01-26 15:59:09 -0800111static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
112
113// Specify the comparison required to sort and find char* map objects
114struct CmpStr
115{
116 bool operator()(const char* a, const char* b) const
117 {
118 return std::strcmp(a, b) < 0;
119 }
120};
121const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
122 sensorUnits{{{"temperature", SensorUnits::degreesC},
123 {"voltage", SensorUnits::volts},
124 {"current", SensorUnits::amps},
125 {"fan_tach", SensorUnits::rpm},
126 {"power", SensorUnits::watts}}};
127
128void registerSensorFunctions() __attribute__((constructor));
129
Patrick Williams5d82f472022-07-22 19:26:53 -0500130static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800131 *getSdBus(),
132 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
133 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500134 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500135 getSensorTree().clear();
136 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
137 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
138 std::chrono::system_clock::now().time_since_epoch())
139 .count();
Willy Tude54f482021-01-26 15:59:09 -0800140 });
141
Patrick Williams5d82f472022-07-22 19:26:53 -0500142static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800143 *getSdBus(),
144 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
145 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500146 [](sdbusplus::message_t&) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500147 getSensorTree().clear();
148 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
149 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
150 std::chrono::system_clock::now().time_since_epoch())
151 .count();
Willy Tude54f482021-01-26 15:59:09 -0800152 });
153
154// this keeps track of deassertions for sensor event status command. A
155// deasertion can only happen if an assertion was seen first.
156static boost::container::flat_map<
157 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
158 thresholdDeassertMap;
159
Patrick Williams5d82f472022-07-22 19:26:53 -0500160static sdbusplus::bus::match_t thresholdChanged(
Willy Tude54f482021-01-26 15:59:09 -0800161 *getSdBus(),
162 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
163 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500164 [](sdbusplus::message_t& m) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500165 boost::container::flat_map<std::string, std::variant<bool, double>> values;
166 m.read(std::string(), values);
Willy Tude54f482021-01-26 15:59:09 -0800167
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500168 auto findAssert = std::find_if(values.begin(), values.end(),
169 [](const auto& pair) {
170 return pair.first.find("Alarm") != std::string::npos;
171 });
172 if (findAssert != values.end())
173 {
174 auto ptr = std::get_if<bool>(&(findAssert->second));
175 if (ptr == nullptr)
Willy Tude54f482021-01-26 15:59:09 -0800176 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500177 phosphor::logging::log<phosphor::logging::level::ERR>(
178 "thresholdChanged: Assert non bool");
179 return;
180 }
181 if (*ptr)
182 {
183 phosphor::logging::log<phosphor::logging::level::INFO>(
184 "thresholdChanged: Assert",
185 phosphor::logging::entry("SENSOR=%s", m.get_path()));
186 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
187 }
188 else
189 {
190 auto& value = thresholdDeassertMap[m.get_path()][findAssert->first];
191 if (value)
Willy Tude54f482021-01-26 15:59:09 -0800192 {
193 phosphor::logging::log<phosphor::logging::level::INFO>(
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500194 "thresholdChanged: deassert",
Willy Tude54f482021-01-26 15:59:09 -0800195 phosphor::logging::entry("SENSOR=%s", m.get_path()));
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500196 value = *ptr;
Willy Tude54f482021-01-26 15:59:09 -0800197 }
198 }
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500199 }
Willy Tude54f482021-01-26 15:59:09 -0800200 });
201
Hao Jiangd2afd052020-12-10 15:09:32 -0800202namespace sensor
203{
204static constexpr const char* vrInterface =
205 "xyz.openbmc_project.Control.VoltageRegulatorMode";
206static constexpr const char* sensorInterface =
207 "xyz.openbmc_project.Sensor.Value";
208} // namespace sensor
209
Willy Tude54f482021-01-26 15:59:09 -0800210static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
211 double& min)
212{
213 max = 127;
214 min = -128;
215
Hao Jiangd2afd052020-12-10 15:09:32 -0800216 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800217 auto critical =
218 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
219 auto warning =
220 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
221
222 if (sensorObject != sensorMap.end())
223 {
224 auto maxMap = sensorObject->second.find("MaxValue");
225 auto minMap = sensorObject->second.find("MinValue");
226
227 if (maxMap != sensorObject->second.end())
228 {
229 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
230 }
231 if (minMap != sensorObject->second.end())
232 {
233 min = std::visit(VariantToDoubleVisitor(), minMap->second);
234 }
235 }
236 if (critical != sensorMap.end())
237 {
238 auto lower = critical->second.find("CriticalLow");
239 auto upper = critical->second.find("CriticalHigh");
240 if (lower != critical->second.end())
241 {
242 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300243 if (std::isfinite(value))
244 {
245 min = std::min(value, min);
246 }
Willy Tude54f482021-01-26 15:59:09 -0800247 }
248 if (upper != critical->second.end())
249 {
250 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300251 if (std::isfinite(value))
252 {
253 max = std::max(value, max);
254 }
Willy Tude54f482021-01-26 15:59:09 -0800255 }
256 }
257 if (warning != sensorMap.end())
258 {
Willy Tude54f482021-01-26 15:59:09 -0800259 auto lower = warning->second.find("WarningLow");
260 auto upper = warning->second.find("WarningHigh");
261 if (lower != warning->second.end())
262 {
263 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300264 if (std::isfinite(value))
265 {
266 min = std::min(value, min);
267 }
Willy Tude54f482021-01-26 15:59:09 -0800268 }
269 if (upper != warning->second.end())
270 {
271 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300272 if (std::isfinite(value))
273 {
274 max = std::max(value, max);
275 }
Willy Tude54f482021-01-26 15:59:09 -0800276 }
277 }
278}
279
280static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700281 std::string sensorPath, DbusInterfaceMap& sensorMap,
282 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800283{
Scron Chang2703b022021-07-06 15:47:45 +0800284#ifdef FEATURE_HYBRID_SENSORS
285 if (auto sensor = findStaticSensor(sensorPath);
286 sensor != ipmi::sensor::sensors.end() &&
287 getSensorEventTypeFromPath(sensorPath) !=
288 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
289 {
290 // If the incoming sensor is a discrete sensor, it might fail in
291 // getManagedObjects(), return true, and use its own getFunc to get
292 // value.
293 return true;
294 }
295#endif
296
Willy Tude54f482021-01-26 15:59:09 -0800297 static boost::container::flat_map<
298 std::string, std::chrono::time_point<std::chrono::steady_clock>>
299 updateTimeMap;
300
301 auto updateFind = updateTimeMap.find(sensorConnection);
302 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
303 if (updateFind != updateTimeMap.end())
304 {
305 lastUpdate = updateFind->second;
306 }
307
308 auto now = std::chrono::steady_clock::now();
309
310 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700311 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800312 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800313 bool found = false;
Willy Tude54f482021-01-26 15:59:09 -0800314
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800315 // Object managers for different kinds of OpenBMC DBus interfaces.
316 // Documented in the phosphor-dbus-interfaces repository.
317 const char* paths[] = {
318 "/xyz/openbmc_project/sensors",
319 "/xyz/openbmc_project/vr",
320 };
321 constexpr size_t num_paths = sizeof(paths) / sizeof(paths[0]);
322 ObjectValueTree allManagedObjects;
323
324 for (size_t i = 0; i < num_paths; i++)
325 {
326 ObjectValueTree managedObjects;
327 boost::system::error_code ec = getManagedObjects(
328 ctx, sensorConnection.c_str(), paths[i], managedObjects);
329 if (ec)
330 {
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800331 continue;
332 }
333 allManagedObjects.merge(managedObjects);
334 found = true;
335 }
336
337 if (!found)
338 {
Tom Tung6615d472023-05-31 18:48:12 +0800339 phosphor::logging::log<phosphor::logging::level::ERR>(
340 "GetMangagedObjects for getSensorMap failed",
341 phosphor::logging::entry("SERVICE=%s",
342 sensorConnection.c_str()));
343
Willy Tude54f482021-01-26 15:59:09 -0800344 return false;
345 }
346
Sui Chenc2cb1bc2023-01-10 02:52:06 -0800347 SensorCache[sensorConnection] = allManagedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700348 // Update time after finish building the map which allow the
349 // data to be cached for updatePeriod plus the build time.
350 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800351 }
352 auto connection = SensorCache.find(sensorConnection);
353 if (connection == SensorCache.end())
354 {
355 return false;
356 }
357 auto path = connection->second.find(sensorPath);
358 if (path == connection->second.end())
359 {
360 return false;
361 }
362 sensorMap = path->second;
363
364 return true;
365}
366
Hao Jiangd2afd052020-12-10 15:09:32 -0800367namespace sensor
368{
Hao Jiangd48c9212021-02-03 15:45:06 -0800369// Read VR profiles from sensor(daemon) interface
370static std::optional<std::vector<std::string>>
371 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800372{
373 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800374 auto supportedProperty = object.find("Supported");
375 if (supportedProperty == object.end() ||
376 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800377 {
378 phosphor::logging::log<phosphor::logging::level::ERR>(
379 "Missing the required Supported and Selected properties");
380 return std::nullopt;
381 }
382
383 const auto profilesPtr =
384 std::get_if<std::vector<std::string>>(&supportedProperty->second);
385
386 if (profilesPtr == nullptr)
387 {
388 phosphor::logging::log<phosphor::logging::level::ERR>(
389 "property is not array of string");
390 return std::nullopt;
391 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800392 return *profilesPtr;
393}
394
395// Calculate VR Mode from input IPMI discrete event bytes
396static std::optional<std::string>
397 calculateVRMode(uint15_t assertOffset,
398 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
399{
400 // get VR mode profiles from Supported Interface
401 auto profiles = getSupportedVrProfiles(VRObject);
402 if (!profiles)
403 {
404 return std::nullopt;
405 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800406
407 // interpret IPMI cmd bits into profiles' index
408 long unsigned int index = 0;
409 // only one bit should be set and the highest bit should not be used.
410 if (assertOffset == 0 || assertOffset == (1u << 15) ||
411 (assertOffset & (assertOffset - 1)))
412 {
413 phosphor::logging::log<phosphor::logging::level::ERR>(
414 "IPMI cmd format incorrect",
415
416 phosphor::logging::entry("BYTES=%#02x",
417 static_cast<uint16_t>(assertOffset)));
418 return std::nullopt;
419 }
420
421 while (assertOffset != 1)
422 {
423 assertOffset >>= 1;
424 index++;
425 }
426
Hao Jiangd48c9212021-02-03 15:45:06 -0800427 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800428 {
429 phosphor::logging::log<phosphor::logging::level::ERR>(
430 "profile index out of boundary");
431 return std::nullopt;
432 }
433
Hao Jiangd48c9212021-02-03 15:45:06 -0800434 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800435}
436
437// Calculate sensor value from IPMI reading byte
438static std::optional<double>
439 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
440 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
441{
442 if (valueObject.find("Value") == valueObject.end())
443 {
444 phosphor::logging::log<phosphor::logging::level::ERR>(
445 "Missing the required Value property");
446 return std::nullopt;
447 }
448
449 double max = 0;
450 double min = 0;
451 getSensorMaxMin(sensorMap, max, min);
452
453 int16_t mValue = 0;
454 int16_t bValue = 0;
455 int8_t rExp = 0;
456 int8_t bExp = 0;
457 bool bSigned = false;
458
459 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
460 {
461 return std::nullopt;
462 }
463
464 double value = bSigned ? ((int8_t)reading) : reading;
465
466 value *= ((double)mValue);
467 value += ((double)bValue) * std::pow(10.0, bExp);
468 value *= std::pow(10.0, rExp);
469
470 return value;
471}
472
Willy Tu38e7a2b2021-03-29 15:09:56 -0700473// Extract file name from sensor path as the sensors SDR ID. Simplify the name
474// if it is too long.
475std::string parseSdrIdFromPath(const std::string& path)
476{
477 std::string name;
478 size_t nameStart = path.rfind("/");
479 if (nameStart != std::string::npos)
480 {
481 name = path.substr(nameStart + 1, std::string::npos - nameStart);
482 }
483
Willy Tu38e7a2b2021-03-29 15:09:56 -0700484 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
485 {
486 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800487 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700488 {
JeffLind950f412021-10-20 18:49:34 +0800489 if (boost::ends_with(name, suffix))
490 {
491 boost::replace_all(name, suffix, "");
492 break;
493 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700494 }
Duke Du97014f52021-12-16 17:21:01 +0800495 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
496 {
497 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
498 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700499 }
500 return name;
501}
502
Hao Jiangd48c9212021-02-03 15:45:06 -0800503bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
504 const std::string& path,
505 const ipmi::DbusInterfaceMap::mapped_type& object,
506 std::bitset<16>& assertions)
507{
508 auto profiles = sensor::getSupportedVrProfiles(object);
509 if (!profiles)
510 {
511 return false;
512 }
Willy Tu8366f0b2022-04-29 05:00:17 -0700513 std::string mode;
Hao Jiangd48c9212021-02-03 15:45:06 -0800514
515 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
Willy Tu8366f0b2022-04-29 05:00:17 -0700516 "Selected", mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800517 if (ec)
518 {
519 log<level::ERR>("Failed to get property",
520 entry("PROPERTY=%s", "Selected"),
521 entry("PATH=%s", path.c_str()),
522 entry("INTERFACE=%s", sensor::sensorInterface),
523 entry("WHAT=%s", ec.message().c_str()));
524 return false;
525 }
526
Willy Tu8366f0b2022-04-29 05:00:17 -0700527 auto itr = std::find(profiles->begin(), profiles->end(), mode);
Hao Jiangd48c9212021-02-03 15:45:06 -0800528 if (itr == profiles->end())
529 {
530 using namespace phosphor::logging;
531 log<level::ERR>("VR mode doesn't match any of its profiles",
532 entry("PATH=%s", path.c_str()));
533 return false;
534 }
535 std::size_t index =
536 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
537
Willy Tubef102a2022-06-09 15:36:09 -0700538 // map index to response event assertion bit.
539 if (index < 16)
Hao Jiangd48c9212021-02-03 15:45:06 -0800540 {
Willy Tubef102a2022-06-09 15:36:09 -0700541 assertions.set(index);
Hao Jiangd48c9212021-02-03 15:45:06 -0800542 }
543 else
544 {
545 log<level::ERR>("VR profile index reaches max assertion bit",
546 entry("PATH=%s", path.c_str()),
547 entry("INDEX=%uz", index));
548 return false;
549 }
550 if constexpr (debug)
551 {
552 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
Willy Tu8366f0b2022-04-29 05:00:17 -0700553 << " mode is: [" << index << "] " << mode << std::endl;
Hao Jiangd48c9212021-02-03 15:45:06 -0800554 }
555 return true;
556}
Hao Jiangd2afd052020-12-10 15:09:32 -0800557} // namespace sensor
558
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000559ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
560 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800561{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000562 constexpr const uint8_t validEnvmRev = 0x04;
563 constexpr const uint8_t lastSensorType = 0x2C;
564 constexpr const uint8_t oemReserved = 0xC0;
565
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700566 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000567 uint8_t evmRev = 0;
568 uint8_t sensorType = 0;
569 uint8_t sensorNum = 0;
570 uint8_t eventType = 0;
571 uint8_t eventData1 = 0;
572 std::optional<uint8_t> eventData2 = 0;
573 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700574 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000575 ipmi::ChannelInfo chInfo;
576
577 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
578 {
579 phosphor::logging::log<phosphor::logging::level::ERR>(
580 "Failed to get Channel Info",
581 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
582 return ipmi::responseUnspecifiedError();
583 }
584
585 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
586 ipmi::EChannelMediumType::systemInterface)
587 {
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700588 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000589 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700590 // Refer to IPMI Spec Table 32: SEL Event Records
591 generatorID = (ctx->channel << 12) // Channel
592 | (0x0 << 10) // Reserved
593 | (0x0 << 8) // 0x0 for sys-soft ID
594 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000595 }
596 else
597 {
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000598 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
599 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700600 // Refer to IPMI Spec Table 32: SEL Event Records
601 generatorID = (ctx->channel << 12) // Channel
602 | (0x0 << 10) // Reserved
603 | ((ctx->lun & 0x3) << 8) // Lun
604 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000605 }
606
607 if (!p.fullyUnpacked())
608 {
609 return ipmi::responseReqDataLenInvalid();
610 }
611
612 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
613 if (evmRev != validEnvmRev)
614 {
615 return ipmi::responseInvalidFieldRequest();
616 }
617 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
618 {
619 return ipmi::responseInvalidFieldRequest();
620 }
621
Willy Tude54f482021-01-26 15:59:09 -0800622 return ipmi::responseSuccess();
623}
624
Willy Tudbafbce2021-03-29 00:37:05 -0700625ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
Willy Tu11d68892022-01-20 10:37:34 -0800626 uint8_t sensorNumber, uint8_t,
Willy Tudbafbce2021-03-29 00:37:05 -0700627 uint8_t reading, uint15_t assertOffset,
Willy Tu11d68892022-01-20 10:37:34 -0800628 bool, uint15_t, bool, uint8_t, uint8_t,
629 uint8_t)
Willy Tudbafbce2021-03-29 00:37:05 -0700630{
631 std::string connection;
632 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700633 std::vector<std::string> interfaces;
634
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500635 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path,
636 &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700637 if (status)
638 {
639 return ipmi::response(status);
640 }
641
Hao Jiangd2afd052020-12-10 15:09:32 -0800642 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700643 if (std::find(interfaces.begin(), interfaces.end(),
644 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700645 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700646 DbusInterfaceMap sensorMap;
647 if (!getSensorMap(ctx, connection, path, sensorMap))
648 {
649 return ipmi::responseResponseError();
650 }
651 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800652 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700653 {
654 return ipmi::responseResponseError();
655 }
656
Jie Yangf0a89942021-07-29 15:30:25 -0700657 // Only allow external SetSensor if write permission granted
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800658 if (!details::sdrWriteTable.getWritePermission((ctx->lun << 8) |
659 sensorNumber))
Jie Yangf0a89942021-07-29 15:30:25 -0700660 {
661 return ipmi::responseResponseError();
662 }
663
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500664 auto value = sensor::calculateValue(reading, sensorMap,
665 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800666 if (!value)
667 {
668 return ipmi::responseResponseError();
669 }
670
671 if constexpr (debug)
672 {
673 phosphor::logging::log<phosphor::logging::level::INFO>(
674 "IPMI SET_SENSOR",
675 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
676 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
677 phosphor::logging::entry("VALUE=%f", *value));
678 }
679
680 boost::system::error_code ec =
681 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
682 "Value", ipmi::Value(*value));
683
684 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500685 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800686 // callback functions for now (e.g. ipmiSetSensorReading).
687 if (ec)
688 {
689 using namespace phosphor::logging;
690 log<level::ERR>("Failed to set property",
691 entry("PROPERTY=%s", "Value"),
692 entry("PATH=%s", path.c_str()),
693 entry("INTERFACE=%s", sensor::sensorInterface),
694 entry("WHAT=%s", ec.message().c_str()));
695 return ipmi::responseResponseError();
696 }
697 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700698 }
699
Hao Jiange39d4d82021-04-16 17:02:40 -0700700 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
701 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700702 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700703 DbusInterfaceMap sensorMap;
704 if (!getSensorMap(ctx, connection, path, sensorMap))
705 {
706 return ipmi::responseResponseError();
707 }
708 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800709 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700710 {
711 return ipmi::responseResponseError();
712 }
713
Hao Jiangd2afd052020-12-10 15:09:32 -0800714 // VR sensors are treated as a special case and we will not check the
715 // write permission for VR sensors, since they always deemed writable
716 // and permission table are not applied to VR sensors.
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500717 auto vrMode = sensor::calculateVRMode(assertOffset,
718 sensorObject->second);
Hao Jiangd2afd052020-12-10 15:09:32 -0800719 if (!vrMode)
720 {
721 return ipmi::responseResponseError();
722 }
723 boost::system::error_code ec = setDbusProperty(
724 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
725 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500726 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800727 // callback functions for now (e.g. ipmiSetSensorReading).
728 if (ec)
729 {
730 using namespace phosphor::logging;
731 log<level::ERR>("Failed to set property",
732 entry("PROPERTY=%s", "Selected"),
733 entry("PATH=%s", path.c_str()),
734 entry("INTERFACE=%s", sensor::sensorInterface),
735 entry("WHAT=%s", ec.message().c_str()));
736 return ipmi::responseResponseError();
737 }
738 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700739 }
740
Hao Jiangd2afd052020-12-10 15:09:32 -0800741 phosphor::logging::log<phosphor::logging::level::ERR>(
742 "unknown sensor type",
743 phosphor::logging::entry("PATH=%s", path.c_str()));
744 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700745}
746
Willy Tude54f482021-01-26 15:59:09 -0800747ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
748 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
749{
750 std::string connection;
751 std::string path;
752
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000753 if (sensnum == reservedSensorNumber)
754 {
755 return ipmi::responseInvalidFieldRequest();
756 }
757
Willy Tude54f482021-01-26 15:59:09 -0800758 auto status = getSensorConnection(ctx, sensnum, connection, path);
759 if (status)
760 {
761 return ipmi::response(status);
762 }
763
Scron Chang2703b022021-07-06 15:47:45 +0800764#ifdef FEATURE_HYBRID_SENSORS
765 if (auto sensor = findStaticSensor(path);
766 sensor != ipmi::sensor::sensors.end() &&
767 getSensorEventTypeFromPath(path) !=
768 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
769 {
770 if (ipmi::sensor::Mutability::Read !=
771 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
772 {
773 return ipmi::responseIllegalCommand();
774 }
775
776 uint8_t operation;
777 try
778 {
779 ipmi::sensor::GetSensorResponse getResponse =
780 sensor->second.getFunc(sensor->second);
781
782 if (getResponse.readingOrStateUnavailable)
783 {
784 operation |= static_cast<uint8_t>(
785 IPMISensorReadingByte2::readingStateUnavailable);
786 }
787 if (getResponse.scanningEnabled)
788 {
789 operation |= static_cast<uint8_t>(
790 IPMISensorReadingByte2::sensorScanningEnable);
791 }
792 if (getResponse.allEventMessagesEnabled)
793 {
794 operation |= static_cast<uint8_t>(
795 IPMISensorReadingByte2::eventMessagesEnable);
796 }
797 return ipmi::responseSuccess(
798 getResponse.reading, operation,
799 getResponse.thresholdLevelsStates,
800 getResponse.discreteReadingSensorStates);
801 }
802 catch (const std::exception& e)
803 {
804 operation |= static_cast<uint8_t>(
805 IPMISensorReadingByte2::readingStateUnavailable);
806 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
807 }
808 }
809#endif
810
Willy Tude54f482021-01-26 15:59:09 -0800811 DbusInterfaceMap sensorMap;
812 if (!getSensorMap(ctx, connection, path, sensorMap))
813 {
814 return ipmi::responseResponseError();
815 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800816 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800817
818 if (sensorObject == sensorMap.end() ||
819 sensorObject->second.find("Value") == sensorObject->second.end())
820 {
821 return ipmi::responseResponseError();
822 }
823 auto& valueVariant = sensorObject->second["Value"];
824 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
825
826 double max = 0;
827 double min = 0;
828 getSensorMaxMin(sensorMap, max, min);
829
830 int16_t mValue = 0;
831 int16_t bValue = 0;
832 int8_t rExp = 0;
833 int8_t bExp = 0;
834 bool bSigned = false;
835
836 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
837 {
838 return ipmi::responseResponseError();
839 }
840
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500841 uint8_t value = scaleIPMIValueFromDouble(reading, mValue, rExp, bValue,
842 bExp, bSigned);
Willy Tude54f482021-01-26 15:59:09 -0800843 uint8_t operation =
844 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
845 operation |=
846 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
847 bool notReading = std::isnan(reading);
848
849 if (!notReading)
850 {
851 auto availableObject =
852 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
853 if (availableObject != sensorMap.end())
854 {
855 auto findAvailable = availableObject->second.find("Available");
856 if (findAvailable != availableObject->second.end())
857 {
858 bool* available = std::get_if<bool>(&(findAvailable->second));
859 if (available && !(*available))
860 {
861 notReading = true;
862 }
863 }
864 }
865 }
866
867 if (notReading)
868 {
869 operation |= static_cast<uint8_t>(
870 IPMISensorReadingByte2::readingStateUnavailable);
871 }
872
Josh Lehana55c9532020-10-28 21:59:06 -0700873 if constexpr (details::enableInstrumentation)
874 {
875 int byteValue;
876 if (bSigned)
877 {
878 byteValue = static_cast<int>(static_cast<int8_t>(value));
879 }
880 else
881 {
882 byteValue = static_cast<int>(static_cast<uint8_t>(value));
883 }
884
885 // Keep stats on the reading just obtained, even if it is "NaN"
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800886 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
887 reading, byteValue))
Josh Lehana55c9532020-10-28 21:59:06 -0700888 {
889 // This is the first reading, show the coefficients
890 double step = (max - min) / 255.0;
891 std::cerr << "IPMI sensor "
Harvey.Wu0e7a8af2022-06-10 16:46:46 +0800892 << details::sdrStatsTable.getName((ctx->lun << 8) |
893 sensnum)
Josh Lehana55c9532020-10-28 21:59:06 -0700894 << ": Range min=" << min << " max=" << max
895 << ", step=" << step
896 << ", Coefficients mValue=" << static_cast<int>(mValue)
897 << " rExp=" << static_cast<int>(rExp)
898 << " bValue=" << static_cast<int>(bValue)
899 << " bExp=" << static_cast<int>(bExp)
900 << " bSigned=" << static_cast<int>(bSigned) << "\n";
901 }
902 }
903
Willy Tude54f482021-01-26 15:59:09 -0800904 uint8_t thresholds = 0;
905
906 auto warningObject =
907 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
908 if (warningObject != sensorMap.end())
909 {
910 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
911 auto alarmLow = warningObject->second.find("WarningAlarmLow");
912 if (alarmHigh != warningObject->second.end())
913 {
914 if (std::get<bool>(alarmHigh->second))
915 {
916 thresholds |= static_cast<uint8_t>(
917 IPMISensorReadingByte3::upperNonCritical);
918 }
919 }
920 if (alarmLow != warningObject->second.end())
921 {
922 if (std::get<bool>(alarmLow->second))
923 {
924 thresholds |= static_cast<uint8_t>(
925 IPMISensorReadingByte3::lowerNonCritical);
926 }
927 }
928 }
929
930 auto criticalObject =
931 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
932 if (criticalObject != sensorMap.end())
933 {
934 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
935 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
936 if (alarmHigh != criticalObject->second.end())
937 {
938 if (std::get<bool>(alarmHigh->second))
939 {
940 thresholds |=
941 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
942 }
943 }
944 if (alarmLow != criticalObject->second.end())
945 {
946 if (std::get<bool>(alarmLow->second))
947 {
948 thresholds |=
949 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
950 }
951 }
952 }
953
954 // no discrete as of today so optional byte is never returned
955 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
956}
957
958/** @brief implements the Set Sensor threshold command
959 * @param sensorNumber - sensor number
960 * @param lowerNonCriticalThreshMask
961 * @param lowerCriticalThreshMask
962 * @param lowerNonRecovThreshMask
963 * @param upperNonCriticalThreshMask
964 * @param upperCriticalThreshMask
965 * @param upperNonRecovThreshMask
966 * @param reserved
967 * @param lowerNonCritical - lower non-critical threshold
968 * @param lowerCritical - Lower critical threshold
969 * @param lowerNonRecoverable - Lower non recovarable threshold
970 * @param upperNonCritical - Upper non-critical threshold
971 * @param upperCritical - Upper critical
972 * @param upperNonRecoverable - Upper Non-recoverable
973 *
974 * @returns IPMI completion code
975 */
976ipmi::RspType<> ipmiSenSetSensorThresholds(
977 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
978 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
979 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
980 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800981 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
Willy Tude54f482021-01-26 15:59:09 -0800982 uint8_t upperNonCritical, uint8_t upperCritical,
Willy Tu11d68892022-01-20 10:37:34 -0800983 [[maybe_unused]] uint8_t upperNonRecoverable)
Willy Tude54f482021-01-26 15:59:09 -0800984{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000985 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800986 {
987 return ipmi::responseInvalidFieldRequest();
988 }
989
990 // lower nc and upper nc not suppported on any sensor
991 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
992 {
993 return ipmi::responseInvalidFieldRequest();
994 }
995
996 // if none of the threshold mask are set, nothing to do
997 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
998 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
999 upperCriticalThreshMask | upperNonRecovThreshMask))
1000 {
1001 return ipmi::responseSuccess();
1002 }
1003
1004 std::string connection;
1005 std::string path;
1006
1007 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1008 if (status)
1009 {
1010 return ipmi::response(status);
1011 }
1012 DbusInterfaceMap sensorMap;
1013 if (!getSensorMap(ctx, connection, path, sensorMap))
1014 {
1015 return ipmi::responseResponseError();
1016 }
1017
1018 double max = 0;
1019 double min = 0;
1020 getSensorMaxMin(sensorMap, max, min);
1021
1022 int16_t mValue = 0;
1023 int16_t bValue = 0;
1024 int8_t rExp = 0;
1025 int8_t bExp = 0;
1026 bool bSigned = false;
1027
1028 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1029 {
1030 return ipmi::responseResponseError();
1031 }
1032
1033 // store a vector of property name, value to set, and interface
1034 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1035
1036 // define the indexes of the tuple
1037 constexpr uint8_t propertyName = 0;
1038 constexpr uint8_t thresholdValue = 1;
1039 constexpr uint8_t interface = 2;
1040 // verifiy all needed fields are present
1041 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1042 {
1043 auto findThreshold =
1044 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1045 if (findThreshold == sensorMap.end())
1046 {
1047 return ipmi::responseInvalidFieldRequest();
1048 }
1049 if (lowerCriticalThreshMask)
1050 {
1051 auto findLower = findThreshold->second.find("CriticalLow");
1052 if (findLower == findThreshold->second.end())
1053 {
1054 return ipmi::responseInvalidFieldRequest();
1055 }
1056 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1057 findThreshold->first);
1058 }
1059 if (upperCriticalThreshMask)
1060 {
1061 auto findUpper = findThreshold->second.find("CriticalHigh");
1062 if (findUpper == findThreshold->second.end())
1063 {
1064 return ipmi::responseInvalidFieldRequest();
1065 }
1066 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1067 findThreshold->first);
1068 }
1069 }
1070 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1071 {
1072 auto findThreshold =
1073 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1074 if (findThreshold == sensorMap.end())
1075 {
1076 return ipmi::responseInvalidFieldRequest();
1077 }
1078 if (lowerNonCriticalThreshMask)
1079 {
1080 auto findLower = findThreshold->second.find("WarningLow");
1081 if (findLower == findThreshold->second.end())
1082 {
1083 return ipmi::responseInvalidFieldRequest();
1084 }
1085 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1086 findThreshold->first);
1087 }
1088 if (upperNonCriticalThreshMask)
1089 {
1090 auto findUpper = findThreshold->second.find("WarningHigh");
1091 if (findUpper == findThreshold->second.end())
1092 {
1093 return ipmi::responseInvalidFieldRequest();
1094 }
1095 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1096 findThreshold->first);
1097 }
1098 }
1099 for (const auto& property : thresholdsToSet)
1100 {
1101 // from section 36.3 in the IPMI Spec, assume all linear
1102 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1103 (bValue * std::pow(10.0, bExp))) *
1104 std::pow(10.0, rExp);
1105 setDbusProperty(
1106 *getSdBus(), connection, path, std::get<interface>(property),
1107 std::get<propertyName>(property), ipmi::Value(valueToSet));
1108 }
1109 return ipmi::responseSuccess();
1110}
1111
1112IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1113{
1114 IPMIThresholds resp;
1115 auto warningInterface =
1116 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1117 auto criticalInterface =
1118 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1119
1120 if ((warningInterface != sensorMap.end()) ||
1121 (criticalInterface != sensorMap.end()))
1122 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001123 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001124
1125 if (sensorPair == sensorMap.end())
1126 {
1127 // should not have been able to find a sensor not implementing
1128 // the sensor object
1129 throw std::runtime_error("Invalid sensor map");
1130 }
1131
1132 double max = 0;
1133 double min = 0;
1134 getSensorMaxMin(sensorMap, max, min);
1135
1136 int16_t mValue = 0;
1137 int16_t bValue = 0;
1138 int8_t rExp = 0;
1139 int8_t bExp = 0;
1140 bool bSigned = false;
1141
1142 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1143 {
1144 throw std::runtime_error("Invalid sensor atrributes");
1145 }
1146 if (warningInterface != sensorMap.end())
1147 {
1148 auto& warningMap = warningInterface->second;
1149
1150 auto warningHigh = warningMap.find("WarningHigh");
1151 auto warningLow = warningMap.find("WarningLow");
1152
1153 if (warningHigh != warningMap.end())
1154 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001155 double value = std::visit(VariantToDoubleVisitor(),
1156 warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001157 if (std::isfinite(value))
1158 {
1159 resp.warningHigh = scaleIPMIValueFromDouble(
1160 value, mValue, rExp, bValue, bExp, bSigned);
1161 }
Willy Tude54f482021-01-26 15:59:09 -08001162 }
1163 if (warningLow != warningMap.end())
1164 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001165 double value = std::visit(VariantToDoubleVisitor(),
1166 warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001167 if (std::isfinite(value))
1168 {
1169 resp.warningLow = scaleIPMIValueFromDouble(
1170 value, mValue, rExp, bValue, bExp, bSigned);
1171 }
Willy Tude54f482021-01-26 15:59:09 -08001172 }
1173 }
1174 if (criticalInterface != sensorMap.end())
1175 {
1176 auto& criticalMap = criticalInterface->second;
1177
1178 auto criticalHigh = criticalMap.find("CriticalHigh");
1179 auto criticalLow = criticalMap.find("CriticalLow");
1180
1181 if (criticalHigh != criticalMap.end())
1182 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001183 double value = std::visit(VariantToDoubleVisitor(),
1184 criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001185 if (std::isfinite(value))
1186 {
1187 resp.criticalHigh = scaleIPMIValueFromDouble(
1188 value, mValue, rExp, bValue, bExp, bSigned);
1189 }
Willy Tude54f482021-01-26 15:59:09 -08001190 }
1191 if (criticalLow != criticalMap.end())
1192 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001193 double value = std::visit(VariantToDoubleVisitor(),
1194 criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001195 if (std::isfinite(value))
1196 {
1197 resp.criticalLow = scaleIPMIValueFromDouble(
1198 value, mValue, rExp, bValue, bExp, bSigned);
1199 }
Willy Tude54f482021-01-26 15:59:09 -08001200 }
1201 }
1202 }
1203 return resp;
1204}
1205
1206ipmi::RspType<uint8_t, // readable
1207 uint8_t, // lowerNCrit
1208 uint8_t, // lowerCrit
1209 uint8_t, // lowerNrecoverable
1210 uint8_t, // upperNC
1211 uint8_t, // upperCrit
1212 uint8_t> // upperNRecoverable
1213 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1214{
1215 std::string connection;
1216 std::string path;
1217
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001218 if (sensorNumber == reservedSensorNumber)
1219 {
1220 return ipmi::responseInvalidFieldRequest();
1221 }
1222
Willy Tude54f482021-01-26 15:59:09 -08001223 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1224 if (status)
1225 {
1226 return ipmi::response(status);
1227 }
1228
1229 DbusInterfaceMap sensorMap;
1230 if (!getSensorMap(ctx, connection, path, sensorMap))
1231 {
1232 return ipmi::responseResponseError();
1233 }
1234
1235 IPMIThresholds thresholdData;
1236 try
1237 {
1238 thresholdData = getIPMIThresholds(sensorMap);
1239 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001240 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001241 {
1242 return ipmi::responseResponseError();
1243 }
1244
1245 uint8_t readable = 0;
1246 uint8_t lowerNC = 0;
1247 uint8_t lowerCritical = 0;
1248 uint8_t lowerNonRecoverable = 0;
1249 uint8_t upperNC = 0;
1250 uint8_t upperCritical = 0;
1251 uint8_t upperNonRecoverable = 0;
1252
1253 if (thresholdData.warningHigh)
1254 {
1255 readable |=
1256 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1257 upperNC = *thresholdData.warningHigh;
1258 }
1259 if (thresholdData.warningLow)
1260 {
1261 readable |=
1262 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1263 lowerNC = *thresholdData.warningLow;
1264 }
1265
1266 if (thresholdData.criticalHigh)
1267 {
1268 readable |=
1269 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1270 upperCritical = *thresholdData.criticalHigh;
1271 }
1272 if (thresholdData.criticalLow)
1273 {
1274 readable |=
1275 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1276 lowerCritical = *thresholdData.criticalLow;
1277 }
1278
1279 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1280 lowerNonRecoverable, upperNC, upperCritical,
1281 upperNonRecoverable);
1282}
1283
1284/** @brief implements the get Sensor event enable command
1285 * @param sensorNumber - sensor number
1286 *
1287 * @returns IPMI completion code plus response data
1288 * - enabled - Sensor Event messages
1289 * - assertionEnabledLsb - Assertion event messages
1290 * - assertionEnabledMsb - Assertion event messages
1291 * - deassertionEnabledLsb - Deassertion event messages
1292 * - deassertionEnabledMsb - Deassertion event messages
1293 */
1294
1295ipmi::RspType<uint8_t, // enabled
1296 uint8_t, // assertionEnabledLsb
1297 uint8_t, // assertionEnabledMsb
1298 uint8_t, // deassertionEnabledLsb
1299 uint8_t> // deassertionEnabledMsb
1300 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1301{
1302 std::string connection;
1303 std::string path;
1304
1305 uint8_t enabled = 0;
1306 uint8_t assertionEnabledLsb = 0;
1307 uint8_t assertionEnabledMsb = 0;
1308 uint8_t deassertionEnabledLsb = 0;
1309 uint8_t deassertionEnabledMsb = 0;
1310
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001311 if (sensorNum == reservedSensorNumber)
1312 {
1313 return ipmi::responseInvalidFieldRequest();
1314 }
1315
Willy Tude54f482021-01-26 15:59:09 -08001316 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1317 if (status)
1318 {
1319 return ipmi::response(status);
1320 }
1321
Scron Chang2703b022021-07-06 15:47:45 +08001322#ifdef FEATURE_HYBRID_SENSORS
1323 if (auto sensor = findStaticSensor(path);
1324 sensor != ipmi::sensor::sensors.end() &&
1325 getSensorEventTypeFromPath(path) !=
1326 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1327 {
1328 enabled = static_cast<uint8_t>(
1329 IPMISensorEventEnableByte2::sensorScanningEnable);
1330 uint16_t assertionEnabled = 0;
1331 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1332 ->second.begin()
1333 ->second.second)
1334 {
1335 assertionEnabled |= (1 << offsetValMap.first);
1336 }
1337 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1338 assertionEnabledMsb =
1339 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1340
1341 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1342 assertionEnabledMsb, deassertionEnabledLsb,
1343 deassertionEnabledMsb);
1344 }
1345#endif
1346
Willy Tude54f482021-01-26 15:59:09 -08001347 DbusInterfaceMap sensorMap;
1348 if (!getSensorMap(ctx, connection, path, sensorMap))
1349 {
1350 return ipmi::responseResponseError();
1351 }
1352
1353 auto warningInterface =
1354 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1355 auto criticalInterface =
1356 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1357 if ((warningInterface != sensorMap.end()) ||
1358 (criticalInterface != sensorMap.end()))
1359 {
1360 enabled = static_cast<uint8_t>(
1361 IPMISensorEventEnableByte2::sensorScanningEnable);
1362 if (warningInterface != sensorMap.end())
1363 {
1364 auto& warningMap = warningInterface->second;
1365
1366 auto warningHigh = warningMap.find("WarningHigh");
1367 auto warningLow = warningMap.find("WarningLow");
1368 if (warningHigh != warningMap.end())
1369 {
1370 assertionEnabledLsb |= static_cast<uint8_t>(
1371 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1372 deassertionEnabledLsb |= static_cast<uint8_t>(
1373 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1374 }
1375 if (warningLow != warningMap.end())
1376 {
1377 assertionEnabledLsb |= static_cast<uint8_t>(
1378 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1379 deassertionEnabledLsb |= static_cast<uint8_t>(
1380 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1381 }
1382 }
1383 if (criticalInterface != sensorMap.end())
1384 {
1385 auto& criticalMap = criticalInterface->second;
1386
1387 auto criticalHigh = criticalMap.find("CriticalHigh");
1388 auto criticalLow = criticalMap.find("CriticalLow");
1389
1390 if (criticalHigh != criticalMap.end())
1391 {
1392 assertionEnabledMsb |= static_cast<uint8_t>(
1393 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1394 deassertionEnabledMsb |= static_cast<uint8_t>(
1395 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1396 }
1397 if (criticalLow != criticalMap.end())
1398 {
1399 assertionEnabledLsb |= static_cast<uint8_t>(
1400 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1401 deassertionEnabledLsb |= static_cast<uint8_t>(
1402 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1403 }
1404 }
1405 }
1406
1407 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1408 assertionEnabledMsb, deassertionEnabledLsb,
1409 deassertionEnabledMsb);
1410}
1411
1412/** @brief implements the get Sensor event status command
1413 * @param sensorNumber - sensor number, FFh = reserved
1414 *
1415 * @returns IPMI completion code plus response data
1416 * - sensorEventStatus - Sensor Event messages state
1417 * - assertions - Assertion event messages
1418 * - deassertions - Deassertion event messages
1419 */
1420ipmi::RspType<uint8_t, // sensorEventStatus
1421 std::bitset<16>, // assertions
1422 std::bitset<16> // deassertion
1423 >
1424 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1425{
1426 if (sensorNum == reservedSensorNumber)
1427 {
1428 return ipmi::responseInvalidFieldRequest();
1429 }
1430
1431 std::string connection;
1432 std::string path;
1433 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1434 if (status)
1435 {
1436 phosphor::logging::log<phosphor::logging::level::ERR>(
1437 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1438 phosphor::logging::entry("SENSOR=%d", sensorNum));
1439 return ipmi::response(status);
1440 }
1441
Scron Chang2703b022021-07-06 15:47:45 +08001442#ifdef FEATURE_HYBRID_SENSORS
1443 if (auto sensor = findStaticSensor(path);
1444 sensor != ipmi::sensor::sensors.end() &&
1445 getSensorEventTypeFromPath(path) !=
1446 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1447 {
1448 auto response = ipmi::sensor::get::mapDbusToAssertion(
1449 sensor->second, path, sensor->second.sensorInterface);
1450 std::bitset<16> assertions;
1451 // deassertions are not used.
1452 std::bitset<16> deassertions = 0;
1453 uint8_t sensorEventStatus;
1454 if (response.readingOrStateUnavailable)
1455 {
1456 sensorEventStatus |= static_cast<uint8_t>(
1457 IPMISensorReadingByte2::readingStateUnavailable);
1458 }
1459 if (response.scanningEnabled)
1460 {
1461 sensorEventStatus |= static_cast<uint8_t>(
1462 IPMISensorReadingByte2::sensorScanningEnable);
1463 }
1464 if (response.allEventMessagesEnabled)
1465 {
1466 sensorEventStatus |= static_cast<uint8_t>(
1467 IPMISensorReadingByte2::eventMessagesEnable);
1468 }
1469 assertions |= response.discreteReadingSensorStates << 8;
1470 assertions |= response.thresholdLevelsStates;
1471 return ipmi::responseSuccess(sensorEventStatus, assertions,
1472 deassertions);
1473 }
1474#endif
1475
Willy Tude54f482021-01-26 15:59:09 -08001476 DbusInterfaceMap sensorMap;
1477 if (!getSensorMap(ctx, connection, path, sensorMap))
1478 {
1479 phosphor::logging::log<phosphor::logging::level::ERR>(
1480 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1481 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1482 return ipmi::responseResponseError();
1483 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001484
1485 uint8_t sensorEventStatus =
1486 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1487 std::bitset<16> assertions = 0;
1488 std::bitset<16> deassertions = 0;
1489
1490 // handle VR typed sensor
1491 auto vrInterface = sensorMap.find(sensor::vrInterface);
1492 if (vrInterface != sensorMap.end())
1493 {
1494 if (!sensor::getVrEventStatus(ctx, connection, path,
1495 vrInterface->second, assertions))
1496 {
1497 return ipmi::responseResponseError();
1498 }
1499
1500 // both Event Message and Sensor Scanning are disable for VR.
1501 sensorEventStatus = 0;
1502 return ipmi::responseSuccess(sensorEventStatus, assertions,
1503 deassertions);
1504 }
1505
Willy Tude54f482021-01-26 15:59:09 -08001506 auto warningInterface =
1507 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1508 auto criticalInterface =
1509 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1510
Willy Tude54f482021-01-26 15:59:09 -08001511 std::optional<bool> criticalDeassertHigh =
1512 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1513 std::optional<bool> criticalDeassertLow =
1514 thresholdDeassertMap[path]["CriticalAlarmLow"];
1515 std::optional<bool> warningDeassertHigh =
1516 thresholdDeassertMap[path]["WarningAlarmHigh"];
1517 std::optional<bool> warningDeassertLow =
1518 thresholdDeassertMap[path]["WarningAlarmLow"];
1519
Willy Tude54f482021-01-26 15:59:09 -08001520 if (criticalDeassertHigh && !*criticalDeassertHigh)
1521 {
1522 deassertions.set(static_cast<size_t>(
1523 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1524 }
1525 if (criticalDeassertLow && !*criticalDeassertLow)
1526 {
1527 deassertions.set(static_cast<size_t>(
1528 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1529 }
1530 if (warningDeassertHigh && !*warningDeassertHigh)
1531 {
1532 deassertions.set(static_cast<size_t>(
1533 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1534 }
1535 if (warningDeassertLow && !*warningDeassertLow)
1536 {
1537 deassertions.set(static_cast<size_t>(
1538 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1539 }
1540 if ((warningInterface != sensorMap.end()) ||
1541 (criticalInterface != sensorMap.end()))
1542 {
1543 sensorEventStatus = static_cast<size_t>(
1544 IPMISensorEventEnableByte2::eventMessagesEnable);
1545 if (warningInterface != sensorMap.end())
1546 {
1547 auto& warningMap = warningInterface->second;
1548
1549 auto warningHigh = warningMap.find("WarningAlarmHigh");
1550 auto warningLow = warningMap.find("WarningAlarmLow");
1551 auto warningHighAlarm = false;
1552 auto warningLowAlarm = false;
1553
1554 if (warningHigh != warningMap.end())
1555 {
1556 warningHighAlarm = std::get<bool>(warningHigh->second);
1557 }
1558 if (warningLow != warningMap.end())
1559 {
1560 warningLowAlarm = std::get<bool>(warningLow->second);
1561 }
1562 if (warningHighAlarm)
1563 {
1564 assertions.set(
1565 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1566 upperNonCriticalGoingHigh));
1567 }
1568 if (warningLowAlarm)
1569 {
1570 assertions.set(
1571 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1572 lowerNonCriticalGoingLow));
1573 }
1574 }
1575 if (criticalInterface != sensorMap.end())
1576 {
1577 auto& criticalMap = criticalInterface->second;
1578
1579 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1580 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1581 auto criticalHighAlarm = false;
1582 auto criticalLowAlarm = false;
1583
1584 if (criticalHigh != criticalMap.end())
1585 {
1586 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1587 }
1588 if (criticalLow != criticalMap.end())
1589 {
1590 criticalLowAlarm = std::get<bool>(criticalLow->second);
1591 }
1592 if (criticalHighAlarm)
1593 {
1594 assertions.set(
1595 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1596 upperCriticalGoingHigh));
1597 }
1598 if (criticalLowAlarm)
1599 {
1600 assertions.set(static_cast<size_t>(
1601 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1602 }
1603 }
1604 }
1605
1606 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1607}
1608
Willy Tu38e7a2b2021-03-29 15:09:56 -07001609// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001610void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1611 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001612{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001613 get_sdr::header::set_record_id(
1614 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1615
Willy Tu38e7a2b2021-03-29 15:09:56 -07001616 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1617 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1618
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001619 record.header.sdr_version = ipmiSdrVersion;
1620 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1621 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1622 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001623 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001624 record.key.owner_lun = lun;
1625 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001626}
Willy Tu4eca2512022-06-20 21:14:51 -07001627bool constructSensorSdr(
1628 ipmi::Context::ptr ctx,
1629 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1630 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1631 const std::string& path, get_sdr::SensorDataFullRecord& record)
Hao Jiange39d4d82021-04-16 17:02:40 -07001632{
Hao Jiange39d4d82021-04-16 17:02:40 -07001633 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1634
1635 DbusInterfaceMap sensorMap;
1636 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1637 {
1638 phosphor::logging::log<phosphor::logging::level::ERR>(
1639 "Failed to update sensor map for threshold sensor",
1640 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1641 phosphor::logging::entry("PATH=%s", path.c_str()));
1642 return false;
1643 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001644
1645 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1646 record.body.sensor_type = getSensorTypeFromPath(path);
1647 std::string type = getSensorTypeStringFromPath(path);
1648 auto typeCstr = type.c_str();
1649 auto findUnits = sensorUnits.find(typeCstr);
1650 if (findUnits != sensorUnits.end())
1651 {
1652 record.body.sensor_units_2_base =
1653 static_cast<uint8_t>(findUnits->second);
1654 } // else default 0x0 unspecified
1655
1656 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1657
Hao Jiangd2afd052020-12-10 15:09:32 -08001658 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001659 if (sensorObject == sensorMap.end())
1660 {
1661 phosphor::logging::log<phosphor::logging::level::ERR>(
1662 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001663 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001664 }
1665
1666 uint8_t entityId = 0;
1667 uint8_t entityInstance = 0x01;
1668
1669 // follow the association chain to get the parent board's entityid and
1670 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001671 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1672 entityInstance);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001673
1674 record.body.entity_id = entityId;
1675 record.body.entity_instance = entityInstance;
1676
Shakeeb Pasha93889722021-10-14 10:20:13 +05301677 double max = 0;
1678 double min = 0;
1679 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001680
1681 int16_t mValue = 0;
1682 int8_t rExp = 0;
1683 int16_t bValue = 0;
1684 int8_t bExp = 0;
1685 bool bSigned = false;
1686
1687 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1688 {
1689 phosphor::logging::log<phosphor::logging::level::ERR>(
1690 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001691 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001692 }
1693
1694 // The record.body is a struct SensorDataFullRecordBody
1695 // from sensorhandler.hpp in phosphor-ipmi-host.
1696 // The meaning of these bits appears to come from
1697 // table 43.1 of the IPMI spec.
1698 // The above 5 sensor attributes are stuffed in as follows:
1699 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1700 // Byte 22-24 are for other purposes
1701 // Byte 25 = MMMMMMMM = LSB of M
1702 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1703 // Byte 27 = BBBBBBBB = LSB of B
1704 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1705 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1706 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1707
1708 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1709 record.body.m_lsb = mValue & 0xFF;
1710
1711 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1712 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1713
1714 // move the smallest bit of the MSB into place (bit 9)
1715 // the MSbs are bits 7:8 in m_msb_and_tolerance
1716 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1717
1718 record.body.b_lsb = bValue & 0xFF;
1719
1720 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1721 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1722
1723 // move the smallest bit of the MSB into place (bit 9)
1724 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1725 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1726
1727 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1728 uint8_t rExpBits = rExp & 0x07;
1729
1730 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1731 uint8_t bExpBits = bExp & 0x07;
1732
1733 // move rExp and bExp into place
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001734 record.body.r_b_exponents = (rExpSign << 7) | (rExpBits << 4) |
1735 (bExpSign << 3) | bExpBits;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001736
1737 // Set the analog reading byte interpretation accordingly
1738 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1739
1740 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1741 // These seem redundant, but derivable from the above 5 attributes
1742 // Original comment said "todo fill out rest of units"
1743
1744 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001745 auto name = sensor::parseSdrIdFromPath(path);
Paul Fertser51136982022-08-18 12:36:41 +00001746 get_sdr::body::set_id_strlen(name.size(), &record.body);
1747 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001748 std::strncpy(record.body.id_string, name.c_str(),
1749 sizeof(record.body.id_string));
1750
Josh Lehana55c9532020-10-28 21:59:06 -07001751 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001752 details::sdrStatsTable.updateName(sensorNum, name);
Josh Lehana55c9532020-10-28 21:59:06 -07001753
Jie Yangf0a89942021-07-29 15:30:25 -07001754 bool sensorSettable = false;
1755 auto mutability =
1756 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1757 if (mutability != sensorMap.end())
1758 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05001759 sensorSettable = mappedVariant<bool>(mutability->second, "Mutable",
1760 false);
Jie Yangf0a89942021-07-29 15:30:25 -07001761 }
1762 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1763
1764 // Grant write permission to sensors deemed externally settable
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001765 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001766
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001767 IPMIThresholds thresholdData;
1768 try
1769 {
1770 thresholdData = getIPMIThresholds(sensorMap);
1771 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001772 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001773 {
1774 phosphor::logging::log<phosphor::logging::level::ERR>(
1775 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001776 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001777 }
1778
1779 if (thresholdData.criticalHigh)
1780 {
1781 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1782 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1783 IPMISensorEventEnableThresholds::criticalThreshold);
1784 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1785 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1786 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1788 record.body.discrete_reading_setting_mask[0] |=
1789 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1790 }
1791 if (thresholdData.warningHigh)
1792 {
1793 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1794 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1795 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1796 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1797 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1798 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1799 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1800 record.body.discrete_reading_setting_mask[0] |=
1801 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1802 }
1803 if (thresholdData.criticalLow)
1804 {
1805 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1806 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1807 IPMISensorEventEnableThresholds::criticalThreshold);
1808 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1809 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1810 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1811 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1812 record.body.discrete_reading_setting_mask[0] |=
1813 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1814 }
1815 if (thresholdData.warningLow)
1816 {
1817 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1818 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1819 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1820 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1821 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1822 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1823 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1824 record.body.discrete_reading_setting_mask[0] |=
1825 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1826 }
1827
1828 // everything that is readable is setable
1829 record.body.discrete_reading_setting_mask[1] =
1830 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001831 return true;
1832}
1833
Scron Chang2703b022021-07-06 15:47:45 +08001834#ifdef FEATURE_HYBRID_SENSORS
1835// Construct a type 1 SDR for discrete Sensor typed sensor.
Willy Tu11d68892022-01-20 10:37:34 -08001836void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
Scron Chang2703b022021-07-06 15:47:45 +08001837 uint16_t recordID,
1838 ipmi::sensor::IdInfoMap::const_iterator sensor,
1839 get_sdr::SensorDataFullRecord& record)
1840{
1841 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1842
1843 record.body.entity_id = sensor->second.entityType;
1844 record.body.sensor_type = sensor->second.sensorType;
1845 record.body.event_reading_type = sensor->second.sensorReadingType;
1846 record.body.entity_instance = sensor->second.instance;
1847 if (ipmi::sensor::Mutability::Write ==
1848 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1849 {
1850 get_sdr::body::init_settable_state(true, &(record.body));
1851 }
1852
1853 auto id_string = sensor->second.sensorName;
1854
1855 if (id_string.empty())
1856 {
1857 id_string = sensor->second.sensorNameFunc(sensor->second);
1858 }
1859
1860 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1861 {
1862 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1863 &(record.body));
1864 }
1865 else
1866 {
1867 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1868 }
Paul Fertser51136982022-08-18 12:36:41 +00001869 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Scron Chang2703b022021-07-06 15:47:45 +08001870 std::strncpy(record.body.id_string, id_string.c_str(),
1871 get_sdr::body::get_id_strlen(&(record.body)));
1872}
1873#endif
1874
Hao Jiange39d4d82021-04-16 17:02:40 -07001875// Construct type 3 SDR header and key (for VR and other discrete sensors)
1876void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1877 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001878{
1879 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1880 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1881
1882 get_sdr::header::set_record_id(
1883 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1884
1885 record.header.sdr_version = ipmiSdrVersion;
1886 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1887 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1888 sizeof(get_sdr::SensorDataRecordHeader);
1889 record.key.owner_id = bmcI2CAddr;
1890 record.key.owner_lun = lun;
1891 record.key.sensor_number = sensornumber;
1892
1893 record.body.entity_id = 0x00;
1894 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001895}
Willy Tu61992ad2021-03-29 15:33:20 -07001896
Hao Jiange39d4d82021-04-16 17:02:40 -07001897// Construct a type 3 SDR for VR typed sensor(daemon).
Willy Tu4eca2512022-06-20 21:14:51 -07001898bool constructVrSdr(ipmi::Context::ptr ctx,
1899 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1900 uint16_t sensorNum, uint16_t recordID,
1901 const std::string& service, const std::string& path,
Hao Jiange39d4d82021-04-16 17:02:40 -07001902 get_sdr::SensorDataEventRecord& record)
1903{
Hao Jiange39d4d82021-04-16 17:02:40 -07001904 constructEventSdrHeaderKey(sensorNum, recordID, record);
1905
1906 DbusInterfaceMap sensorMap;
1907 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1908 {
1909 phosphor::logging::log<phosphor::logging::level::ERR>(
1910 "Failed to update sensor map for VR sensor",
1911 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1912 phosphor::logging::entry("PATH=%s", path.c_str()));
1913 return false;
1914 }
Willy Tu61992ad2021-03-29 15:33:20 -07001915 // follow the association chain to get the parent board's entityid and
1916 // entityInstance
Willy Tu4eca2512022-06-20 21:14:51 -07001917 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
1918 record.body.entity_id,
Willy Tu61992ad2021-03-29 15:33:20 -07001919 record.body.entity_instance);
1920
1921 // Sensor type is hardcoded as a module/board type instead of parsing from
1922 // sensor path. This is because VR control is allocated in an independent
1923 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1924 // types.
1925 static constexpr const uint8_t module_board_type = 0x15;
1926 record.body.sensor_type = module_board_type;
1927 record.body.event_reading_type = 0x00;
1928
1929 record.body.sensor_record_sharing_1 = 0x00;
1930 record.body.sensor_record_sharing_2 = 0x00;
1931
1932 // populate sensor name from path
1933 auto name = sensor::parseSdrIdFromPath(path);
1934 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
Paul Fertser51136982022-08-18 12:36:41 +00001935 get_sdr::body::set_id_strlen(nameSize, &record.body);
1936 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
Willy Tu61992ad2021-03-29 15:33:20 -07001937 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1938 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1939
1940 // Remember the sensor name, as determined for this sensor number
Harvey.Wu0e7a8af2022-06-10 16:46:46 +08001941 details::sdrStatsTable.updateName(sensorNum, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001942
1943 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001944}
1945
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001946static inline uint16_t getNumberOfSensors()
1947{
1948 return std::min(getSensorTree().size(), maxIPMISensors);
1949}
1950
Willy Tu4eca2512022-06-20 21:14:51 -07001951static int getSensorDataRecord(
1952 ipmi::Context::ptr ctx,
1953 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1954 std::vector<uint8_t>& recordData, uint16_t recordID,
1955 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001956{
1957 size_t fruCount = 0;
1958 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1959 if (ret != ipmi::ccSuccess)
1960 {
1961 phosphor::logging::log<phosphor::logging::level::ERR>(
1962 "getSensorDataRecord: getFruSdrCount error");
1963 return GENERAL_ERROR;
1964 }
1965
Harvey Wu05d17c02021-09-15 08:46:59 +08001966 const auto& entityRecords =
1967 ipmi::sensor::EntityInfoMapContainer::getContainer()
1968 ->getIpmiEntityRecords();
1969 size_t entityCount = entityRecords.size();
1970
selvaganapathi7b2e5502023-02-14 07:10:47 +05301971 recordData.clear();
Harvey Wu05d17c02021-09-15 08:46:59 +08001972 size_t lastRecord = getNumberOfSensors() + fruCount +
1973 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001974 if (recordID == lastRecordIndex)
1975 {
1976 recordID = lastRecord;
1977 }
1978 if (recordID > lastRecord)
1979 {
1980 phosphor::logging::log<phosphor::logging::level::ERR>(
1981 "getSensorDataRecord: recordID > lastRecord error");
1982 return GENERAL_ERROR;
1983 }
1984
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001985 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001986 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001987 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001988
Harvey Wu05d17c02021-09-15 08:46:59 +08001989 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1990 {
1991 // handle type 8 entity map records
1992 ipmi::sensor::EntityInfoMap::const_iterator entity =
1993 entityRecords.find(static_cast<uint8_t>(
1994 sdrIndex - fruCount - ipmi::storage::type12Count));
1995 if (entity == entityRecords.end())
1996 {
1997 return IPMI_CC_SENSOR_INVALID;
1998 }
1999 recordData = ipmi::storage::getType8SDRs(entity, recordID);
2000 }
2001 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07002002 {
2003 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08002004 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002005 if (type12Index >= ipmi::storage::type12Count)
2006 {
2007 phosphor::logging::log<phosphor::logging::level::ERR>(
2008 "getSensorDataRecord: type12Index error");
2009 return GENERAL_ERROR;
2010 }
2011 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
2012 }
2013 else
2014 {
2015 // handle fru records
2016 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08002017 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002018 if (ret != IPMI_CC_OK)
2019 {
2020 return GENERAL_ERROR;
2021 }
2022 data.header.record_id_msb = recordID >> 8;
2023 data.header.record_id_lsb = recordID & 0xFF;
selvaganapathi7b2e5502023-02-14 07:10:47 +05302024 recordData.insert(recordData.end(),
2025 reinterpret_cast<uint8_t*>(&data),
2026 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
Willy Tu38e7a2b2021-03-29 15:09:56 -07002027 }
2028
2029 return 0;
2030 }
2031
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002032 // Perform a incremental scan of the SDR Record ID's and translate the
2033 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2034 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2035 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2036 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002037 std::string connection;
2038 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002039 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002040 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002041 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002042 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002043 // LUN 0 has one reserved sensor number. Compensate here by adding one
2044 // to the record ID
2045 sensNumFromRecID = recordID + 1;
Harvey Wu75893062023-03-22 17:17:31 +08002046 ctx->lun = lun1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002047 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002048 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002049 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002050 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2051 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2052 // rules governing its use.
2053 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Harvey Wu75893062023-03-22 17:17:31 +08002054 ctx->lun = lun3;
Johnathan Manteyce982772021-07-28 15:08:30 -07002055 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002056
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002057 auto status = getSensorConnection(ctx,
2058 static_cast<uint8_t>(sensNumFromRecID),
2059 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002060 if (status)
2061 {
2062 phosphor::logging::log<phosphor::logging::level::ERR>(
2063 "getSensorDataRecord: getSensorConnection error");
2064 return GENERAL_ERROR;
2065 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002066 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002067 // Return an error on LUN 2 assingments, and any sensor number beyond the
2068 // range of LUN 3
2069 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2070 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002071 {
2072 phosphor::logging::log<phosphor::logging::level::ERR>(
2073 "getSensorDataRecord: invalidSensorNumber");
2074 return GENERAL_ERROR;
2075 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002076 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2077 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2078
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002079 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2080 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002081 {
2082 phosphor::logging::log<phosphor::logging::level::ERR>(
2083 "getSensorDataRecord: sensor record mismatch");
2084 return GENERAL_ERROR;
2085 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002086
Willy Tu38e7a2b2021-03-29 15:09:56 -07002087 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002088 if (std::find(interfaces.begin(), interfaces.end(),
2089 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002090 {
Willy Tu11d68892022-01-20 10:37:34 -08002091 get_sdr::SensorDataFullRecord record = {};
Willy Tu38e7a2b2021-03-29 15:09:56 -07002092
Hao Jiange39d4d82021-04-16 17:02:40 -07002093 // If the request doesn't read SDR body, construct only header and key
2094 // part to avoid additional DBus transaction.
2095 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2096 {
2097 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2098 }
Willy Tu4eca2512022-06-20 21:14:51 -07002099 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2100 recordID, connection, path, record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002101 {
2102 return GENERAL_ERROR;
2103 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002104
selvaganapathi7b2e5502023-02-14 07:10:47 +05302105 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2106 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002107
2108 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002109 }
Willy Tu61992ad2021-03-29 15:33:20 -07002110
Scron Chang2703b022021-07-06 15:47:45 +08002111#ifdef FEATURE_HYBRID_SENSORS
2112 if (auto sensor = findStaticSensor(path);
2113 sensor != ipmi::sensor::sensors.end() &&
2114 getSensorEventTypeFromPath(path) !=
2115 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2116 {
Willy Tu11d68892022-01-20 10:37:34 -08002117 get_sdr::SensorDataFullRecord record = {};
Scron Chang2703b022021-07-06 15:47:45 +08002118
2119 // If the request doesn't read SDR body, construct only header and key
2120 // part to avoid additional DBus transaction.
2121 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2122 {
2123 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2124 }
2125 else
2126 {
2127 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2128 }
2129
selvaganapathi7b2e5502023-02-14 07:10:47 +05302130 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2131 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Scron Chang2703b022021-07-06 15:47:45 +08002132
2133 return 0;
2134 }
2135#endif
2136
Willy Tu61992ad2021-03-29 15:33:20 -07002137 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002138 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2139 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002140 {
Willy Tu11d68892022-01-20 10:37:34 -08002141 get_sdr::SensorDataEventRecord record = {};
Willy Tu61992ad2021-03-29 15:33:20 -07002142
Hao Jiange39d4d82021-04-16 17:02:40 -07002143 // If the request doesn't read SDR body, construct only header and key
2144 // part to avoid additional DBus transaction.
2145 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2146 {
2147 constructEventSdrHeaderKey(sensorNum, recordID, record);
2148 }
Willy Tu4eca2512022-06-20 21:14:51 -07002149 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2150 connection, path, record))
Hao Jiange39d4d82021-04-16 17:02:40 -07002151 {
2152 return GENERAL_ERROR;
2153 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302154 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2155 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002156 }
2157
Willy Tude54f482021-01-26 15:59:09 -08002158 return 0;
2159}
2160
2161/** @brief implements the get SDR Info command
2162 * @param count - Operation
2163 *
2164 * @returns IPMI completion code plus response data
2165 * - sdrCount - sensor/SDR count
2166 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2167 */
2168static ipmi::RspType<uint8_t, // respcount
2169 uint8_t, // dynamic population flags
2170 uint32_t // last time a sensor was added
2171 >
2172 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2173 std::optional<uint8_t> count)
2174{
2175 auto& sensorTree = getSensorTree();
2176 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002177 uint16_t recordID = 0;
2178 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002179 // Sensors are dynamically allocated, and there is at least one LUN
2180 uint8_t lunsAndDynamicPopulation = 0x80;
2181 constexpr uint8_t getSdrCount = 0x01;
2182 constexpr uint8_t getSensorCount = 0x00;
2183
2184 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2185 {
2186 return ipmi::responseResponseError();
2187 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002188 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002189 if (count.value_or(0) == getSdrCount)
2190 {
Willy Tu4eca2512022-06-20 21:14:51 -07002191 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2192
Harvey Wu75893062023-03-22 17:17:31 +08002193 if (ctx->lun == lun1)
Harvey Wua3476272023-03-22 10:09:38 +08002194 {
2195 recordID += maxSensorsPerLUN;
2196 }
Harvey Wu75893062023-03-22 17:17:31 +08002197 else if (ctx->lun == lun3)
Harvey Wua3476272023-03-22 10:09:38 +08002198 {
2199 recordID += maxSensorsPerLUN * 2;
2200 }
2201
Harvey Wu75893062023-03-22 17:17:31 +08002202 // Count the number of Type 1h, Type 2h, Type 11h, Type 12h SDR entries
2203 // assigned to the LUN
Willy Tu4eca2512022-06-20 21:14:51 -07002204 while (!getSensorDataRecord(
2205 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2206 record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002207 {
2208 get_sdr::SensorDataRecordHeader* hdr =
2209 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002210 record.data());
selvaganapathi7b2e5502023-02-14 07:10:47 +05302211 if (!hdr)
2212 {
2213 continue;
2214 }
2215
2216 if (hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
Willy Tude54f482021-01-26 15:59:09 -08002217 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002218 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002219 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002220 record.data());
2221 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002222 {
2223 sdrCount++;
2224 }
2225 }
selvaganapathi7b2e5502023-02-14 07:10:47 +05302226 else if (hdr->record_type == get_sdr::SENSOR_DATA_COMPACT_RECORD)
2227 {
2228 get_sdr::SensorDataCompactRecord* recordData =
2229 reinterpret_cast<get_sdr::SensorDataCompactRecord*>(
2230 record.data());
2231 if (ctx->lun == recordData->key.owner_lun)
2232 {
2233 sdrCount++;
2234 }
2235 }
Harvey Wua3476272023-03-22 10:09:38 +08002236 else if (hdr->record_type == get_sdr::SENSOR_DATA_FRU_RECORD ||
2237 hdr->record_type == get_sdr::SENSOR_DATA_MGMT_CTRL_LOCATOR)
selvaganapathi7b2e5502023-02-14 07:10:47 +05302238 {
2239 sdrCount++;
2240 }
Harvey Wua3476272023-03-22 10:09:38 +08002241
2242 // Because response count data is 1 byte, so sdrCount need to avoid
2243 // overflow.
2244 if (sdrCount == maxSensorsPerLUN)
2245 {
2246 break;
2247 }
Willy Tude54f482021-01-26 15:59:09 -08002248 }
2249 }
2250 else if (count.value_or(0) == getSensorCount)
2251 {
2252 // Return the number of sensors attached to the LUN
Harvey Wu75893062023-03-22 17:17:31 +08002253 if ((ctx->lun == lun0) && (numSensors > 0))
Willy Tude54f482021-01-26 15:59:09 -08002254 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002255 sdrCount = (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN
2256 : numSensors;
Willy Tude54f482021-01-26 15:59:09 -08002257 }
Harvey Wu75893062023-03-22 17:17:31 +08002258 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
Willy Tude54f482021-01-26 15:59:09 -08002259 {
2260 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2261 ? maxSensorsPerLUN
2262 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2263 }
Harvey Wu75893062023-03-22 17:17:31 +08002264 else if (ctx->lun == lun3)
Willy Tude54f482021-01-26 15:59:09 -08002265 {
2266 if (numSensors <= maxIPMISensors)
2267 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002268 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2269 maxSensorsPerLUN;
Willy Tude54f482021-01-26 15:59:09 -08002270 }
2271 else
2272 {
2273 // error
2274 throw std::out_of_range(
2275 "Maximum number of IPMI sensors exceeded.");
2276 }
2277 }
2278 }
2279 else
2280 {
2281 return ipmi::responseInvalidFieldRequest();
2282 }
2283
2284 // Get Sensor count. This returns the number of sensors
2285 if (numSensors > 0)
2286 {
2287 lunsAndDynamicPopulation |= 1;
2288 }
2289 if (numSensors > maxSensorsPerLUN)
2290 {
2291 lunsAndDynamicPopulation |= 2;
2292 }
2293 if (numSensors >= (maxSensorsPerLUN * 2))
2294 {
2295 lunsAndDynamicPopulation |= 8;
2296 }
2297 if (numSensors > maxIPMISensors)
2298 {
2299 // error
2300 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2301 }
2302
2303 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2304 sdrLastAdd);
2305}
2306
2307/* end sensor commands */
2308
2309/* storage commands */
2310
2311ipmi::RspType<uint8_t, // sdr version
2312 uint16_t, // record count
2313 uint16_t, // free space
2314 uint32_t, // most recent addition
2315 uint32_t, // most recent erase
2316 uint8_t // operationSupport
2317 >
2318 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2319{
2320 auto& sensorTree = getSensorTree();
2321 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002322 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002323 {
2324 return ipmi::responseResponseError();
2325 }
2326
2327 size_t fruCount = 0;
2328 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2329 if (ret != ipmi::ccSuccess)
2330 {
2331 return ipmi::response(ret);
2332 }
2333
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002334 uint16_t recordCount = getNumberOfSensors() + fruCount +
2335 ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002336
2337 uint8_t operationSupport = static_cast<uint8_t>(
2338 SdrRepositoryInfoOps::overflow); // write not supported
2339
2340 operationSupport |=
2341 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2342 operationSupport |= static_cast<uint8_t>(
2343 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2344 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2345 unspecifiedFreeSpace, sdrLastAdd,
2346 sdrLastRemove, operationSupport);
2347}
2348
2349/** @brief implements the get SDR allocation info command
2350 *
2351 * @returns IPMI completion code plus response data
2352 * - allocUnits - Number of possible allocation units
2353 * - allocUnitSize - Allocation unit size in bytes.
2354 * - allocUnitFree - Number of free allocation units
2355 * - allocUnitLargestFree - Largest free block in allocation units
2356 * - maxRecordSize - Maximum record size in allocation units.
2357 */
2358ipmi::RspType<uint16_t, // allocUnits
2359 uint16_t, // allocUnitSize
2360 uint16_t, // allocUnitFree
2361 uint16_t, // allocUnitLargestFree
2362 uint8_t // maxRecordSize
2363 >
2364 ipmiStorageGetSDRAllocationInfo()
2365{
2366 // 0000h unspecified number of alloc units
2367 constexpr uint16_t allocUnits = 0;
2368
2369 constexpr uint16_t allocUnitFree = 0;
2370 constexpr uint16_t allocUnitLargestFree = 0;
2371 // only allow one block at a time
2372 constexpr uint8_t maxRecordSize = 1;
2373
2374 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2375 allocUnitLargestFree, maxRecordSize);
2376}
2377
2378/** @brief implements the reserve SDR command
2379 * @returns IPMI completion code plus response data
2380 * - sdrReservationID
2381 */
2382ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2383{
2384 sdrReservationID++;
2385 if (sdrReservationID == 0)
2386 {
2387 sdrReservationID++;
2388 }
2389
2390 return ipmi::responseSuccess(sdrReservationID);
2391}
2392
2393ipmi::RspType<uint16_t, // next record ID
2394 std::vector<uint8_t> // payload
2395 >
2396 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2397 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2398{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002399 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002400 // reservation required for partial reads with non zero offset into
2401 // record
2402 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2403 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002404 phosphor::logging::log<phosphor::logging::level::ERR>(
2405 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002406 return ipmi::responseInvalidReservationId();
2407 }
Willy Tude54f482021-01-26 15:59:09 -08002408 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2409 if (ret != ipmi::ccSuccess)
2410 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002411 phosphor::logging::log<phosphor::logging::level::ERR>(
2412 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002413 return ipmi::response(ret);
2414 }
2415
Harvey Wu05d17c02021-09-15 08:46:59 +08002416 const auto& entityRecords =
2417 ipmi::sensor::EntityInfoMapContainer::getContainer()
2418 ->getIpmiEntityRecords();
2419 int entityCount = entityRecords.size();
2420
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002421 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002422 size_t lastRecord = getNumberOfSensors() + fruCount +
2423 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002424 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2425
2426 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002427 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002428 phosphor::logging::log<phosphor::logging::level::ERR>(
2429 "ipmiStorageGetSDR: getSensorSubtree error");
2430 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002431 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002432
Willy Tu4eca2512022-06-20 21:14:51 -07002433 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2434
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002435 std::vector<uint8_t> record;
Willy Tu4eca2512022-06-20 21:14:51 -07002436 if (getSensorDataRecord(
2437 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2438 record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002439 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002440 phosphor::logging::log<phosphor::logging::level::ERR>(
2441 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002442 return ipmi::responseInvalidFieldRequest();
2443 }
Willy Tude54f482021-01-26 15:59:09 -08002444 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002445 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002446 if (!hdr)
2447 {
2448 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002449 "ipmiStorageGetSDR: record header is null");
2450 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002451 }
2452
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -05002453 size_t sdrLength = sizeof(get_sdr::SensorDataRecordHeader) +
2454 hdr->record_length;
Vernon Mauery6dbea082023-07-21 11:43:00 -07002455 if (offset >= sdrLength)
2456 {
2457 phosphor::logging::log<phosphor::logging::level::ERR>(
2458 "ipmiStorageGetSDR: offset is outside the record");
2459 return ipmi::responseParmOutOfRange();
2460 }
Willy Tude54f482021-01-26 15:59:09 -08002461 if (sdrLength < (offset + bytesToRead))
2462 {
2463 bytesToRead = sdrLength - offset;
2464 }
2465
2466 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2467 if (!respStart)
2468 {
2469 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002470 "ipmiStorageGetSDR: record is null");
2471 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002472 }
2473
2474 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002475
Willy Tude54f482021-01-26 15:59:09 -08002476 return ipmi::responseSuccess(nextRecordId, recordData);
2477}
adarshgrami042e9db2022-09-15 10:34:34 +05302478namespace dcmi
2479{
2480
2481ipmi::RspType<uint8_t, // No of instances for requested id
2482 uint8_t, // No of record ids in the response
2483 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2484 // IDs
2485 >
2486 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
Willy Tu0679e4b2022-11-11 14:34:33 -08002487 uint8_t entityInstance,
2488 [[maybe_unused]] uint8_t instanceStart)
adarshgrami042e9db2022-09-15 10:34:34 +05302489{
2490 auto match = ipmi::dcmi::validEntityId.find(entityId);
2491 if (match == ipmi::dcmi::validEntityId.end())
2492 {
2493 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", entityId));
2494
2495 return ipmi::responseInvalidFieldRequest();
2496 }
2497
2498 if (sensorType != ipmi::dcmi::temperatureSensorType)
2499 {
2500 log<level::ERR>("Invalid sensor type",
2501 entry("SENSOR_TYPE=%d", sensorType));
2502
2503 return ipmi::responseInvalidFieldRequest();
2504 }
2505 auto& sensorTree = getSensorTree();
2506 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2507 {
2508 return ipmi::responseUnspecifiedError();
2509 }
2510
2511 std::vector<uint16_t> sensorRec{};
2512 uint8_t numInstances = 0;
2513
Willy Tu0679e4b2022-11-11 14:34:33 -08002514 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
adarshgrami042e9db2022-09-15 10:34:34 +05302515 for (const auto& sensor : sensorTree)
2516 {
2517 auto sensorTypeValue = getSensorTypeFromPath(sensor.first);
2518 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2519 {
2520 continue;
2521 }
2522 const auto& connection = sensor.second.begin()->first;
2523
2524 DbusInterfaceMap sensorMap;
2525 if (!getSensorMap(ctx, connection, sensor.first, sensorMap,
2526 sensorMapSdrUpdatePeriod))
2527 {
2528 phosphor::logging::log<phosphor::logging::level::ERR>(
2529 "Failed to update sensor map for threshold sensor",
2530 phosphor::logging::entry("SERVICE=%s", connection.c_str()),
2531 phosphor::logging::entry("PATH=%s", sensor.first.c_str()));
2532 continue;
2533 }
2534 uint8_t entityIdValue = 0;
2535 uint8_t entityInstanceValue = 0;
Willy Tu0679e4b2022-11-11 14:34:33 -08002536 updateIpmiFromAssociation(
2537 sensor.first,
2538 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2539 sensorMap, entityIdValue, entityInstanceValue);
adarshgrami042e9db2022-09-15 10:34:34 +05302540 if (!entityInstance)
2541 {
2542 if (entityIdValue == match->first || entityIdValue == match->second)
2543 {
2544 auto recordId = getSensorNumberFromPath(sensor.first);
2545 if (recordId != invalidSensorNumber)
2546 {
2547 numInstances++;
2548 if (numInstances <= ipmi::dcmi::maxRecords)
2549 {
2550 sensorRec.push_back(recordId);
2551 }
2552 }
2553 }
2554 }
2555 else
2556 {
2557 if (entityIdValue == match->first || entityIdValue == match->second)
2558 {
2559 if (entityInstance == entityInstanceValue)
2560 {
2561 auto recordId = getSensorNumberFromPath(sensor.first);
2562 if ((recordId != invalidSensorNumber) && sensorRec.empty())
2563 {
2564 sensorRec.push_back(recordId);
2565 }
2566 }
2567 numInstances++;
2568 }
2569 }
2570 }
2571 if (sensorRec.empty())
2572 {
2573 return ipmi::responseSensorInvalid();
2574 }
2575 uint8_t numRecords = sensorRec.size();
2576 return ipmi::responseSuccess(numInstances, numRecords, sensorRec);
2577}
2578} // namespace dcmi
2579
Willy Tude54f482021-01-26 15:59:09 -08002580/* end storage commands */
2581
2582void registerSensorFunctions()
2583{
2584 // <Platform Event>
2585 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2586 ipmi::sensor_event::cmdPlatformEvent,
2587 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2588
Willy Tudbafbce2021-03-29 00:37:05 -07002589 // <Set Sensor Reading and Event Status>
2590 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2591 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2592 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002593
Willy Tude54f482021-01-26 15:59:09 -08002594 // <Get Sensor Reading>
2595 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2596 ipmi::sensor_event::cmdGetSensorReading,
2597 ipmi::Privilege::User, ipmiSenGetSensorReading);
2598
2599 // <Get Sensor Threshold>
2600 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2601 ipmi::sensor_event::cmdGetSensorThreshold,
2602 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2603
2604 // <Set Sensor Threshold>
2605 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2606 ipmi::sensor_event::cmdSetSensorThreshold,
2607 ipmi::Privilege::Operator,
2608 ipmiSenSetSensorThresholds);
2609
2610 // <Get Sensor Event Enable>
2611 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2612 ipmi::sensor_event::cmdGetSensorEventEnable,
2613 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2614
2615 // <Get Sensor Event Status>
2616 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2617 ipmi::sensor_event::cmdGetSensorEventStatus,
2618 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2619
2620 // register all storage commands for both Sensor and Storage command
2621 // versions
2622
2623 // <Get SDR Repository Info>
2624 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2625 ipmi::storage::cmdGetSdrRepositoryInfo,
2626 ipmi::Privilege::User,
2627 ipmiStorageGetSDRRepositoryInfo);
2628
2629 // <Get Device SDR Info>
2630 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2631 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2632 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2633
2634 // <Get SDR Allocation Info>
2635 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2636 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2637 ipmi::Privilege::User,
2638 ipmiStorageGetSDRAllocationInfo);
2639
2640 // <Reserve SDR Repo>
2641 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2642 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2643 ipmi::Privilege::User, ipmiStorageReserveSDR);
2644
2645 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2646 ipmi::storage::cmdReserveSdrRepository,
2647 ipmi::Privilege::User, ipmiStorageReserveSDR);
2648
2649 // <Get Sdr>
2650 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2651 ipmi::sensor_event::cmdGetDeviceSdr,
2652 ipmi::Privilege::User, ipmiStorageGetSDR);
2653
2654 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2655 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2656 ipmiStorageGetSDR);
adarshgrami042e9db2022-09-15 10:34:34 +05302657 // <Get DCMI Sensor Info>
2658 ipmi::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2659 ipmi::dcmi::cmdGetDcmiSensorInfo,
Chau Lyd74df5f2023-05-25 10:33:00 +00002660 ipmi::Privilege::Operator,
adarshgrami042e9db2022-09-15 10:34:34 +05302661 ipmi::dcmi::getSensorInfo);
Willy Tude54f482021-01-26 15:59:09 -08002662}
2663} // namespace ipmi