blob: 601fea62ed772974950d4e11128fb3a7ac9e125a [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
Harvey Wu05d17c02021-09-15 08:46:59 +080022#include "entity_map_json.hpp"
Willy Tude54f482021-01-26 15:59:09 -080023
24#include <algorithm>
25#include <array>
26#include <boost/algorithm/string.hpp>
27#include <boost/container/flat_map.hpp>
28#include <chrono>
29#include <cmath>
30#include <cstring>
31#include <iostream>
32#include <ipmid/api.hpp>
33#include <ipmid/types.hpp>
34#include <ipmid/utils.hpp>
35#include <map>
36#include <memory>
37#include <optional>
38#include <phosphor-logging/log.hpp>
39#include <sdbusplus/bus.hpp>
40#include <stdexcept>
41#include <string>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000042#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080043#include <utility>
44#include <variant>
45
Scron Chang2703b022021-07-06 15:47:45 +080046#ifdef FEATURE_HYBRID_SENSORS
47
48#include "sensordatahandler.hpp"
49namespace ipmi
50{
51namespace sensor
52{
53extern const IdInfoMap sensors;
54} // namespace sensor
55} // namespace ipmi
56#endif
57
JeffLind950f412021-10-20 18:49:34 +080058constexpr std::array<const char*, 7> suffixes = {
59 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
60 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080061namespace ipmi
62{
Hao Jiangd48c9212021-02-03 15:45:06 -080063
64using phosphor::logging::entry;
65using phosphor::logging::level;
66using phosphor::logging::log;
67
Willy Tude54f482021-01-26 15:59:09 -080068static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070069static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080070
Willy Tu38e7a2b2021-03-29 15:09:56 -070071// BMC I2C address is generally at 0x20
72static constexpr uint8_t bmcI2CAddr = 0x20;
73
Willy Tude54f482021-01-26 15:59:09 -080074constexpr size_t maxSDRTotalSize =
75 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
76constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
77
78static uint16_t sdrReservationID;
79static uint32_t sdrLastAdd = noTimestamp;
80static uint32_t sdrLastRemove = noTimestamp;
81static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070082
83// The IPMI spec defines four Logical Units (LUN), each capable of supporting
84// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
85// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
86// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
87// location.
88static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
89
90static constexpr size_t lun0MaxSensorNum = 0xfe;
91static constexpr size_t lun1MaxSensorNum = 0x1fe;
92static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -080093static constexpr int GENERAL_ERROR = -1;
94
Willy Tude54f482021-01-26 15:59:09 -080095static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
96
97// Specify the comparison required to sort and find char* map objects
98struct CmpStr
99{
100 bool operator()(const char* a, const char* b) const
101 {
102 return std::strcmp(a, b) < 0;
103 }
104};
105const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
106 sensorUnits{{{"temperature", SensorUnits::degreesC},
107 {"voltage", SensorUnits::volts},
108 {"current", SensorUnits::amps},
109 {"fan_tach", SensorUnits::rpm},
110 {"power", SensorUnits::watts}}};
111
112void registerSensorFunctions() __attribute__((constructor));
113
114static sdbusplus::bus::match::match sensorAdded(
115 *getSdBus(),
116 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
117 "sensors/'",
118 [](sdbusplus::message::message& m) {
119 getSensorTree().clear();
120 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
121 std::chrono::system_clock::now().time_since_epoch())
122 .count();
123 });
124
125static sdbusplus::bus::match::match sensorRemoved(
126 *getSdBus(),
127 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
128 "sensors/'",
129 [](sdbusplus::message::message& m) {
130 getSensorTree().clear();
131 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
132 std::chrono::system_clock::now().time_since_epoch())
133 .count();
134 });
135
136// this keeps track of deassertions for sensor event status command. A
137// deasertion can only happen if an assertion was seen first.
138static boost::container::flat_map<
139 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
140 thresholdDeassertMap;
141
142static sdbusplus::bus::match::match thresholdChanged(
143 *getSdBus(),
144 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
145 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
146 [](sdbusplus::message::message& m) {
147 boost::container::flat_map<std::string, std::variant<bool, double>>
148 values;
149 m.read(std::string(), values);
150
151 auto findAssert =
152 std::find_if(values.begin(), values.end(), [](const auto& pair) {
153 return pair.first.find("Alarm") != std::string::npos;
154 });
155 if (findAssert != values.end())
156 {
157 auto ptr = std::get_if<bool>(&(findAssert->second));
158 if (ptr == nullptr)
159 {
160 phosphor::logging::log<phosphor::logging::level::ERR>(
161 "thresholdChanged: Assert non bool");
162 return;
163 }
164 if (*ptr)
165 {
166 phosphor::logging::log<phosphor::logging::level::INFO>(
167 "thresholdChanged: Assert",
168 phosphor::logging::entry("SENSOR=%s", m.get_path()));
169 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
170 }
171 else
172 {
173 auto& value =
174 thresholdDeassertMap[m.get_path()][findAssert->first];
175 if (value)
176 {
177 phosphor::logging::log<phosphor::logging::level::INFO>(
178 "thresholdChanged: deassert",
179 phosphor::logging::entry("SENSOR=%s", m.get_path()));
180 value = *ptr;
181 }
182 }
183 }
184 });
185
Hao Jiangd2afd052020-12-10 15:09:32 -0800186namespace sensor
187{
188static constexpr const char* vrInterface =
189 "xyz.openbmc_project.Control.VoltageRegulatorMode";
190static constexpr const char* sensorInterface =
191 "xyz.openbmc_project.Sensor.Value";
192} // namespace sensor
193
Willy Tude54f482021-01-26 15:59:09 -0800194static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
195 double& min)
196{
197 max = 127;
198 min = -128;
199
Hao Jiangd2afd052020-12-10 15:09:32 -0800200 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800201 auto critical =
202 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
203 auto warning =
204 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
205
206 if (sensorObject != sensorMap.end())
207 {
208 auto maxMap = sensorObject->second.find("MaxValue");
209 auto minMap = sensorObject->second.find("MinValue");
210
211 if (maxMap != sensorObject->second.end())
212 {
213 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
214 }
215 if (minMap != sensorObject->second.end())
216 {
217 min = std::visit(VariantToDoubleVisitor(), minMap->second);
218 }
219 }
220 if (critical != sensorMap.end())
221 {
222 auto lower = critical->second.find("CriticalLow");
223 auto upper = critical->second.find("CriticalHigh");
224 if (lower != critical->second.end())
225 {
226 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300227 if (std::isfinite(value))
228 {
229 min = std::min(value, min);
230 }
Willy Tude54f482021-01-26 15:59:09 -0800231 }
232 if (upper != critical->second.end())
233 {
234 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300235 if (std::isfinite(value))
236 {
237 max = std::max(value, max);
238 }
Willy Tude54f482021-01-26 15:59:09 -0800239 }
240 }
241 if (warning != sensorMap.end())
242 {
243
244 auto lower = warning->second.find("WarningLow");
245 auto upper = warning->second.find("WarningHigh");
246 if (lower != warning->second.end())
247 {
248 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300249 if (std::isfinite(value))
250 {
251 min = std::min(value, min);
252 }
Willy Tude54f482021-01-26 15:59:09 -0800253 }
254 if (upper != warning->second.end())
255 {
256 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300257 if (std::isfinite(value))
258 {
259 max = std::max(value, max);
260 }
Willy Tude54f482021-01-26 15:59:09 -0800261 }
262 }
263}
264
265static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700266 std::string sensorPath, DbusInterfaceMap& sensorMap,
267 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800268{
Scron Chang2703b022021-07-06 15:47:45 +0800269#ifdef FEATURE_HYBRID_SENSORS
270 if (auto sensor = findStaticSensor(sensorPath);
271 sensor != ipmi::sensor::sensors.end() &&
272 getSensorEventTypeFromPath(sensorPath) !=
273 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
274 {
275 // If the incoming sensor is a discrete sensor, it might fail in
276 // getManagedObjects(), return true, and use its own getFunc to get
277 // value.
278 return true;
279 }
280#endif
281
Willy Tude54f482021-01-26 15:59:09 -0800282 static boost::container::flat_map<
283 std::string, std::chrono::time_point<std::chrono::steady_clock>>
284 updateTimeMap;
285
286 auto updateFind = updateTimeMap.find(sensorConnection);
287 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
288 if (updateFind != updateTimeMap.end())
289 {
290 lastUpdate = updateFind->second;
291 }
292
293 auto now = std::chrono::steady_clock::now();
294
295 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700296 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800297 {
Willy Tude54f482021-01-26 15:59:09 -0800298 ObjectValueTree managedObjects;
299 boost::system::error_code ec = getManagedObjects(
300 ctx, sensorConnection.c_str(), "/", managedObjects);
301 if (ec)
302 {
303 phosphor::logging::log<phosphor::logging::level::ERR>(
304 "GetMangagedObjects for getSensorMap failed",
305 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
306
307 return false;
308 }
309
310 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700311 // Update time after finish building the map which allow the
312 // data to be cached for updatePeriod plus the build time.
313 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800314 }
315 auto connection = SensorCache.find(sensorConnection);
316 if (connection == SensorCache.end())
317 {
318 return false;
319 }
320 auto path = connection->second.find(sensorPath);
321 if (path == connection->second.end())
322 {
323 return false;
324 }
325 sensorMap = path->second;
326
327 return true;
328}
329
Hao Jiangd2afd052020-12-10 15:09:32 -0800330namespace sensor
331{
Hao Jiangd48c9212021-02-03 15:45:06 -0800332// Read VR profiles from sensor(daemon) interface
333static std::optional<std::vector<std::string>>
334 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800335{
336 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800337 auto supportedProperty = object.find("Supported");
338 if (supportedProperty == object.end() ||
339 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800340 {
341 phosphor::logging::log<phosphor::logging::level::ERR>(
342 "Missing the required Supported and Selected properties");
343 return std::nullopt;
344 }
345
346 const auto profilesPtr =
347 std::get_if<std::vector<std::string>>(&supportedProperty->second);
348
349 if (profilesPtr == nullptr)
350 {
351 phosphor::logging::log<phosphor::logging::level::ERR>(
352 "property is not array of string");
353 return std::nullopt;
354 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800355 return *profilesPtr;
356}
357
358// Calculate VR Mode from input IPMI discrete event bytes
359static std::optional<std::string>
360 calculateVRMode(uint15_t assertOffset,
361 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
362{
363 // get VR mode profiles from Supported Interface
364 auto profiles = getSupportedVrProfiles(VRObject);
365 if (!profiles)
366 {
367 return std::nullopt;
368 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800369
370 // interpret IPMI cmd bits into profiles' index
371 long unsigned int index = 0;
372 // only one bit should be set and the highest bit should not be used.
373 if (assertOffset == 0 || assertOffset == (1u << 15) ||
374 (assertOffset & (assertOffset - 1)))
375 {
376 phosphor::logging::log<phosphor::logging::level::ERR>(
377 "IPMI cmd format incorrect",
378
379 phosphor::logging::entry("BYTES=%#02x",
380 static_cast<uint16_t>(assertOffset)));
381 return std::nullopt;
382 }
383
384 while (assertOffset != 1)
385 {
386 assertOffset >>= 1;
387 index++;
388 }
389
Hao Jiangd48c9212021-02-03 15:45:06 -0800390 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800391 {
392 phosphor::logging::log<phosphor::logging::level::ERR>(
393 "profile index out of boundary");
394 return std::nullopt;
395 }
396
Hao Jiangd48c9212021-02-03 15:45:06 -0800397 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800398}
399
400// Calculate sensor value from IPMI reading byte
401static std::optional<double>
402 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
403 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
404{
405 if (valueObject.find("Value") == valueObject.end())
406 {
407 phosphor::logging::log<phosphor::logging::level::ERR>(
408 "Missing the required Value property");
409 return std::nullopt;
410 }
411
412 double max = 0;
413 double min = 0;
414 getSensorMaxMin(sensorMap, max, min);
415
416 int16_t mValue = 0;
417 int16_t bValue = 0;
418 int8_t rExp = 0;
419 int8_t bExp = 0;
420 bool bSigned = false;
421
422 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
423 {
424 return std::nullopt;
425 }
426
427 double value = bSigned ? ((int8_t)reading) : reading;
428
429 value *= ((double)mValue);
430 value += ((double)bValue) * std::pow(10.0, bExp);
431 value *= std::pow(10.0, rExp);
432
433 return value;
434}
435
Willy Tu38e7a2b2021-03-29 15:09:56 -0700436// Extract file name from sensor path as the sensors SDR ID. Simplify the name
437// if it is too long.
438std::string parseSdrIdFromPath(const std::string& path)
439{
440 std::string name;
441 size_t nameStart = path.rfind("/");
442 if (nameStart != std::string::npos)
443 {
444 name = path.substr(nameStart + 1, std::string::npos - nameStart);
445 }
446
Willy Tu38e7a2b2021-03-29 15:09:56 -0700447 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
448 {
449 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800450 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700451 {
JeffLind950f412021-10-20 18:49:34 +0800452 if (boost::ends_with(name, suffix))
453 {
454 boost::replace_all(name, suffix, "");
455 break;
456 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700457 }
Duke Du97014f52021-12-16 17:21:01 +0800458 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
459 {
460 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
461 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700462 }
JeffLind950f412021-10-20 18:49:34 +0800463 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700464 return name;
465}
466
Hao Jiangd48c9212021-02-03 15:45:06 -0800467bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
468 const std::string& path,
469 const ipmi::DbusInterfaceMap::mapped_type& object,
470 std::bitset<16>& assertions)
471{
472 auto profiles = sensor::getSupportedVrProfiles(object);
473 if (!profiles)
474 {
475 return false;
476 }
477 ipmi::Value modeVariant;
478
479 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
480 "Selected", modeVariant);
481 if (ec)
482 {
483 log<level::ERR>("Failed to get property",
484 entry("PROPERTY=%s", "Selected"),
485 entry("PATH=%s", path.c_str()),
486 entry("INTERFACE=%s", sensor::sensorInterface),
487 entry("WHAT=%s", ec.message().c_str()));
488 return false;
489 }
490
491 auto mode = std::get_if<std::string>(&modeVariant);
492 if (mode == nullptr)
493 {
494 log<level::ERR>("property is not a string",
495 entry("PROPERTY=%s", "Selected"),
496 entry("PATH=%s", path.c_str()),
497 entry("INTERFACE=%s", sensor::sensorInterface));
498 return false;
499 }
500
501 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
502 if (itr == profiles->end())
503 {
504 using namespace phosphor::logging;
505 log<level::ERR>("VR mode doesn't match any of its profiles",
506 entry("PATH=%s", path.c_str()));
507 return false;
508 }
509 std::size_t index =
510 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
511
512 // map index to reponse event assertion bit.
513 if (index < 8)
514 {
515 assertions.set(1u << index);
516 }
517 else if (index < 15)
518 {
519 assertions.set(1u << (index - 8));
520 }
521 else
522 {
523 log<level::ERR>("VR profile index reaches max assertion bit",
524 entry("PATH=%s", path.c_str()),
525 entry("INDEX=%uz", index));
526 return false;
527 }
528 if constexpr (debug)
529 {
530 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
531 << " mode is: [" << index << "] " << *mode << std::endl;
532 }
533 return true;
534}
Hao Jiangd2afd052020-12-10 15:09:32 -0800535} // namespace sensor
536
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000537ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
538 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800539{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000540 constexpr const uint8_t validEnvmRev = 0x04;
541 constexpr const uint8_t lastSensorType = 0x2C;
542 constexpr const uint8_t oemReserved = 0xC0;
543
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700544 uint8_t sysgeneratorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000545 uint8_t evmRev = 0;
546 uint8_t sensorType = 0;
547 uint8_t sensorNum = 0;
548 uint8_t eventType = 0;
549 uint8_t eventData1 = 0;
550 std::optional<uint8_t> eventData2 = 0;
551 std::optional<uint8_t> eventData3 = 0;
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700552 [[maybe_unused]] uint16_t generatorID = 0;
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000553 ipmi::ChannelInfo chInfo;
554
555 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
556 {
557 phosphor::logging::log<phosphor::logging::level::ERR>(
558 "Failed to get Channel Info",
559 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
560 return ipmi::responseUnspecifiedError();
561 }
562
563 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
564 ipmi::EChannelMediumType::systemInterface)
565 {
566
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700567 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000568 eventData1, eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700569 // Refer to IPMI Spec Table 32: SEL Event Records
570 generatorID = (ctx->channel << 12) // Channel
571 | (0x0 << 10) // Reserved
572 | (0x0 << 8) // 0x0 for sys-soft ID
573 | ((sysgeneratorID << 1) | 0x1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000574 }
575 else
576 {
577
578 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
579 eventData2, eventData3);
Sujoy Ray3ab2c2b2021-11-01 13:17:27 -0700580 // Refer to IPMI Spec Table 32: SEL Event Records
581 generatorID = (ctx->channel << 12) // Channel
582 | (0x0 << 10) // Reserved
583 | ((ctx->lun & 0x3) << 8) // Lun
584 | (ctx->rqSA << 1);
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000585 }
586
587 if (!p.fullyUnpacked())
588 {
589 return ipmi::responseReqDataLenInvalid();
590 }
591
592 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
593 if (evmRev != validEnvmRev)
594 {
595 return ipmi::responseInvalidFieldRequest();
596 }
597 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
598 {
599 return ipmi::responseInvalidFieldRequest();
600 }
601
Willy Tude54f482021-01-26 15:59:09 -0800602 return ipmi::responseSuccess();
603}
604
Willy Tudbafbce2021-03-29 00:37:05 -0700605ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
606 uint8_t sensorNumber, uint8_t operation,
607 uint8_t reading, uint15_t assertOffset,
608 bool resvd1, uint15_t deassertOffset,
609 bool resvd2, uint8_t eventData1,
610 uint8_t eventData2, uint8_t eventData3)
611{
612 std::string connection;
613 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700614 std::vector<std::string> interfaces;
615
616 ipmi::Cc status =
617 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700618 if (status)
619 {
620 return ipmi::response(status);
621 }
622
Hao Jiangd2afd052020-12-10 15:09:32 -0800623 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700624 if (std::find(interfaces.begin(), interfaces.end(),
625 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700626 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700627 DbusInterfaceMap sensorMap;
628 if (!getSensorMap(ctx, connection, path, sensorMap))
629 {
630 return ipmi::responseResponseError();
631 }
632 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800633 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700634 {
635 return ipmi::responseResponseError();
636 }
637
Jie Yangf0a89942021-07-29 15:30:25 -0700638 // Only allow external SetSensor if write permission granted
639 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
640 {
641 return ipmi::responseResponseError();
642 }
643
Hao Jiangd2afd052020-12-10 15:09:32 -0800644 auto value =
645 sensor::calculateValue(reading, sensorMap, sensorObject->second);
646 if (!value)
647 {
648 return ipmi::responseResponseError();
649 }
650
651 if constexpr (debug)
652 {
653 phosphor::logging::log<phosphor::logging::level::INFO>(
654 "IPMI SET_SENSOR",
655 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
656 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
657 phosphor::logging::entry("VALUE=%f", *value));
658 }
659
660 boost::system::error_code ec =
661 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
662 "Value", ipmi::Value(*value));
663
664 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500665 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800666 // callback functions for now (e.g. ipmiSetSensorReading).
667 if (ec)
668 {
669 using namespace phosphor::logging;
670 log<level::ERR>("Failed to set property",
671 entry("PROPERTY=%s", "Value"),
672 entry("PATH=%s", path.c_str()),
673 entry("INTERFACE=%s", sensor::sensorInterface),
674 entry("WHAT=%s", ec.message().c_str()));
675 return ipmi::responseResponseError();
676 }
677 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700678 }
679
Hao Jiange39d4d82021-04-16 17:02:40 -0700680 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
681 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700682 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700683 DbusInterfaceMap sensorMap;
684 if (!getSensorMap(ctx, connection, path, sensorMap))
685 {
686 return ipmi::responseResponseError();
687 }
688 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800689 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700690 {
691 return ipmi::responseResponseError();
692 }
693
Hao Jiangd2afd052020-12-10 15:09:32 -0800694 // VR sensors are treated as a special case and we will not check the
695 // write permission for VR sensors, since they always deemed writable
696 // and permission table are not applied to VR sensors.
697 auto vrMode =
698 sensor::calculateVRMode(assertOffset, sensorObject->second);
699 if (!vrMode)
700 {
701 return ipmi::responseResponseError();
702 }
703 boost::system::error_code ec = setDbusProperty(
704 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
705 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500706 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800707 // callback functions for now (e.g. ipmiSetSensorReading).
708 if (ec)
709 {
710 using namespace phosphor::logging;
711 log<level::ERR>("Failed to set property",
712 entry("PROPERTY=%s", "Selected"),
713 entry("PATH=%s", path.c_str()),
714 entry("INTERFACE=%s", sensor::sensorInterface),
715 entry("WHAT=%s", ec.message().c_str()));
716 return ipmi::responseResponseError();
717 }
718 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700719 }
720
Hao Jiangd2afd052020-12-10 15:09:32 -0800721 phosphor::logging::log<phosphor::logging::level::ERR>(
722 "unknown sensor type",
723 phosphor::logging::entry("PATH=%s", path.c_str()));
724 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700725}
726
Willy Tude54f482021-01-26 15:59:09 -0800727ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
728 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
729{
730 std::string connection;
731 std::string path;
732
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000733 if (sensnum == reservedSensorNumber)
734 {
735 return ipmi::responseInvalidFieldRequest();
736 }
737
Willy Tude54f482021-01-26 15:59:09 -0800738 auto status = getSensorConnection(ctx, sensnum, connection, path);
739 if (status)
740 {
741 return ipmi::response(status);
742 }
743
Scron Chang2703b022021-07-06 15:47:45 +0800744#ifdef FEATURE_HYBRID_SENSORS
745 if (auto sensor = findStaticSensor(path);
746 sensor != ipmi::sensor::sensors.end() &&
747 getSensorEventTypeFromPath(path) !=
748 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
749 {
750 if (ipmi::sensor::Mutability::Read !=
751 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
752 {
753 return ipmi::responseIllegalCommand();
754 }
755
756 uint8_t operation;
757 try
758 {
759 ipmi::sensor::GetSensorResponse getResponse =
760 sensor->second.getFunc(sensor->second);
761
762 if (getResponse.readingOrStateUnavailable)
763 {
764 operation |= static_cast<uint8_t>(
765 IPMISensorReadingByte2::readingStateUnavailable);
766 }
767 if (getResponse.scanningEnabled)
768 {
769 operation |= static_cast<uint8_t>(
770 IPMISensorReadingByte2::sensorScanningEnable);
771 }
772 if (getResponse.allEventMessagesEnabled)
773 {
774 operation |= static_cast<uint8_t>(
775 IPMISensorReadingByte2::eventMessagesEnable);
776 }
777 return ipmi::responseSuccess(
778 getResponse.reading, operation,
779 getResponse.thresholdLevelsStates,
780 getResponse.discreteReadingSensorStates);
781 }
782 catch (const std::exception& e)
783 {
784 operation |= static_cast<uint8_t>(
785 IPMISensorReadingByte2::readingStateUnavailable);
786 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
787 }
788 }
789#endif
790
Willy Tude54f482021-01-26 15:59:09 -0800791 DbusInterfaceMap sensorMap;
792 if (!getSensorMap(ctx, connection, path, sensorMap))
793 {
794 return ipmi::responseResponseError();
795 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800796 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800797
798 if (sensorObject == sensorMap.end() ||
799 sensorObject->second.find("Value") == sensorObject->second.end())
800 {
801 return ipmi::responseResponseError();
802 }
803 auto& valueVariant = sensorObject->second["Value"];
804 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
805
806 double max = 0;
807 double min = 0;
808 getSensorMaxMin(sensorMap, max, min);
809
810 int16_t mValue = 0;
811 int16_t bValue = 0;
812 int8_t rExp = 0;
813 int8_t bExp = 0;
814 bool bSigned = false;
815
816 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
817 {
818 return ipmi::responseResponseError();
819 }
820
821 uint8_t value =
822 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
823 uint8_t operation =
824 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
825 operation |=
826 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
827 bool notReading = std::isnan(reading);
828
829 if (!notReading)
830 {
831 auto availableObject =
832 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
833 if (availableObject != sensorMap.end())
834 {
835 auto findAvailable = availableObject->second.find("Available");
836 if (findAvailable != availableObject->second.end())
837 {
838 bool* available = std::get_if<bool>(&(findAvailable->second));
839 if (available && !(*available))
840 {
841 notReading = true;
842 }
843 }
844 }
845 }
846
847 if (notReading)
848 {
849 operation |= static_cast<uint8_t>(
850 IPMISensorReadingByte2::readingStateUnavailable);
851 }
852
Josh Lehana55c9532020-10-28 21:59:06 -0700853 if constexpr (details::enableInstrumentation)
854 {
855 int byteValue;
856 if (bSigned)
857 {
858 byteValue = static_cast<int>(static_cast<int8_t>(value));
859 }
860 else
861 {
862 byteValue = static_cast<int>(static_cast<uint8_t>(value));
863 }
864
865 // Keep stats on the reading just obtained, even if it is "NaN"
866 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
867 {
868 // This is the first reading, show the coefficients
869 double step = (max - min) / 255.0;
870 std::cerr << "IPMI sensor "
871 << details::sdrStatsTable.getName(sensnum)
872 << ": Range min=" << min << " max=" << max
873 << ", step=" << step
874 << ", Coefficients mValue=" << static_cast<int>(mValue)
875 << " rExp=" << static_cast<int>(rExp)
876 << " bValue=" << static_cast<int>(bValue)
877 << " bExp=" << static_cast<int>(bExp)
878 << " bSigned=" << static_cast<int>(bSigned) << "\n";
879 }
880 }
881
Willy Tude54f482021-01-26 15:59:09 -0800882 uint8_t thresholds = 0;
883
884 auto warningObject =
885 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
886 if (warningObject != sensorMap.end())
887 {
888 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
889 auto alarmLow = warningObject->second.find("WarningAlarmLow");
890 if (alarmHigh != warningObject->second.end())
891 {
892 if (std::get<bool>(alarmHigh->second))
893 {
894 thresholds |= static_cast<uint8_t>(
895 IPMISensorReadingByte3::upperNonCritical);
896 }
897 }
898 if (alarmLow != warningObject->second.end())
899 {
900 if (std::get<bool>(alarmLow->second))
901 {
902 thresholds |= static_cast<uint8_t>(
903 IPMISensorReadingByte3::lowerNonCritical);
904 }
905 }
906 }
907
908 auto criticalObject =
909 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
910 if (criticalObject != sensorMap.end())
911 {
912 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
913 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
914 if (alarmHigh != criticalObject->second.end())
915 {
916 if (std::get<bool>(alarmHigh->second))
917 {
918 thresholds |=
919 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
920 }
921 }
922 if (alarmLow != criticalObject->second.end())
923 {
924 if (std::get<bool>(alarmLow->second))
925 {
926 thresholds |=
927 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
928 }
929 }
930 }
931
932 // no discrete as of today so optional byte is never returned
933 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
934}
935
936/** @brief implements the Set Sensor threshold command
937 * @param sensorNumber - sensor number
938 * @param lowerNonCriticalThreshMask
939 * @param lowerCriticalThreshMask
940 * @param lowerNonRecovThreshMask
941 * @param upperNonCriticalThreshMask
942 * @param upperCriticalThreshMask
943 * @param upperNonRecovThreshMask
944 * @param reserved
945 * @param lowerNonCritical - lower non-critical threshold
946 * @param lowerCritical - Lower critical threshold
947 * @param lowerNonRecoverable - Lower non recovarable threshold
948 * @param upperNonCritical - Upper non-critical threshold
949 * @param upperCritical - Upper critical
950 * @param upperNonRecoverable - Upper Non-recoverable
951 *
952 * @returns IPMI completion code
953 */
954ipmi::RspType<> ipmiSenSetSensorThresholds(
955 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
956 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
957 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
958 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
959 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
960 uint8_t upperNonCritical, uint8_t upperCritical,
961 uint8_t upperNonRecoverable)
962{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000963 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800964 {
965 return ipmi::responseInvalidFieldRequest();
966 }
967
968 // lower nc and upper nc not suppported on any sensor
969 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
970 {
971 return ipmi::responseInvalidFieldRequest();
972 }
973
974 // if none of the threshold mask are set, nothing to do
975 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
976 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
977 upperCriticalThreshMask | upperNonRecovThreshMask))
978 {
979 return ipmi::responseSuccess();
980 }
981
982 std::string connection;
983 std::string path;
984
985 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
986 if (status)
987 {
988 return ipmi::response(status);
989 }
990 DbusInterfaceMap sensorMap;
991 if (!getSensorMap(ctx, connection, path, sensorMap))
992 {
993 return ipmi::responseResponseError();
994 }
995
996 double max = 0;
997 double min = 0;
998 getSensorMaxMin(sensorMap, max, min);
999
1000 int16_t mValue = 0;
1001 int16_t bValue = 0;
1002 int8_t rExp = 0;
1003 int8_t bExp = 0;
1004 bool bSigned = false;
1005
1006 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1007 {
1008 return ipmi::responseResponseError();
1009 }
1010
1011 // store a vector of property name, value to set, and interface
1012 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1013
1014 // define the indexes of the tuple
1015 constexpr uint8_t propertyName = 0;
1016 constexpr uint8_t thresholdValue = 1;
1017 constexpr uint8_t interface = 2;
1018 // verifiy all needed fields are present
1019 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1020 {
1021 auto findThreshold =
1022 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1023 if (findThreshold == sensorMap.end())
1024 {
1025 return ipmi::responseInvalidFieldRequest();
1026 }
1027 if (lowerCriticalThreshMask)
1028 {
1029 auto findLower = findThreshold->second.find("CriticalLow");
1030 if (findLower == findThreshold->second.end())
1031 {
1032 return ipmi::responseInvalidFieldRequest();
1033 }
1034 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1035 findThreshold->first);
1036 }
1037 if (upperCriticalThreshMask)
1038 {
1039 auto findUpper = findThreshold->second.find("CriticalHigh");
1040 if (findUpper == findThreshold->second.end())
1041 {
1042 return ipmi::responseInvalidFieldRequest();
1043 }
1044 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1045 findThreshold->first);
1046 }
1047 }
1048 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1049 {
1050 auto findThreshold =
1051 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1052 if (findThreshold == sensorMap.end())
1053 {
1054 return ipmi::responseInvalidFieldRequest();
1055 }
1056 if (lowerNonCriticalThreshMask)
1057 {
1058 auto findLower = findThreshold->second.find("WarningLow");
1059 if (findLower == findThreshold->second.end())
1060 {
1061 return ipmi::responseInvalidFieldRequest();
1062 }
1063 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1064 findThreshold->first);
1065 }
1066 if (upperNonCriticalThreshMask)
1067 {
1068 auto findUpper = findThreshold->second.find("WarningHigh");
1069 if (findUpper == findThreshold->second.end())
1070 {
1071 return ipmi::responseInvalidFieldRequest();
1072 }
1073 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1074 findThreshold->first);
1075 }
1076 }
1077 for (const auto& property : thresholdsToSet)
1078 {
1079 // from section 36.3 in the IPMI Spec, assume all linear
1080 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1081 (bValue * std::pow(10.0, bExp))) *
1082 std::pow(10.0, rExp);
1083 setDbusProperty(
1084 *getSdBus(), connection, path, std::get<interface>(property),
1085 std::get<propertyName>(property), ipmi::Value(valueToSet));
1086 }
1087 return ipmi::responseSuccess();
1088}
1089
1090IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1091{
1092 IPMIThresholds resp;
1093 auto warningInterface =
1094 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1095 auto criticalInterface =
1096 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1097
1098 if ((warningInterface != sensorMap.end()) ||
1099 (criticalInterface != sensorMap.end()))
1100 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001101 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001102
1103 if (sensorPair == sensorMap.end())
1104 {
1105 // should not have been able to find a sensor not implementing
1106 // the sensor object
1107 throw std::runtime_error("Invalid sensor map");
1108 }
1109
1110 double max = 0;
1111 double min = 0;
1112 getSensorMaxMin(sensorMap, max, min);
1113
1114 int16_t mValue = 0;
1115 int16_t bValue = 0;
1116 int8_t rExp = 0;
1117 int8_t bExp = 0;
1118 bool bSigned = false;
1119
1120 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1121 {
1122 throw std::runtime_error("Invalid sensor atrributes");
1123 }
1124 if (warningInterface != sensorMap.end())
1125 {
1126 auto& warningMap = warningInterface->second;
1127
1128 auto warningHigh = warningMap.find("WarningHigh");
1129 auto warningLow = warningMap.find("WarningLow");
1130
1131 if (warningHigh != warningMap.end())
1132 {
1133
1134 double value =
1135 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001136 if (std::isfinite(value))
1137 {
1138 resp.warningHigh = scaleIPMIValueFromDouble(
1139 value, mValue, rExp, bValue, bExp, bSigned);
1140 }
Willy Tude54f482021-01-26 15:59:09 -08001141 }
1142 if (warningLow != warningMap.end())
1143 {
1144 double value =
1145 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001146 if (std::isfinite(value))
1147 {
1148 resp.warningLow = scaleIPMIValueFromDouble(
1149 value, mValue, rExp, bValue, bExp, bSigned);
1150 }
Willy Tude54f482021-01-26 15:59:09 -08001151 }
1152 }
1153 if (criticalInterface != sensorMap.end())
1154 {
1155 auto& criticalMap = criticalInterface->second;
1156
1157 auto criticalHigh = criticalMap.find("CriticalHigh");
1158 auto criticalLow = criticalMap.find("CriticalLow");
1159
1160 if (criticalHigh != criticalMap.end())
1161 {
1162 double value =
1163 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001164 if (std::isfinite(value))
1165 {
1166 resp.criticalHigh = scaleIPMIValueFromDouble(
1167 value, mValue, rExp, bValue, bExp, bSigned);
1168 }
Willy Tude54f482021-01-26 15:59:09 -08001169 }
1170 if (criticalLow != criticalMap.end())
1171 {
1172 double value =
1173 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001174 if (std::isfinite(value))
1175 {
1176 resp.criticalLow = scaleIPMIValueFromDouble(
1177 value, mValue, rExp, bValue, bExp, bSigned);
1178 }
Willy Tude54f482021-01-26 15:59:09 -08001179 }
1180 }
1181 }
1182 return resp;
1183}
1184
1185ipmi::RspType<uint8_t, // readable
1186 uint8_t, // lowerNCrit
1187 uint8_t, // lowerCrit
1188 uint8_t, // lowerNrecoverable
1189 uint8_t, // upperNC
1190 uint8_t, // upperCrit
1191 uint8_t> // upperNRecoverable
1192 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1193{
1194 std::string connection;
1195 std::string path;
1196
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001197 if (sensorNumber == reservedSensorNumber)
1198 {
1199 return ipmi::responseInvalidFieldRequest();
1200 }
1201
Willy Tude54f482021-01-26 15:59:09 -08001202 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1203 if (status)
1204 {
1205 return ipmi::response(status);
1206 }
1207
1208 DbusInterfaceMap sensorMap;
1209 if (!getSensorMap(ctx, connection, path, sensorMap))
1210 {
1211 return ipmi::responseResponseError();
1212 }
1213
1214 IPMIThresholds thresholdData;
1215 try
1216 {
1217 thresholdData = getIPMIThresholds(sensorMap);
1218 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001219 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001220 {
1221 return ipmi::responseResponseError();
1222 }
1223
1224 uint8_t readable = 0;
1225 uint8_t lowerNC = 0;
1226 uint8_t lowerCritical = 0;
1227 uint8_t lowerNonRecoverable = 0;
1228 uint8_t upperNC = 0;
1229 uint8_t upperCritical = 0;
1230 uint8_t upperNonRecoverable = 0;
1231
1232 if (thresholdData.warningHigh)
1233 {
1234 readable |=
1235 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1236 upperNC = *thresholdData.warningHigh;
1237 }
1238 if (thresholdData.warningLow)
1239 {
1240 readable |=
1241 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1242 lowerNC = *thresholdData.warningLow;
1243 }
1244
1245 if (thresholdData.criticalHigh)
1246 {
1247 readable |=
1248 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1249 upperCritical = *thresholdData.criticalHigh;
1250 }
1251 if (thresholdData.criticalLow)
1252 {
1253 readable |=
1254 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1255 lowerCritical = *thresholdData.criticalLow;
1256 }
1257
1258 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1259 lowerNonRecoverable, upperNC, upperCritical,
1260 upperNonRecoverable);
1261}
1262
1263/** @brief implements the get Sensor event enable command
1264 * @param sensorNumber - sensor number
1265 *
1266 * @returns IPMI completion code plus response data
1267 * - enabled - Sensor Event messages
1268 * - assertionEnabledLsb - Assertion event messages
1269 * - assertionEnabledMsb - Assertion event messages
1270 * - deassertionEnabledLsb - Deassertion event messages
1271 * - deassertionEnabledMsb - Deassertion event messages
1272 */
1273
1274ipmi::RspType<uint8_t, // enabled
1275 uint8_t, // assertionEnabledLsb
1276 uint8_t, // assertionEnabledMsb
1277 uint8_t, // deassertionEnabledLsb
1278 uint8_t> // deassertionEnabledMsb
1279 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1280{
1281 std::string connection;
1282 std::string path;
1283
1284 uint8_t enabled = 0;
1285 uint8_t assertionEnabledLsb = 0;
1286 uint8_t assertionEnabledMsb = 0;
1287 uint8_t deassertionEnabledLsb = 0;
1288 uint8_t deassertionEnabledMsb = 0;
1289
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001290 if (sensorNum == reservedSensorNumber)
1291 {
1292 return ipmi::responseInvalidFieldRequest();
1293 }
1294
Willy Tude54f482021-01-26 15:59:09 -08001295 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1296 if (status)
1297 {
1298 return ipmi::response(status);
1299 }
1300
Scron Chang2703b022021-07-06 15:47:45 +08001301#ifdef FEATURE_HYBRID_SENSORS
1302 if (auto sensor = findStaticSensor(path);
1303 sensor != ipmi::sensor::sensors.end() &&
1304 getSensorEventTypeFromPath(path) !=
1305 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1306 {
1307 enabled = static_cast<uint8_t>(
1308 IPMISensorEventEnableByte2::sensorScanningEnable);
1309 uint16_t assertionEnabled = 0;
1310 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1311 ->second.begin()
1312 ->second.second)
1313 {
1314 assertionEnabled |= (1 << offsetValMap.first);
1315 }
1316 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1317 assertionEnabledMsb =
1318 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1319
1320 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1321 assertionEnabledMsb, deassertionEnabledLsb,
1322 deassertionEnabledMsb);
1323 }
1324#endif
1325
Willy Tude54f482021-01-26 15:59:09 -08001326 DbusInterfaceMap sensorMap;
1327 if (!getSensorMap(ctx, connection, path, sensorMap))
1328 {
1329 return ipmi::responseResponseError();
1330 }
1331
1332 auto warningInterface =
1333 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1334 auto criticalInterface =
1335 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1336 if ((warningInterface != sensorMap.end()) ||
1337 (criticalInterface != sensorMap.end()))
1338 {
1339 enabled = static_cast<uint8_t>(
1340 IPMISensorEventEnableByte2::sensorScanningEnable);
1341 if (warningInterface != sensorMap.end())
1342 {
1343 auto& warningMap = warningInterface->second;
1344
1345 auto warningHigh = warningMap.find("WarningHigh");
1346 auto warningLow = warningMap.find("WarningLow");
1347 if (warningHigh != warningMap.end())
1348 {
1349 assertionEnabledLsb |= static_cast<uint8_t>(
1350 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1351 deassertionEnabledLsb |= static_cast<uint8_t>(
1352 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1353 }
1354 if (warningLow != warningMap.end())
1355 {
1356 assertionEnabledLsb |= static_cast<uint8_t>(
1357 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1358 deassertionEnabledLsb |= static_cast<uint8_t>(
1359 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1360 }
1361 }
1362 if (criticalInterface != sensorMap.end())
1363 {
1364 auto& criticalMap = criticalInterface->second;
1365
1366 auto criticalHigh = criticalMap.find("CriticalHigh");
1367 auto criticalLow = criticalMap.find("CriticalLow");
1368
1369 if (criticalHigh != criticalMap.end())
1370 {
1371 assertionEnabledMsb |= static_cast<uint8_t>(
1372 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1373 deassertionEnabledMsb |= static_cast<uint8_t>(
1374 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1375 }
1376 if (criticalLow != criticalMap.end())
1377 {
1378 assertionEnabledLsb |= static_cast<uint8_t>(
1379 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1380 deassertionEnabledLsb |= static_cast<uint8_t>(
1381 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1382 }
1383 }
1384 }
1385
1386 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1387 assertionEnabledMsb, deassertionEnabledLsb,
1388 deassertionEnabledMsb);
1389}
1390
1391/** @brief implements the get Sensor event status command
1392 * @param sensorNumber - sensor number, FFh = reserved
1393 *
1394 * @returns IPMI completion code plus response data
1395 * - sensorEventStatus - Sensor Event messages state
1396 * - assertions - Assertion event messages
1397 * - deassertions - Deassertion event messages
1398 */
1399ipmi::RspType<uint8_t, // sensorEventStatus
1400 std::bitset<16>, // assertions
1401 std::bitset<16> // deassertion
1402 >
1403 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1404{
1405 if (sensorNum == reservedSensorNumber)
1406 {
1407 return ipmi::responseInvalidFieldRequest();
1408 }
1409
1410 std::string connection;
1411 std::string path;
1412 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1413 if (status)
1414 {
1415 phosphor::logging::log<phosphor::logging::level::ERR>(
1416 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1417 phosphor::logging::entry("SENSOR=%d", sensorNum));
1418 return ipmi::response(status);
1419 }
1420
Scron Chang2703b022021-07-06 15:47:45 +08001421#ifdef FEATURE_HYBRID_SENSORS
1422 if (auto sensor = findStaticSensor(path);
1423 sensor != ipmi::sensor::sensors.end() &&
1424 getSensorEventTypeFromPath(path) !=
1425 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1426 {
1427 auto response = ipmi::sensor::get::mapDbusToAssertion(
1428 sensor->second, path, sensor->second.sensorInterface);
1429 std::bitset<16> assertions;
1430 // deassertions are not used.
1431 std::bitset<16> deassertions = 0;
1432 uint8_t sensorEventStatus;
1433 if (response.readingOrStateUnavailable)
1434 {
1435 sensorEventStatus |= static_cast<uint8_t>(
1436 IPMISensorReadingByte2::readingStateUnavailable);
1437 }
1438 if (response.scanningEnabled)
1439 {
1440 sensorEventStatus |= static_cast<uint8_t>(
1441 IPMISensorReadingByte2::sensorScanningEnable);
1442 }
1443 if (response.allEventMessagesEnabled)
1444 {
1445 sensorEventStatus |= static_cast<uint8_t>(
1446 IPMISensorReadingByte2::eventMessagesEnable);
1447 }
1448 assertions |= response.discreteReadingSensorStates << 8;
1449 assertions |= response.thresholdLevelsStates;
1450 return ipmi::responseSuccess(sensorEventStatus, assertions,
1451 deassertions);
1452 }
1453#endif
1454
Willy Tude54f482021-01-26 15:59:09 -08001455 DbusInterfaceMap sensorMap;
1456 if (!getSensorMap(ctx, connection, path, sensorMap))
1457 {
1458 phosphor::logging::log<phosphor::logging::level::ERR>(
1459 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1460 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1461 return ipmi::responseResponseError();
1462 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001463
1464 uint8_t sensorEventStatus =
1465 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1466 std::bitset<16> assertions = 0;
1467 std::bitset<16> deassertions = 0;
1468
1469 // handle VR typed sensor
1470 auto vrInterface = sensorMap.find(sensor::vrInterface);
1471 if (vrInterface != sensorMap.end())
1472 {
1473 if (!sensor::getVrEventStatus(ctx, connection, path,
1474 vrInterface->second, assertions))
1475 {
1476 return ipmi::responseResponseError();
1477 }
1478
1479 // both Event Message and Sensor Scanning are disable for VR.
1480 sensorEventStatus = 0;
1481 return ipmi::responseSuccess(sensorEventStatus, assertions,
1482 deassertions);
1483 }
1484
Willy Tude54f482021-01-26 15:59:09 -08001485 auto warningInterface =
1486 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1487 auto criticalInterface =
1488 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1489
Willy Tude54f482021-01-26 15:59:09 -08001490 std::optional<bool> criticalDeassertHigh =
1491 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1492 std::optional<bool> criticalDeassertLow =
1493 thresholdDeassertMap[path]["CriticalAlarmLow"];
1494 std::optional<bool> warningDeassertHigh =
1495 thresholdDeassertMap[path]["WarningAlarmHigh"];
1496 std::optional<bool> warningDeassertLow =
1497 thresholdDeassertMap[path]["WarningAlarmLow"];
1498
Willy Tude54f482021-01-26 15:59:09 -08001499 if (criticalDeassertHigh && !*criticalDeassertHigh)
1500 {
1501 deassertions.set(static_cast<size_t>(
1502 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1503 }
1504 if (criticalDeassertLow && !*criticalDeassertLow)
1505 {
1506 deassertions.set(static_cast<size_t>(
1507 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1508 }
1509 if (warningDeassertHigh && !*warningDeassertHigh)
1510 {
1511 deassertions.set(static_cast<size_t>(
1512 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1513 }
1514 if (warningDeassertLow && !*warningDeassertLow)
1515 {
1516 deassertions.set(static_cast<size_t>(
1517 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1518 }
1519 if ((warningInterface != sensorMap.end()) ||
1520 (criticalInterface != sensorMap.end()))
1521 {
1522 sensorEventStatus = static_cast<size_t>(
1523 IPMISensorEventEnableByte2::eventMessagesEnable);
1524 if (warningInterface != sensorMap.end())
1525 {
1526 auto& warningMap = warningInterface->second;
1527
1528 auto warningHigh = warningMap.find("WarningAlarmHigh");
1529 auto warningLow = warningMap.find("WarningAlarmLow");
1530 auto warningHighAlarm = false;
1531 auto warningLowAlarm = false;
1532
1533 if (warningHigh != warningMap.end())
1534 {
1535 warningHighAlarm = std::get<bool>(warningHigh->second);
1536 }
1537 if (warningLow != warningMap.end())
1538 {
1539 warningLowAlarm = std::get<bool>(warningLow->second);
1540 }
1541 if (warningHighAlarm)
1542 {
1543 assertions.set(
1544 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1545 upperNonCriticalGoingHigh));
1546 }
1547 if (warningLowAlarm)
1548 {
1549 assertions.set(
1550 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1551 lowerNonCriticalGoingLow));
1552 }
1553 }
1554 if (criticalInterface != sensorMap.end())
1555 {
1556 auto& criticalMap = criticalInterface->second;
1557
1558 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1559 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1560 auto criticalHighAlarm = false;
1561 auto criticalLowAlarm = false;
1562
1563 if (criticalHigh != criticalMap.end())
1564 {
1565 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1566 }
1567 if (criticalLow != criticalMap.end())
1568 {
1569 criticalLowAlarm = std::get<bool>(criticalLow->second);
1570 }
1571 if (criticalHighAlarm)
1572 {
1573 assertions.set(
1574 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1575 upperCriticalGoingHigh));
1576 }
1577 if (criticalLowAlarm)
1578 {
1579 assertions.set(static_cast<size_t>(
1580 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1581 }
1582 }
1583 }
1584
1585 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1586}
1587
Willy Tu38e7a2b2021-03-29 15:09:56 -07001588// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001589void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1590 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001591{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001592 get_sdr::header::set_record_id(
1593 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1594
Willy Tu38e7a2b2021-03-29 15:09:56 -07001595 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1596 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1597
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001598 record.header.sdr_version = ipmiSdrVersion;
1599 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1600 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1601 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001602 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001603 record.key.owner_lun = lun;
1604 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001605}
1606bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1607 uint16_t recordID, const std::string& service,
1608 const std::string& path,
1609 get_sdr::SensorDataFullRecord& record)
1610{
1611 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1612 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1613
1614 DbusInterfaceMap sensorMap;
1615 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1616 {
1617 phosphor::logging::log<phosphor::logging::level::ERR>(
1618 "Failed to update sensor map for threshold sensor",
1619 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1620 phosphor::logging::entry("PATH=%s", path.c_str()));
1621 return false;
1622 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001623
1624 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1625 record.body.sensor_type = getSensorTypeFromPath(path);
1626 std::string type = getSensorTypeStringFromPath(path);
1627 auto typeCstr = type.c_str();
1628 auto findUnits = sensorUnits.find(typeCstr);
1629 if (findUnits != sensorUnits.end())
1630 {
1631 record.body.sensor_units_2_base =
1632 static_cast<uint8_t>(findUnits->second);
1633 } // else default 0x0 unspecified
1634
1635 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1636
Hao Jiangd2afd052020-12-10 15:09:32 -08001637 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001638 if (sensorObject == sensorMap.end())
1639 {
1640 phosphor::logging::log<phosphor::logging::level::ERR>(
1641 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001642 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001643 }
1644
1645 uint8_t entityId = 0;
1646 uint8_t entityInstance = 0x01;
1647
1648 // follow the association chain to get the parent board's entityid and
1649 // entityInstance
1650 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1651
1652 record.body.entity_id = entityId;
1653 record.body.entity_instance = entityInstance;
1654
Shakeeb Pasha93889722021-10-14 10:20:13 +05301655 double max = 0;
1656 double min = 0;
1657 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001658
1659 int16_t mValue = 0;
1660 int8_t rExp = 0;
1661 int16_t bValue = 0;
1662 int8_t bExp = 0;
1663 bool bSigned = false;
1664
1665 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1666 {
1667 phosphor::logging::log<phosphor::logging::level::ERR>(
1668 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001669 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001670 }
1671
1672 // The record.body is a struct SensorDataFullRecordBody
1673 // from sensorhandler.hpp in phosphor-ipmi-host.
1674 // The meaning of these bits appears to come from
1675 // table 43.1 of the IPMI spec.
1676 // The above 5 sensor attributes are stuffed in as follows:
1677 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1678 // Byte 22-24 are for other purposes
1679 // Byte 25 = MMMMMMMM = LSB of M
1680 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1681 // Byte 27 = BBBBBBBB = LSB of B
1682 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1683 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1684 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1685
1686 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1687 record.body.m_lsb = mValue & 0xFF;
1688
1689 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1690 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1691
1692 // move the smallest bit of the MSB into place (bit 9)
1693 // the MSbs are bits 7:8 in m_msb_and_tolerance
1694 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1695
1696 record.body.b_lsb = bValue & 0xFF;
1697
1698 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1699 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1700
1701 // move the smallest bit of the MSB into place (bit 9)
1702 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1703 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1704
1705 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1706 uint8_t rExpBits = rExp & 0x07;
1707
1708 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1709 uint8_t bExpBits = bExp & 0x07;
1710
1711 // move rExp and bExp into place
1712 record.body.r_b_exponents =
1713 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1714
1715 // Set the analog reading byte interpretation accordingly
1716 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1717
1718 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1719 // These seem redundant, but derivable from the above 5 attributes
1720 // Original comment said "todo fill out rest of units"
1721
1722 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001723 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001724 record.body.id_string_info = name.size();
1725 std::strncpy(record.body.id_string, name.c_str(),
1726 sizeof(record.body.id_string));
1727
Josh Lehana55c9532020-10-28 21:59:06 -07001728 // Remember the sensor name, as determined for this sensor number
1729 details::sdrStatsTable.updateName(sensornumber, name);
1730
Jie Yangf0a89942021-07-29 15:30:25 -07001731 bool sensorSettable = false;
1732 auto mutability =
1733 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1734 if (mutability != sensorMap.end())
1735 {
1736 sensorSettable =
1737 mappedVariant<bool>(mutability->second, "Mutable", false);
1738 }
1739 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1740
1741 // Grant write permission to sensors deemed externally settable
1742 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001743
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001744 IPMIThresholds thresholdData;
1745 try
1746 {
1747 thresholdData = getIPMIThresholds(sensorMap);
1748 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001749 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001750 {
1751 phosphor::logging::log<phosphor::logging::level::ERR>(
1752 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001753 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001754 }
1755
1756 if (thresholdData.criticalHigh)
1757 {
1758 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1759 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1760 IPMISensorEventEnableThresholds::criticalThreshold);
1761 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1762 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1763 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1764 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1765 record.body.discrete_reading_setting_mask[0] |=
1766 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1767 }
1768 if (thresholdData.warningHigh)
1769 {
1770 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1771 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1772 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1773 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1774 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1775 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1776 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1777 record.body.discrete_reading_setting_mask[0] |=
1778 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1779 }
1780 if (thresholdData.criticalLow)
1781 {
1782 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1783 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1784 IPMISensorEventEnableThresholds::criticalThreshold);
1785 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1786 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1787 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1788 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1789 record.body.discrete_reading_setting_mask[0] |=
1790 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1791 }
1792 if (thresholdData.warningLow)
1793 {
1794 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1795 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1796 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1797 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1798 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1799 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1800 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1801 record.body.discrete_reading_setting_mask[0] |=
1802 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1803 }
1804
1805 // everything that is readable is setable
1806 record.body.discrete_reading_setting_mask[1] =
1807 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001808 return true;
1809}
1810
Scron Chang2703b022021-07-06 15:47:45 +08001811#ifdef FEATURE_HYBRID_SENSORS
1812// Construct a type 1 SDR for discrete Sensor typed sensor.
1813void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1814 uint16_t recordID,
1815 ipmi::sensor::IdInfoMap::const_iterator sensor,
1816 get_sdr::SensorDataFullRecord& record)
1817{
1818 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1819
1820 record.body.entity_id = sensor->second.entityType;
1821 record.body.sensor_type = sensor->second.sensorType;
1822 record.body.event_reading_type = sensor->second.sensorReadingType;
1823 record.body.entity_instance = sensor->second.instance;
1824 if (ipmi::sensor::Mutability::Write ==
1825 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1826 {
1827 get_sdr::body::init_settable_state(true, &(record.body));
1828 }
1829
1830 auto id_string = sensor->second.sensorName;
1831
1832 if (id_string.empty())
1833 {
1834 id_string = sensor->second.sensorNameFunc(sensor->second);
1835 }
1836
1837 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1838 {
1839 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1840 &(record.body));
1841 }
1842 else
1843 {
1844 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1845 }
1846 std::strncpy(record.body.id_string, id_string.c_str(),
1847 get_sdr::body::get_id_strlen(&(record.body)));
1848}
1849#endif
1850
Hao Jiange39d4d82021-04-16 17:02:40 -07001851// Construct type 3 SDR header and key (for VR and other discrete sensors)
1852void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1853 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001854{
1855 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1856 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1857
1858 get_sdr::header::set_record_id(
1859 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1860
1861 record.header.sdr_version = ipmiSdrVersion;
1862 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1863 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1864 sizeof(get_sdr::SensorDataRecordHeader);
1865 record.key.owner_id = bmcI2CAddr;
1866 record.key.owner_lun = lun;
1867 record.key.sensor_number = sensornumber;
1868
1869 record.body.entity_id = 0x00;
1870 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001871}
Willy Tu61992ad2021-03-29 15:33:20 -07001872
Hao Jiange39d4d82021-04-16 17:02:40 -07001873// Construct a type 3 SDR for VR typed sensor(daemon).
1874bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1875 uint16_t recordID, const std::string& service,
1876 const std::string& path,
1877 get_sdr::SensorDataEventRecord& record)
1878{
1879 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1880 constructEventSdrHeaderKey(sensorNum, recordID, record);
1881
1882 DbusInterfaceMap sensorMap;
1883 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1884 {
1885 phosphor::logging::log<phosphor::logging::level::ERR>(
1886 "Failed to update sensor map for VR sensor",
1887 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1888 phosphor::logging::entry("PATH=%s", path.c_str()));
1889 return false;
1890 }
Willy Tu61992ad2021-03-29 15:33:20 -07001891 // follow the association chain to get the parent board's entityid and
1892 // entityInstance
1893 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1894 record.body.entity_instance);
1895
1896 // Sensor type is hardcoded as a module/board type instead of parsing from
1897 // sensor path. This is because VR control is allocated in an independent
1898 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1899 // types.
1900 static constexpr const uint8_t module_board_type = 0x15;
1901 record.body.sensor_type = module_board_type;
1902 record.body.event_reading_type = 0x00;
1903
1904 record.body.sensor_record_sharing_1 = 0x00;
1905 record.body.sensor_record_sharing_2 = 0x00;
1906
1907 // populate sensor name from path
1908 auto name = sensor::parseSdrIdFromPath(path);
1909 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1910 record.body.id_string_info = nameSize;
1911 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1912 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1913
1914 // Remember the sensor name, as determined for this sensor number
1915 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001916
1917 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001918}
1919
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001920static inline uint16_t getNumberOfSensors()
1921{
1922 return std::min(getSensorTree().size(), maxIPMISensors);
1923}
1924
Hao Jiange39d4d82021-04-16 17:02:40 -07001925static int
1926 getSensorDataRecord(ipmi::Context::ptr ctx,
1927 std::vector<uint8_t>& recordData, uint16_t recordID,
1928 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001929{
1930 size_t fruCount = 0;
1931 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1932 if (ret != ipmi::ccSuccess)
1933 {
1934 phosphor::logging::log<phosphor::logging::level::ERR>(
1935 "getSensorDataRecord: getFruSdrCount error");
1936 return GENERAL_ERROR;
1937 }
1938
Harvey Wu05d17c02021-09-15 08:46:59 +08001939 const auto& entityRecords =
1940 ipmi::sensor::EntityInfoMapContainer::getContainer()
1941 ->getIpmiEntityRecords();
1942 size_t entityCount = entityRecords.size();
1943
1944 size_t lastRecord = getNumberOfSensors() + fruCount +
1945 ipmi::storage::type12Count + entityCount - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001946 if (recordID == lastRecordIndex)
1947 {
1948 recordID = lastRecord;
1949 }
1950 if (recordID > lastRecord)
1951 {
1952 phosphor::logging::log<phosphor::logging::level::ERR>(
1953 "getSensorDataRecord: recordID > lastRecord error");
1954 return GENERAL_ERROR;
1955 }
1956
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001957 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001958 {
Harvey Wu05d17c02021-09-15 08:46:59 +08001959 size_t sdrIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001960
Harvey Wu05d17c02021-09-15 08:46:59 +08001961 if (sdrIndex >= fruCount + ipmi::storage::type12Count)
1962 {
1963 // handle type 8 entity map records
1964 ipmi::sensor::EntityInfoMap::const_iterator entity =
1965 entityRecords.find(static_cast<uint8_t>(
1966 sdrIndex - fruCount - ipmi::storage::type12Count));
1967 if (entity == entityRecords.end())
1968 {
1969 return IPMI_CC_SENSOR_INVALID;
1970 }
1971 recordData = ipmi::storage::getType8SDRs(entity, recordID);
1972 }
1973 else if (sdrIndex >= fruCount)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001974 {
1975 // handle type 12 hardcoded records
Harvey Wu05d17c02021-09-15 08:46:59 +08001976 size_t type12Index = sdrIndex - fruCount;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001977 if (type12Index >= ipmi::storage::type12Count)
1978 {
1979 phosphor::logging::log<phosphor::logging::level::ERR>(
1980 "getSensorDataRecord: type12Index error");
1981 return GENERAL_ERROR;
1982 }
1983 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1984 }
1985 else
1986 {
1987 // handle fru records
1988 get_sdr::SensorDataFruRecord data;
Harvey Wu05d17c02021-09-15 08:46:59 +08001989 ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001990 if (ret != IPMI_CC_OK)
1991 {
1992 return GENERAL_ERROR;
1993 }
1994 data.header.record_id_msb = recordID >> 8;
1995 data.header.record_id_lsb = recordID & 0xFF;
1996 recordData.insert(recordData.end(), (uint8_t*)&data,
1997 ((uint8_t*)&data) + sizeof(data));
1998 }
1999
2000 return 0;
2001 }
2002
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002003 // Perform a incremental scan of the SDR Record ID's and translate the
2004 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2005 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2006 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2007 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07002008 std::string connection;
2009 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07002010 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07002011 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002012 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07002013 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002014 // LUN 0 has one reserved sensor number. Compensate here by adding one
2015 // to the record ID
2016 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07002017 ctx->lun = 1;
2018 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002019 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07002020 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002021 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2022 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2023 // rules governing its use.
2024 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07002025 ctx->lun = 3;
2026 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002027
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002028 auto status =
2029 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2030 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002031 if (status)
2032 {
2033 phosphor::logging::log<phosphor::logging::level::ERR>(
2034 "getSensorDataRecord: getSensorConnection error");
2035 return GENERAL_ERROR;
2036 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002037 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002038 // Return an error on LUN 2 assingments, and any sensor number beyond the
2039 // range of LUN 3
2040 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2041 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002042 {
2043 phosphor::logging::log<phosphor::logging::level::ERR>(
2044 "getSensorDataRecord: invalidSensorNumber");
2045 return GENERAL_ERROR;
2046 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002047 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2048 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2049
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002050 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2051 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002052 {
2053 phosphor::logging::log<phosphor::logging::level::ERR>(
2054 "getSensorDataRecord: sensor record mismatch");
2055 return GENERAL_ERROR;
2056 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002057
Willy Tu38e7a2b2021-03-29 15:09:56 -07002058 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002059 if (std::find(interfaces.begin(), interfaces.end(),
2060 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002061 {
2062 get_sdr::SensorDataFullRecord record = {0};
2063
Hao Jiange39d4d82021-04-16 17:02:40 -07002064 // If the request doesn't read SDR body, construct only header and key
2065 // part to avoid additional DBus transaction.
2066 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2067 {
2068 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2069 }
2070 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2071 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002072 {
2073 return GENERAL_ERROR;
2074 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002075
Willy Tu38e7a2b2021-03-29 15:09:56 -07002076 recordData.insert(recordData.end(), (uint8_t*)&record,
2077 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002078
2079 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002080 }
Willy Tu61992ad2021-03-29 15:33:20 -07002081
Scron Chang2703b022021-07-06 15:47:45 +08002082#ifdef FEATURE_HYBRID_SENSORS
2083 if (auto sensor = findStaticSensor(path);
2084 sensor != ipmi::sensor::sensors.end() &&
2085 getSensorEventTypeFromPath(path) !=
2086 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2087 {
2088 get_sdr::SensorDataFullRecord record = {0};
2089
2090 // If the request doesn't read SDR body, construct only header and key
2091 // part to avoid additional DBus transaction.
2092 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2093 {
2094 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2095 }
2096 else
2097 {
2098 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2099 }
2100
2101 recordData.insert(recordData.end(), (uint8_t*)&record,
2102 ((uint8_t*)&record) + sizeof(record));
2103
2104 return 0;
2105 }
2106#endif
2107
Willy Tu61992ad2021-03-29 15:33:20 -07002108 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002109 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2110 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002111 {
2112 get_sdr::SensorDataEventRecord record = {0};
2113
Hao Jiange39d4d82021-04-16 17:02:40 -07002114 // If the request doesn't read SDR body, construct only header and key
2115 // part to avoid additional DBus transaction.
2116 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2117 {
2118 constructEventSdrHeaderKey(sensorNum, recordID, record);
2119 }
2120 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2121 record))
2122 {
2123 return GENERAL_ERROR;
2124 }
Willy Tu61992ad2021-03-29 15:33:20 -07002125 recordData.insert(recordData.end(), (uint8_t*)&record,
2126 ((uint8_t*)&record) + sizeof(record));
2127 }
2128
Willy Tude54f482021-01-26 15:59:09 -08002129 return 0;
2130}
2131
2132/** @brief implements the get SDR Info command
2133 * @param count - Operation
2134 *
2135 * @returns IPMI completion code plus response data
2136 * - sdrCount - sensor/SDR count
2137 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2138 */
2139static ipmi::RspType<uint8_t, // respcount
2140 uint8_t, // dynamic population flags
2141 uint32_t // last time a sensor was added
2142 >
2143 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2144 std::optional<uint8_t> count)
2145{
2146 auto& sensorTree = getSensorTree();
2147 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002148 uint16_t recordID = 0;
2149 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002150 // Sensors are dynamically allocated, and there is at least one LUN
2151 uint8_t lunsAndDynamicPopulation = 0x80;
2152 constexpr uint8_t getSdrCount = 0x01;
2153 constexpr uint8_t getSensorCount = 0x00;
2154
2155 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2156 {
2157 return ipmi::responseResponseError();
2158 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002159 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002160 if (count.value_or(0) == getSdrCount)
2161 {
2162 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002163 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002164 {
2165 get_sdr::SensorDataRecordHeader* hdr =
2166 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002167 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002168 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2169 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002170 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002171 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002172 record.data());
2173 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002174 {
2175 sdrCount++;
2176 }
2177 }
2178 }
2179 }
2180 else if (count.value_or(0) == getSensorCount)
2181 {
2182 // Return the number of sensors attached to the LUN
2183 if ((ctx->lun == 0) && (numSensors > 0))
2184 {
2185 sdrCount =
2186 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2187 }
2188 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2189 {
2190 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2191 ? maxSensorsPerLUN
2192 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2193 }
2194 else if (ctx->lun == 3)
2195 {
2196 if (numSensors <= maxIPMISensors)
2197 {
2198 sdrCount =
2199 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2200 }
2201 else
2202 {
2203 // error
2204 throw std::out_of_range(
2205 "Maximum number of IPMI sensors exceeded.");
2206 }
2207 }
2208 }
2209 else
2210 {
2211 return ipmi::responseInvalidFieldRequest();
2212 }
2213
2214 // Get Sensor count. This returns the number of sensors
2215 if (numSensors > 0)
2216 {
2217 lunsAndDynamicPopulation |= 1;
2218 }
2219 if (numSensors > maxSensorsPerLUN)
2220 {
2221 lunsAndDynamicPopulation |= 2;
2222 }
2223 if (numSensors >= (maxSensorsPerLUN * 2))
2224 {
2225 lunsAndDynamicPopulation |= 8;
2226 }
2227 if (numSensors > maxIPMISensors)
2228 {
2229 // error
2230 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2231 }
2232
2233 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2234 sdrLastAdd);
2235}
2236
2237/* end sensor commands */
2238
2239/* storage commands */
2240
2241ipmi::RspType<uint8_t, // sdr version
2242 uint16_t, // record count
2243 uint16_t, // free space
2244 uint32_t, // most recent addition
2245 uint32_t, // most recent erase
2246 uint8_t // operationSupport
2247 >
2248 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2249{
2250 auto& sensorTree = getSensorTree();
2251 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002252 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002253 {
2254 return ipmi::responseResponseError();
2255 }
2256
2257 size_t fruCount = 0;
2258 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2259 if (ret != ipmi::ccSuccess)
2260 {
2261 return ipmi::response(ret);
2262 }
2263
2264 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002265 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002266
2267 uint8_t operationSupport = static_cast<uint8_t>(
2268 SdrRepositoryInfoOps::overflow); // write not supported
2269
2270 operationSupport |=
2271 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2272 operationSupport |= static_cast<uint8_t>(
2273 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2274 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2275 unspecifiedFreeSpace, sdrLastAdd,
2276 sdrLastRemove, operationSupport);
2277}
2278
2279/** @brief implements the get SDR allocation info command
2280 *
2281 * @returns IPMI completion code plus response data
2282 * - allocUnits - Number of possible allocation units
2283 * - allocUnitSize - Allocation unit size in bytes.
2284 * - allocUnitFree - Number of free allocation units
2285 * - allocUnitLargestFree - Largest free block in allocation units
2286 * - maxRecordSize - Maximum record size in allocation units.
2287 */
2288ipmi::RspType<uint16_t, // allocUnits
2289 uint16_t, // allocUnitSize
2290 uint16_t, // allocUnitFree
2291 uint16_t, // allocUnitLargestFree
2292 uint8_t // maxRecordSize
2293 >
2294 ipmiStorageGetSDRAllocationInfo()
2295{
2296 // 0000h unspecified number of alloc units
2297 constexpr uint16_t allocUnits = 0;
2298
2299 constexpr uint16_t allocUnitFree = 0;
2300 constexpr uint16_t allocUnitLargestFree = 0;
2301 // only allow one block at a time
2302 constexpr uint8_t maxRecordSize = 1;
2303
2304 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2305 allocUnitLargestFree, maxRecordSize);
2306}
2307
2308/** @brief implements the reserve SDR command
2309 * @returns IPMI completion code plus response data
2310 * - sdrReservationID
2311 */
2312ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2313{
2314 sdrReservationID++;
2315 if (sdrReservationID == 0)
2316 {
2317 sdrReservationID++;
2318 }
2319
2320 return ipmi::responseSuccess(sdrReservationID);
2321}
2322
2323ipmi::RspType<uint16_t, // next record ID
2324 std::vector<uint8_t> // payload
2325 >
2326 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2327 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2328{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002329 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002330 // reservation required for partial reads with non zero offset into
2331 // record
2332 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2333 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002334 phosphor::logging::log<phosphor::logging::level::ERR>(
2335 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002336 return ipmi::responseInvalidReservationId();
2337 }
Willy Tude54f482021-01-26 15:59:09 -08002338 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2339 if (ret != ipmi::ccSuccess)
2340 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002341 phosphor::logging::log<phosphor::logging::level::ERR>(
2342 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002343 return ipmi::response(ret);
2344 }
2345
Harvey Wu05d17c02021-09-15 08:46:59 +08002346 const auto& entityRecords =
2347 ipmi::sensor::EntityInfoMapContainer::getContainer()
2348 ->getIpmiEntityRecords();
2349 int entityCount = entityRecords.size();
2350
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002351 auto& sensorTree = getSensorTree();
Harvey Wu05d17c02021-09-15 08:46:59 +08002352 size_t lastRecord = getNumberOfSensors() + fruCount +
2353 ipmi::storage::type12Count + entityCount - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002354 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2355
2356 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002357 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002358 phosphor::logging::log<phosphor::logging::level::ERR>(
2359 "ipmiStorageGetSDR: getSensorSubtree error");
2360 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002361 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002362
2363 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002364 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002365 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002366 phosphor::logging::log<phosphor::logging::level::ERR>(
2367 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002368 return ipmi::responseInvalidFieldRequest();
2369 }
Willy Tude54f482021-01-26 15:59:09 -08002370 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002371 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002372 if (!hdr)
2373 {
2374 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002375 "ipmiStorageGetSDR: record header is null");
2376 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002377 }
2378
2379 size_t sdrLength =
2380 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2381 if (sdrLength < (offset + bytesToRead))
2382 {
2383 bytesToRead = sdrLength - offset;
2384 }
2385
2386 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2387 if (!respStart)
2388 {
2389 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002390 "ipmiStorageGetSDR: record is null");
2391 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002392 }
2393
2394 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002395
Willy Tude54f482021-01-26 15:59:09 -08002396 return ipmi::responseSuccess(nextRecordId, recordData);
2397}
2398/* end storage commands */
2399
2400void registerSensorFunctions()
2401{
2402 // <Platform Event>
2403 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2404 ipmi::sensor_event::cmdPlatformEvent,
2405 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2406
Willy Tudbafbce2021-03-29 00:37:05 -07002407 // <Set Sensor Reading and Event Status>
2408 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2409 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2410 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002411
Willy Tude54f482021-01-26 15:59:09 -08002412 // <Get Sensor Reading>
2413 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2414 ipmi::sensor_event::cmdGetSensorReading,
2415 ipmi::Privilege::User, ipmiSenGetSensorReading);
2416
2417 // <Get Sensor Threshold>
2418 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2419 ipmi::sensor_event::cmdGetSensorThreshold,
2420 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2421
2422 // <Set Sensor Threshold>
2423 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2424 ipmi::sensor_event::cmdSetSensorThreshold,
2425 ipmi::Privilege::Operator,
2426 ipmiSenSetSensorThresholds);
2427
2428 // <Get Sensor Event Enable>
2429 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2430 ipmi::sensor_event::cmdGetSensorEventEnable,
2431 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2432
2433 // <Get Sensor Event Status>
2434 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2435 ipmi::sensor_event::cmdGetSensorEventStatus,
2436 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2437
2438 // register all storage commands for both Sensor and Storage command
2439 // versions
2440
2441 // <Get SDR Repository Info>
2442 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2443 ipmi::storage::cmdGetSdrRepositoryInfo,
2444 ipmi::Privilege::User,
2445 ipmiStorageGetSDRRepositoryInfo);
2446
2447 // <Get Device SDR Info>
2448 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2449 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2450 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2451
2452 // <Get SDR Allocation Info>
2453 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2454 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2455 ipmi::Privilege::User,
2456 ipmiStorageGetSDRAllocationInfo);
2457
2458 // <Reserve SDR Repo>
2459 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2460 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2461 ipmi::Privilege::User, ipmiStorageReserveSDR);
2462
2463 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2464 ipmi::storage::cmdReserveSdrRepository,
2465 ipmi::Privilege::User, ipmiStorageReserveSDR);
2466
2467 // <Get Sdr>
2468 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2469 ipmi::sensor_event::cmdGetDeviceSdr,
2470 ipmi::Privilege::User, ipmiStorageGetSDR);
2471
2472 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2473 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2474 ipmiStorageGetSDR);
2475}
2476} // namespace ipmi