blob: 4e971b3531542dd397f8daa8b169c8c4e5048dda [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "dbus-sdr/sensorcommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "dbus-sdr/sensorutils.hpp"
21#include "dbus-sdr/storagecommands.hpp"
22
23#include <algorithm>
24#include <array>
25#include <boost/algorithm/string.hpp>
26#include <boost/container/flat_map.hpp>
27#include <chrono>
28#include <cmath>
29#include <cstring>
30#include <iostream>
31#include <ipmid/api.hpp>
32#include <ipmid/types.hpp>
33#include <ipmid/utils.hpp>
34#include <map>
35#include <memory>
36#include <optional>
37#include <phosphor-logging/log.hpp>
38#include <sdbusplus/bus.hpp>
39#include <stdexcept>
40#include <string>
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +000041#include <user_channel/channel_layer.hpp>
Willy Tude54f482021-01-26 15:59:09 -080042#include <utility>
43#include <variant>
44
Scron Chang2703b022021-07-06 15:47:45 +080045#ifdef FEATURE_HYBRID_SENSORS
46
47#include "sensordatahandler.hpp"
48namespace ipmi
49{
50namespace sensor
51{
52extern const IdInfoMap sensors;
53} // namespace sensor
54} // namespace ipmi
55#endif
56
JeffLind950f412021-10-20 18:49:34 +080057constexpr std::array<const char*, 7> suffixes = {
58 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
59 "_Output_Power", "_Input_Power", "_Temperature"};
Willy Tude54f482021-01-26 15:59:09 -080060namespace ipmi
61{
Hao Jiangd48c9212021-02-03 15:45:06 -080062
63using phosphor::logging::entry;
64using phosphor::logging::level;
65using phosphor::logging::log;
66
Willy Tude54f482021-01-26 15:59:09 -080067static constexpr int sensorMapUpdatePeriod = 10;
Alex Qiu9ab2f942020-07-15 17:56:21 -070068static constexpr int sensorMapSdrUpdatePeriod = 60;
Willy Tude54f482021-01-26 15:59:09 -080069
Willy Tu38e7a2b2021-03-29 15:09:56 -070070// BMC I2C address is generally at 0x20
71static constexpr uint8_t bmcI2CAddr = 0x20;
72
Willy Tude54f482021-01-26 15:59:09 -080073constexpr size_t maxSDRTotalSize =
74 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
75constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
76
77static uint16_t sdrReservationID;
78static uint32_t sdrLastAdd = noTimestamp;
79static uint32_t sdrLastRemove = noTimestamp;
80static constexpr size_t lastRecordIndex = 0xFFFF;
Johnathan Mantey6619ae42021-08-06 11:21:10 -070081
82// The IPMI spec defines four Logical Units (LUN), each capable of supporting
83// 255 sensors. The 256 values assigned to LUN 2 are special and are not used
84// for general purpose sensors. Each LUN reserves location 0xFF. The maximum
85// number of IPMI sensors are LUN 0 + LUN 1 + LUN 2, less the reserved
86// location.
87static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
88
89static constexpr size_t lun0MaxSensorNum = 0xfe;
90static constexpr size_t lun1MaxSensorNum = 0x1fe;
91static constexpr size_t lun3MaxSensorNum = 0x3fe;
Willy Tude54f482021-01-26 15:59:09 -080092static constexpr int GENERAL_ERROR = -1;
93
Willy Tude54f482021-01-26 15:59:09 -080094static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
95
96// Specify the comparison required to sort and find char* map objects
97struct CmpStr
98{
99 bool operator()(const char* a, const char* b) const
100 {
101 return std::strcmp(a, b) < 0;
102 }
103};
104const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
105 sensorUnits{{{"temperature", SensorUnits::degreesC},
106 {"voltage", SensorUnits::volts},
107 {"current", SensorUnits::amps},
108 {"fan_tach", SensorUnits::rpm},
109 {"power", SensorUnits::watts}}};
110
111void registerSensorFunctions() __attribute__((constructor));
112
113static sdbusplus::bus::match::match sensorAdded(
114 *getSdBus(),
115 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
116 "sensors/'",
117 [](sdbusplus::message::message& m) {
118 getSensorTree().clear();
119 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
120 std::chrono::system_clock::now().time_since_epoch())
121 .count();
122 });
123
124static sdbusplus::bus::match::match sensorRemoved(
125 *getSdBus(),
126 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
127 "sensors/'",
128 [](sdbusplus::message::message& m) {
129 getSensorTree().clear();
130 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
131 std::chrono::system_clock::now().time_since_epoch())
132 .count();
133 });
134
135// this keeps track of deassertions for sensor event status command. A
136// deasertion can only happen if an assertion was seen first.
137static boost::container::flat_map<
138 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
139 thresholdDeassertMap;
140
141static sdbusplus::bus::match::match thresholdChanged(
142 *getSdBus(),
143 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
144 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
145 [](sdbusplus::message::message& m) {
146 boost::container::flat_map<std::string, std::variant<bool, double>>
147 values;
148 m.read(std::string(), values);
149
150 auto findAssert =
151 std::find_if(values.begin(), values.end(), [](const auto& pair) {
152 return pair.first.find("Alarm") != std::string::npos;
153 });
154 if (findAssert != values.end())
155 {
156 auto ptr = std::get_if<bool>(&(findAssert->second));
157 if (ptr == nullptr)
158 {
159 phosphor::logging::log<phosphor::logging::level::ERR>(
160 "thresholdChanged: Assert non bool");
161 return;
162 }
163 if (*ptr)
164 {
165 phosphor::logging::log<phosphor::logging::level::INFO>(
166 "thresholdChanged: Assert",
167 phosphor::logging::entry("SENSOR=%s", m.get_path()));
168 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
169 }
170 else
171 {
172 auto& value =
173 thresholdDeassertMap[m.get_path()][findAssert->first];
174 if (value)
175 {
176 phosphor::logging::log<phosphor::logging::level::INFO>(
177 "thresholdChanged: deassert",
178 phosphor::logging::entry("SENSOR=%s", m.get_path()));
179 value = *ptr;
180 }
181 }
182 }
183 });
184
Hao Jiangd2afd052020-12-10 15:09:32 -0800185namespace sensor
186{
187static constexpr const char* vrInterface =
188 "xyz.openbmc_project.Control.VoltageRegulatorMode";
189static constexpr const char* sensorInterface =
190 "xyz.openbmc_project.Sensor.Value";
191} // namespace sensor
192
Willy Tude54f482021-01-26 15:59:09 -0800193static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
194 double& min)
195{
196 max = 127;
197 min = -128;
198
Hao Jiangd2afd052020-12-10 15:09:32 -0800199 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800200 auto critical =
201 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
202 auto warning =
203 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
204
205 if (sensorObject != sensorMap.end())
206 {
207 auto maxMap = sensorObject->second.find("MaxValue");
208 auto minMap = sensorObject->second.find("MinValue");
209
210 if (maxMap != sensorObject->second.end())
211 {
212 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
213 }
214 if (minMap != sensorObject->second.end())
215 {
216 min = std::visit(VariantToDoubleVisitor(), minMap->second);
217 }
218 }
219 if (critical != sensorMap.end())
220 {
221 auto lower = critical->second.find("CriticalLow");
222 auto upper = critical->second.find("CriticalHigh");
223 if (lower != critical->second.end())
224 {
225 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300226 if (std::isfinite(value))
227 {
228 min = std::min(value, min);
229 }
Willy Tude54f482021-01-26 15:59:09 -0800230 }
231 if (upper != critical->second.end())
232 {
233 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300234 if (std::isfinite(value))
235 {
236 max = std::max(value, max);
237 }
Willy Tude54f482021-01-26 15:59:09 -0800238 }
239 }
240 if (warning != sensorMap.end())
241 {
242
243 auto lower = warning->second.find("WarningLow");
244 auto upper = warning->second.find("WarningHigh");
245 if (lower != warning->second.end())
246 {
247 double value = std::visit(VariantToDoubleVisitor(), lower->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300248 if (std::isfinite(value))
249 {
250 min = std::min(value, min);
251 }
Willy Tude54f482021-01-26 15:59:09 -0800252 }
253 if (upper != warning->second.end())
254 {
255 double value = std::visit(VariantToDoubleVisitor(), upper->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +0300256 if (std::isfinite(value))
257 {
258 max = std::max(value, max);
259 }
Willy Tude54f482021-01-26 15:59:09 -0800260 }
261 }
262}
263
264static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700265 std::string sensorPath, DbusInterfaceMap& sensorMap,
266 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800267{
Scron Chang2703b022021-07-06 15:47:45 +0800268#ifdef FEATURE_HYBRID_SENSORS
269 if (auto sensor = findStaticSensor(sensorPath);
270 sensor != ipmi::sensor::sensors.end() &&
271 getSensorEventTypeFromPath(sensorPath) !=
272 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
273 {
274 // If the incoming sensor is a discrete sensor, it might fail in
275 // getManagedObjects(), return true, and use its own getFunc to get
276 // value.
277 return true;
278 }
279#endif
280
Willy Tude54f482021-01-26 15:59:09 -0800281 static boost::container::flat_map<
282 std::string, std::chrono::time_point<std::chrono::steady_clock>>
283 updateTimeMap;
284
285 auto updateFind = updateTimeMap.find(sensorConnection);
286 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
287 if (updateFind != updateTimeMap.end())
288 {
289 lastUpdate = updateFind->second;
290 }
291
292 auto now = std::chrono::steady_clock::now();
293
294 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700295 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800296 {
Willy Tude54f482021-01-26 15:59:09 -0800297 ObjectValueTree managedObjects;
298 boost::system::error_code ec = getManagedObjects(
299 ctx, sensorConnection.c_str(), "/", managedObjects);
300 if (ec)
301 {
302 phosphor::logging::log<phosphor::logging::level::ERR>(
303 "GetMangagedObjects for getSensorMap failed",
304 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
305
306 return false;
307 }
308
309 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700310 // Update time after finish building the map which allow the
311 // data to be cached for updatePeriod plus the build time.
312 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800313 }
314 auto connection = SensorCache.find(sensorConnection);
315 if (connection == SensorCache.end())
316 {
317 return false;
318 }
319 auto path = connection->second.find(sensorPath);
320 if (path == connection->second.end())
321 {
322 return false;
323 }
324 sensorMap = path->second;
325
326 return true;
327}
328
Hao Jiangd2afd052020-12-10 15:09:32 -0800329namespace sensor
330{
Hao Jiangd48c9212021-02-03 15:45:06 -0800331// Read VR profiles from sensor(daemon) interface
332static std::optional<std::vector<std::string>>
333 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800334{
335 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800336 auto supportedProperty = object.find("Supported");
337 if (supportedProperty == object.end() ||
338 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800339 {
340 phosphor::logging::log<phosphor::logging::level::ERR>(
341 "Missing the required Supported and Selected properties");
342 return std::nullopt;
343 }
344
345 const auto profilesPtr =
346 std::get_if<std::vector<std::string>>(&supportedProperty->second);
347
348 if (profilesPtr == nullptr)
349 {
350 phosphor::logging::log<phosphor::logging::level::ERR>(
351 "property is not array of string");
352 return std::nullopt;
353 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800354 return *profilesPtr;
355}
356
357// Calculate VR Mode from input IPMI discrete event bytes
358static std::optional<std::string>
359 calculateVRMode(uint15_t assertOffset,
360 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
361{
362 // get VR mode profiles from Supported Interface
363 auto profiles = getSupportedVrProfiles(VRObject);
364 if (!profiles)
365 {
366 return std::nullopt;
367 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800368
369 // interpret IPMI cmd bits into profiles' index
370 long unsigned int index = 0;
371 // only one bit should be set and the highest bit should not be used.
372 if (assertOffset == 0 || assertOffset == (1u << 15) ||
373 (assertOffset & (assertOffset - 1)))
374 {
375 phosphor::logging::log<phosphor::logging::level::ERR>(
376 "IPMI cmd format incorrect",
377
378 phosphor::logging::entry("BYTES=%#02x",
379 static_cast<uint16_t>(assertOffset)));
380 return std::nullopt;
381 }
382
383 while (assertOffset != 1)
384 {
385 assertOffset >>= 1;
386 index++;
387 }
388
Hao Jiangd48c9212021-02-03 15:45:06 -0800389 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800390 {
391 phosphor::logging::log<phosphor::logging::level::ERR>(
392 "profile index out of boundary");
393 return std::nullopt;
394 }
395
Hao Jiangd48c9212021-02-03 15:45:06 -0800396 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800397}
398
399// Calculate sensor value from IPMI reading byte
400static std::optional<double>
401 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
402 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
403{
404 if (valueObject.find("Value") == valueObject.end())
405 {
406 phosphor::logging::log<phosphor::logging::level::ERR>(
407 "Missing the required Value property");
408 return std::nullopt;
409 }
410
411 double max = 0;
412 double min = 0;
413 getSensorMaxMin(sensorMap, max, min);
414
415 int16_t mValue = 0;
416 int16_t bValue = 0;
417 int8_t rExp = 0;
418 int8_t bExp = 0;
419 bool bSigned = false;
420
421 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
422 {
423 return std::nullopt;
424 }
425
426 double value = bSigned ? ((int8_t)reading) : reading;
427
428 value *= ((double)mValue);
429 value += ((double)bValue) * std::pow(10.0, bExp);
430 value *= std::pow(10.0, rExp);
431
432 return value;
433}
434
Willy Tu38e7a2b2021-03-29 15:09:56 -0700435// Extract file name from sensor path as the sensors SDR ID. Simplify the name
436// if it is too long.
437std::string parseSdrIdFromPath(const std::string& path)
438{
439 std::string name;
440 size_t nameStart = path.rfind("/");
441 if (nameStart != std::string::npos)
442 {
443 name = path.substr(nameStart + 1, std::string::npos - nameStart);
444 }
445
Willy Tu38e7a2b2021-03-29 15:09:56 -0700446 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
447 {
448 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800449 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700450 {
JeffLind950f412021-10-20 18:49:34 +0800451 if (boost::ends_with(name, suffix))
452 {
453 boost::replace_all(name, suffix, "");
454 break;
455 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700456 }
Duke Du97014f52021-12-16 17:21:01 +0800457 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
458 {
459 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
460 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700461 }
JeffLind950f412021-10-20 18:49:34 +0800462 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700463 return name;
464}
465
Hao Jiangd48c9212021-02-03 15:45:06 -0800466bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
467 const std::string& path,
468 const ipmi::DbusInterfaceMap::mapped_type& object,
469 std::bitset<16>& assertions)
470{
471 auto profiles = sensor::getSupportedVrProfiles(object);
472 if (!profiles)
473 {
474 return false;
475 }
476 ipmi::Value modeVariant;
477
478 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
479 "Selected", modeVariant);
480 if (ec)
481 {
482 log<level::ERR>("Failed to get property",
483 entry("PROPERTY=%s", "Selected"),
484 entry("PATH=%s", path.c_str()),
485 entry("INTERFACE=%s", sensor::sensorInterface),
486 entry("WHAT=%s", ec.message().c_str()));
487 return false;
488 }
489
490 auto mode = std::get_if<std::string>(&modeVariant);
491 if (mode == nullptr)
492 {
493 log<level::ERR>("property is not a string",
494 entry("PROPERTY=%s", "Selected"),
495 entry("PATH=%s", path.c_str()),
496 entry("INTERFACE=%s", sensor::sensorInterface));
497 return false;
498 }
499
500 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
501 if (itr == profiles->end())
502 {
503 using namespace phosphor::logging;
504 log<level::ERR>("VR mode doesn't match any of its profiles",
505 entry("PATH=%s", path.c_str()));
506 return false;
507 }
508 std::size_t index =
509 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
510
511 // map index to reponse event assertion bit.
512 if (index < 8)
513 {
514 assertions.set(1u << index);
515 }
516 else if (index < 15)
517 {
518 assertions.set(1u << (index - 8));
519 }
520 else
521 {
522 log<level::ERR>("VR profile index reaches max assertion bit",
523 entry("PATH=%s", path.c_str()),
524 entry("INDEX=%uz", index));
525 return false;
526 }
527 if constexpr (debug)
528 {
529 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
530 << " mode is: [" << index << "] " << *mode << std::endl;
531 }
532 return true;
533}
Hao Jiangd2afd052020-12-10 15:09:32 -0800534} // namespace sensor
535
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000536ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
537 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800538{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000539 constexpr const uint8_t validEnvmRev = 0x04;
540 constexpr const uint8_t lastSensorType = 0x2C;
541 constexpr const uint8_t oemReserved = 0xC0;
542
543 uint8_t generatorID = 0;
544 uint8_t evmRev = 0;
545 uint8_t sensorType = 0;
546 uint8_t sensorNum = 0;
547 uint8_t eventType = 0;
548 uint8_t eventData1 = 0;
549 std::optional<uint8_t> eventData2 = 0;
550 std::optional<uint8_t> eventData3 = 0;
551 ipmi::ChannelInfo chInfo;
552
553 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
554 {
555 phosphor::logging::log<phosphor::logging::level::ERR>(
556 "Failed to get Channel Info",
557 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
558 return ipmi::responseUnspecifiedError();
559 }
560
561 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
562 ipmi::EChannelMediumType::systemInterface)
563 {
564
565 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
566 eventData1, eventData2, eventData3);
567 }
568 else
569 {
570
571 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
572 eventData2, eventData3);
573 generatorID = ctx->rqSA;
574 }
575
576 if (!p.fullyUnpacked())
577 {
578 return ipmi::responseReqDataLenInvalid();
579 }
580
581 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
582 if (evmRev != validEnvmRev)
583 {
584 return ipmi::responseInvalidFieldRequest();
585 }
586 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
587 {
588 return ipmi::responseInvalidFieldRequest();
589 }
590
Willy Tude54f482021-01-26 15:59:09 -0800591 return ipmi::responseSuccess();
592}
593
Willy Tudbafbce2021-03-29 00:37:05 -0700594ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
595 uint8_t sensorNumber, uint8_t operation,
596 uint8_t reading, uint15_t assertOffset,
597 bool resvd1, uint15_t deassertOffset,
598 bool resvd2, uint8_t eventData1,
599 uint8_t eventData2, uint8_t eventData3)
600{
601 std::string connection;
602 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700603 std::vector<std::string> interfaces;
604
605 ipmi::Cc status =
606 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700607 if (status)
608 {
609 return ipmi::response(status);
610 }
611
Hao Jiangd2afd052020-12-10 15:09:32 -0800612 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700613 if (std::find(interfaces.begin(), interfaces.end(),
614 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700615 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700616 DbusInterfaceMap sensorMap;
617 if (!getSensorMap(ctx, connection, path, sensorMap))
618 {
619 return ipmi::responseResponseError();
620 }
621 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800622 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700623 {
624 return ipmi::responseResponseError();
625 }
626
Jie Yangf0a89942021-07-29 15:30:25 -0700627 // Only allow external SetSensor if write permission granted
628 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
629 {
630 return ipmi::responseResponseError();
631 }
632
Hao Jiangd2afd052020-12-10 15:09:32 -0800633 auto value =
634 sensor::calculateValue(reading, sensorMap, sensorObject->second);
635 if (!value)
636 {
637 return ipmi::responseResponseError();
638 }
639
640 if constexpr (debug)
641 {
642 phosphor::logging::log<phosphor::logging::level::INFO>(
643 "IPMI SET_SENSOR",
644 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
645 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
646 phosphor::logging::entry("VALUE=%f", *value));
647 }
648
649 boost::system::error_code ec =
650 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
651 "Value", ipmi::Value(*value));
652
653 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500654 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800655 // callback functions for now (e.g. ipmiSetSensorReading).
656 if (ec)
657 {
658 using namespace phosphor::logging;
659 log<level::ERR>("Failed to set property",
660 entry("PROPERTY=%s", "Value"),
661 entry("PATH=%s", path.c_str()),
662 entry("INTERFACE=%s", sensor::sensorInterface),
663 entry("WHAT=%s", ec.message().c_str()));
664 return ipmi::responseResponseError();
665 }
666 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700667 }
668
Hao Jiange39d4d82021-04-16 17:02:40 -0700669 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
670 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700671 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700672 DbusInterfaceMap sensorMap;
673 if (!getSensorMap(ctx, connection, path, sensorMap))
674 {
675 return ipmi::responseResponseError();
676 }
677 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800678 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700679 {
680 return ipmi::responseResponseError();
681 }
682
Hao Jiangd2afd052020-12-10 15:09:32 -0800683 // VR sensors are treated as a special case and we will not check the
684 // write permission for VR sensors, since they always deemed writable
685 // and permission table are not applied to VR sensors.
686 auto vrMode =
687 sensor::calculateVRMode(assertOffset, sensorObject->second);
688 if (!vrMode)
689 {
690 return ipmi::responseResponseError();
691 }
692 boost::system::error_code ec = setDbusProperty(
693 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
694 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500695 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800696 // callback functions for now (e.g. ipmiSetSensorReading).
697 if (ec)
698 {
699 using namespace phosphor::logging;
700 log<level::ERR>("Failed to set property",
701 entry("PROPERTY=%s", "Selected"),
702 entry("PATH=%s", path.c_str()),
703 entry("INTERFACE=%s", sensor::sensorInterface),
704 entry("WHAT=%s", ec.message().c_str()));
705 return ipmi::responseResponseError();
706 }
707 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700708 }
709
Hao Jiangd2afd052020-12-10 15:09:32 -0800710 phosphor::logging::log<phosphor::logging::level::ERR>(
711 "unknown sensor type",
712 phosphor::logging::entry("PATH=%s", path.c_str()));
713 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700714}
715
Willy Tude54f482021-01-26 15:59:09 -0800716ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
717 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
718{
719 std::string connection;
720 std::string path;
721
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000722 if (sensnum == reservedSensorNumber)
723 {
724 return ipmi::responseInvalidFieldRequest();
725 }
726
Willy Tude54f482021-01-26 15:59:09 -0800727 auto status = getSensorConnection(ctx, sensnum, connection, path);
728 if (status)
729 {
730 return ipmi::response(status);
731 }
732
Scron Chang2703b022021-07-06 15:47:45 +0800733#ifdef FEATURE_HYBRID_SENSORS
734 if (auto sensor = findStaticSensor(path);
735 sensor != ipmi::sensor::sensors.end() &&
736 getSensorEventTypeFromPath(path) !=
737 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
738 {
739 if (ipmi::sensor::Mutability::Read !=
740 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
741 {
742 return ipmi::responseIllegalCommand();
743 }
744
745 uint8_t operation;
746 try
747 {
748 ipmi::sensor::GetSensorResponse getResponse =
749 sensor->second.getFunc(sensor->second);
750
751 if (getResponse.readingOrStateUnavailable)
752 {
753 operation |= static_cast<uint8_t>(
754 IPMISensorReadingByte2::readingStateUnavailable);
755 }
756 if (getResponse.scanningEnabled)
757 {
758 operation |= static_cast<uint8_t>(
759 IPMISensorReadingByte2::sensorScanningEnable);
760 }
761 if (getResponse.allEventMessagesEnabled)
762 {
763 operation |= static_cast<uint8_t>(
764 IPMISensorReadingByte2::eventMessagesEnable);
765 }
766 return ipmi::responseSuccess(
767 getResponse.reading, operation,
768 getResponse.thresholdLevelsStates,
769 getResponse.discreteReadingSensorStates);
770 }
771 catch (const std::exception& e)
772 {
773 operation |= static_cast<uint8_t>(
774 IPMISensorReadingByte2::readingStateUnavailable);
775 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
776 }
777 }
778#endif
779
Willy Tude54f482021-01-26 15:59:09 -0800780 DbusInterfaceMap sensorMap;
781 if (!getSensorMap(ctx, connection, path, sensorMap))
782 {
783 return ipmi::responseResponseError();
784 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800785 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800786
787 if (sensorObject == sensorMap.end() ||
788 sensorObject->second.find("Value") == sensorObject->second.end())
789 {
790 return ipmi::responseResponseError();
791 }
792 auto& valueVariant = sensorObject->second["Value"];
793 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
794
795 double max = 0;
796 double min = 0;
797 getSensorMaxMin(sensorMap, max, min);
798
799 int16_t mValue = 0;
800 int16_t bValue = 0;
801 int8_t rExp = 0;
802 int8_t bExp = 0;
803 bool bSigned = false;
804
805 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
806 {
807 return ipmi::responseResponseError();
808 }
809
810 uint8_t value =
811 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
812 uint8_t operation =
813 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
814 operation |=
815 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
816 bool notReading = std::isnan(reading);
817
818 if (!notReading)
819 {
820 auto availableObject =
821 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
822 if (availableObject != sensorMap.end())
823 {
824 auto findAvailable = availableObject->second.find("Available");
825 if (findAvailable != availableObject->second.end())
826 {
827 bool* available = std::get_if<bool>(&(findAvailable->second));
828 if (available && !(*available))
829 {
830 notReading = true;
831 }
832 }
833 }
834 }
835
836 if (notReading)
837 {
838 operation |= static_cast<uint8_t>(
839 IPMISensorReadingByte2::readingStateUnavailable);
840 }
841
Josh Lehana55c9532020-10-28 21:59:06 -0700842 if constexpr (details::enableInstrumentation)
843 {
844 int byteValue;
845 if (bSigned)
846 {
847 byteValue = static_cast<int>(static_cast<int8_t>(value));
848 }
849 else
850 {
851 byteValue = static_cast<int>(static_cast<uint8_t>(value));
852 }
853
854 // Keep stats on the reading just obtained, even if it is "NaN"
855 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
856 {
857 // This is the first reading, show the coefficients
858 double step = (max - min) / 255.0;
859 std::cerr << "IPMI sensor "
860 << details::sdrStatsTable.getName(sensnum)
861 << ": Range min=" << min << " max=" << max
862 << ", step=" << step
863 << ", Coefficients mValue=" << static_cast<int>(mValue)
864 << " rExp=" << static_cast<int>(rExp)
865 << " bValue=" << static_cast<int>(bValue)
866 << " bExp=" << static_cast<int>(bExp)
867 << " bSigned=" << static_cast<int>(bSigned) << "\n";
868 }
869 }
870
Willy Tude54f482021-01-26 15:59:09 -0800871 uint8_t thresholds = 0;
872
873 auto warningObject =
874 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
875 if (warningObject != sensorMap.end())
876 {
877 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
878 auto alarmLow = warningObject->second.find("WarningAlarmLow");
879 if (alarmHigh != warningObject->second.end())
880 {
881 if (std::get<bool>(alarmHigh->second))
882 {
883 thresholds |= static_cast<uint8_t>(
884 IPMISensorReadingByte3::upperNonCritical);
885 }
886 }
887 if (alarmLow != warningObject->second.end())
888 {
889 if (std::get<bool>(alarmLow->second))
890 {
891 thresholds |= static_cast<uint8_t>(
892 IPMISensorReadingByte3::lowerNonCritical);
893 }
894 }
895 }
896
897 auto criticalObject =
898 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
899 if (criticalObject != sensorMap.end())
900 {
901 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
902 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
903 if (alarmHigh != criticalObject->second.end())
904 {
905 if (std::get<bool>(alarmHigh->second))
906 {
907 thresholds |=
908 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
909 }
910 }
911 if (alarmLow != criticalObject->second.end())
912 {
913 if (std::get<bool>(alarmLow->second))
914 {
915 thresholds |=
916 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
917 }
918 }
919 }
920
921 // no discrete as of today so optional byte is never returned
922 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
923}
924
925/** @brief implements the Set Sensor threshold command
926 * @param sensorNumber - sensor number
927 * @param lowerNonCriticalThreshMask
928 * @param lowerCriticalThreshMask
929 * @param lowerNonRecovThreshMask
930 * @param upperNonCriticalThreshMask
931 * @param upperCriticalThreshMask
932 * @param upperNonRecovThreshMask
933 * @param reserved
934 * @param lowerNonCritical - lower non-critical threshold
935 * @param lowerCritical - Lower critical threshold
936 * @param lowerNonRecoverable - Lower non recovarable threshold
937 * @param upperNonCritical - Upper non-critical threshold
938 * @param upperCritical - Upper critical
939 * @param upperNonRecoverable - Upper Non-recoverable
940 *
941 * @returns IPMI completion code
942 */
943ipmi::RspType<> ipmiSenSetSensorThresholds(
944 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
945 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
946 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
947 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
948 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
949 uint8_t upperNonCritical, uint8_t upperCritical,
950 uint8_t upperNonRecoverable)
951{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000952 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800953 {
954 return ipmi::responseInvalidFieldRequest();
955 }
956
957 // lower nc and upper nc not suppported on any sensor
958 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
959 {
960 return ipmi::responseInvalidFieldRequest();
961 }
962
963 // if none of the threshold mask are set, nothing to do
964 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
965 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
966 upperCriticalThreshMask | upperNonRecovThreshMask))
967 {
968 return ipmi::responseSuccess();
969 }
970
971 std::string connection;
972 std::string path;
973
974 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
975 if (status)
976 {
977 return ipmi::response(status);
978 }
979 DbusInterfaceMap sensorMap;
980 if (!getSensorMap(ctx, connection, path, sensorMap))
981 {
982 return ipmi::responseResponseError();
983 }
984
985 double max = 0;
986 double min = 0;
987 getSensorMaxMin(sensorMap, max, min);
988
989 int16_t mValue = 0;
990 int16_t bValue = 0;
991 int8_t rExp = 0;
992 int8_t bExp = 0;
993 bool bSigned = false;
994
995 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
996 {
997 return ipmi::responseResponseError();
998 }
999
1000 // store a vector of property name, value to set, and interface
1001 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1002
1003 // define the indexes of the tuple
1004 constexpr uint8_t propertyName = 0;
1005 constexpr uint8_t thresholdValue = 1;
1006 constexpr uint8_t interface = 2;
1007 // verifiy all needed fields are present
1008 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1009 {
1010 auto findThreshold =
1011 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1012 if (findThreshold == sensorMap.end())
1013 {
1014 return ipmi::responseInvalidFieldRequest();
1015 }
1016 if (lowerCriticalThreshMask)
1017 {
1018 auto findLower = findThreshold->second.find("CriticalLow");
1019 if (findLower == findThreshold->second.end())
1020 {
1021 return ipmi::responseInvalidFieldRequest();
1022 }
1023 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1024 findThreshold->first);
1025 }
1026 if (upperCriticalThreshMask)
1027 {
1028 auto findUpper = findThreshold->second.find("CriticalHigh");
1029 if (findUpper == findThreshold->second.end())
1030 {
1031 return ipmi::responseInvalidFieldRequest();
1032 }
1033 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1034 findThreshold->first);
1035 }
1036 }
1037 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1038 {
1039 auto findThreshold =
1040 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1041 if (findThreshold == sensorMap.end())
1042 {
1043 return ipmi::responseInvalidFieldRequest();
1044 }
1045 if (lowerNonCriticalThreshMask)
1046 {
1047 auto findLower = findThreshold->second.find("WarningLow");
1048 if (findLower == findThreshold->second.end())
1049 {
1050 return ipmi::responseInvalidFieldRequest();
1051 }
1052 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1053 findThreshold->first);
1054 }
1055 if (upperNonCriticalThreshMask)
1056 {
1057 auto findUpper = findThreshold->second.find("WarningHigh");
1058 if (findUpper == findThreshold->second.end())
1059 {
1060 return ipmi::responseInvalidFieldRequest();
1061 }
1062 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1063 findThreshold->first);
1064 }
1065 }
1066 for (const auto& property : thresholdsToSet)
1067 {
1068 // from section 36.3 in the IPMI Spec, assume all linear
1069 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1070 (bValue * std::pow(10.0, bExp))) *
1071 std::pow(10.0, rExp);
1072 setDbusProperty(
1073 *getSdBus(), connection, path, std::get<interface>(property),
1074 std::get<propertyName>(property), ipmi::Value(valueToSet));
1075 }
1076 return ipmi::responseSuccess();
1077}
1078
1079IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1080{
1081 IPMIThresholds resp;
1082 auto warningInterface =
1083 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1084 auto criticalInterface =
1085 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1086
1087 if ((warningInterface != sensorMap.end()) ||
1088 (criticalInterface != sensorMap.end()))
1089 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001090 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001091
1092 if (sensorPair == sensorMap.end())
1093 {
1094 // should not have been able to find a sensor not implementing
1095 // the sensor object
1096 throw std::runtime_error("Invalid sensor map");
1097 }
1098
1099 double max = 0;
1100 double min = 0;
1101 getSensorMaxMin(sensorMap, max, min);
1102
1103 int16_t mValue = 0;
1104 int16_t bValue = 0;
1105 int8_t rExp = 0;
1106 int8_t bExp = 0;
1107 bool bSigned = false;
1108
1109 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1110 {
1111 throw std::runtime_error("Invalid sensor atrributes");
1112 }
1113 if (warningInterface != sensorMap.end())
1114 {
1115 auto& warningMap = warningInterface->second;
1116
1117 auto warningHigh = warningMap.find("WarningHigh");
1118 auto warningLow = warningMap.find("WarningLow");
1119
1120 if (warningHigh != warningMap.end())
1121 {
1122
1123 double value =
1124 std::visit(VariantToDoubleVisitor(), warningHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001125 if (std::isfinite(value))
1126 {
1127 resp.warningHigh = scaleIPMIValueFromDouble(
1128 value, mValue, rExp, bValue, bExp, bSigned);
1129 }
Willy Tude54f482021-01-26 15:59:09 -08001130 }
1131 if (warningLow != warningMap.end())
1132 {
1133 double value =
1134 std::visit(VariantToDoubleVisitor(), warningLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001135 if (std::isfinite(value))
1136 {
1137 resp.warningLow = scaleIPMIValueFromDouble(
1138 value, mValue, rExp, bValue, bExp, bSigned);
1139 }
Willy Tude54f482021-01-26 15:59:09 -08001140 }
1141 }
1142 if (criticalInterface != sensorMap.end())
1143 {
1144 auto& criticalMap = criticalInterface->second;
1145
1146 auto criticalHigh = criticalMap.find("CriticalHigh");
1147 auto criticalLow = criticalMap.find("CriticalLow");
1148
1149 if (criticalHigh != criticalMap.end())
1150 {
1151 double value =
1152 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001153 if (std::isfinite(value))
1154 {
1155 resp.criticalHigh = scaleIPMIValueFromDouble(
1156 value, mValue, rExp, bValue, bExp, bSigned);
1157 }
Willy Tude54f482021-01-26 15:59:09 -08001158 }
1159 if (criticalLow != criticalMap.end())
1160 {
1161 double value =
1162 std::visit(VariantToDoubleVisitor(), criticalLow->second);
Konstantin Aladyshev8265af22021-12-16 18:18:10 +03001163 if (std::isfinite(value))
1164 {
1165 resp.criticalLow = scaleIPMIValueFromDouble(
1166 value, mValue, rExp, bValue, bExp, bSigned);
1167 }
Willy Tude54f482021-01-26 15:59:09 -08001168 }
1169 }
1170 }
1171 return resp;
1172}
1173
1174ipmi::RspType<uint8_t, // readable
1175 uint8_t, // lowerNCrit
1176 uint8_t, // lowerCrit
1177 uint8_t, // lowerNrecoverable
1178 uint8_t, // upperNC
1179 uint8_t, // upperCrit
1180 uint8_t> // upperNRecoverable
1181 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1182{
1183 std::string connection;
1184 std::string path;
1185
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001186 if (sensorNumber == reservedSensorNumber)
1187 {
1188 return ipmi::responseInvalidFieldRequest();
1189 }
1190
Willy Tude54f482021-01-26 15:59:09 -08001191 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1192 if (status)
1193 {
1194 return ipmi::response(status);
1195 }
1196
1197 DbusInterfaceMap sensorMap;
1198 if (!getSensorMap(ctx, connection, path, sensorMap))
1199 {
1200 return ipmi::responseResponseError();
1201 }
1202
1203 IPMIThresholds thresholdData;
1204 try
1205 {
1206 thresholdData = getIPMIThresholds(sensorMap);
1207 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001208 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001209 {
1210 return ipmi::responseResponseError();
1211 }
1212
1213 uint8_t readable = 0;
1214 uint8_t lowerNC = 0;
1215 uint8_t lowerCritical = 0;
1216 uint8_t lowerNonRecoverable = 0;
1217 uint8_t upperNC = 0;
1218 uint8_t upperCritical = 0;
1219 uint8_t upperNonRecoverable = 0;
1220
1221 if (thresholdData.warningHigh)
1222 {
1223 readable |=
1224 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1225 upperNC = *thresholdData.warningHigh;
1226 }
1227 if (thresholdData.warningLow)
1228 {
1229 readable |=
1230 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1231 lowerNC = *thresholdData.warningLow;
1232 }
1233
1234 if (thresholdData.criticalHigh)
1235 {
1236 readable |=
1237 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1238 upperCritical = *thresholdData.criticalHigh;
1239 }
1240 if (thresholdData.criticalLow)
1241 {
1242 readable |=
1243 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1244 lowerCritical = *thresholdData.criticalLow;
1245 }
1246
1247 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1248 lowerNonRecoverable, upperNC, upperCritical,
1249 upperNonRecoverable);
1250}
1251
1252/** @brief implements the get Sensor event enable command
1253 * @param sensorNumber - sensor number
1254 *
1255 * @returns IPMI completion code plus response data
1256 * - enabled - Sensor Event messages
1257 * - assertionEnabledLsb - Assertion event messages
1258 * - assertionEnabledMsb - Assertion event messages
1259 * - deassertionEnabledLsb - Deassertion event messages
1260 * - deassertionEnabledMsb - Deassertion event messages
1261 */
1262
1263ipmi::RspType<uint8_t, // enabled
1264 uint8_t, // assertionEnabledLsb
1265 uint8_t, // assertionEnabledMsb
1266 uint8_t, // deassertionEnabledLsb
1267 uint8_t> // deassertionEnabledMsb
1268 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1269{
1270 std::string connection;
1271 std::string path;
1272
1273 uint8_t enabled = 0;
1274 uint8_t assertionEnabledLsb = 0;
1275 uint8_t assertionEnabledMsb = 0;
1276 uint8_t deassertionEnabledLsb = 0;
1277 uint8_t deassertionEnabledMsb = 0;
1278
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001279 if (sensorNum == reservedSensorNumber)
1280 {
1281 return ipmi::responseInvalidFieldRequest();
1282 }
1283
Willy Tude54f482021-01-26 15:59:09 -08001284 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1285 if (status)
1286 {
1287 return ipmi::response(status);
1288 }
1289
Scron Chang2703b022021-07-06 15:47:45 +08001290#ifdef FEATURE_HYBRID_SENSORS
1291 if (auto sensor = findStaticSensor(path);
1292 sensor != ipmi::sensor::sensors.end() &&
1293 getSensorEventTypeFromPath(path) !=
1294 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1295 {
1296 enabled = static_cast<uint8_t>(
1297 IPMISensorEventEnableByte2::sensorScanningEnable);
1298 uint16_t assertionEnabled = 0;
1299 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1300 ->second.begin()
1301 ->second.second)
1302 {
1303 assertionEnabled |= (1 << offsetValMap.first);
1304 }
1305 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1306 assertionEnabledMsb =
1307 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1308
1309 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1310 assertionEnabledMsb, deassertionEnabledLsb,
1311 deassertionEnabledMsb);
1312 }
1313#endif
1314
Willy Tude54f482021-01-26 15:59:09 -08001315 DbusInterfaceMap sensorMap;
1316 if (!getSensorMap(ctx, connection, path, sensorMap))
1317 {
1318 return ipmi::responseResponseError();
1319 }
1320
1321 auto warningInterface =
1322 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1323 auto criticalInterface =
1324 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1325 if ((warningInterface != sensorMap.end()) ||
1326 (criticalInterface != sensorMap.end()))
1327 {
1328 enabled = static_cast<uint8_t>(
1329 IPMISensorEventEnableByte2::sensorScanningEnable);
1330 if (warningInterface != sensorMap.end())
1331 {
1332 auto& warningMap = warningInterface->second;
1333
1334 auto warningHigh = warningMap.find("WarningHigh");
1335 auto warningLow = warningMap.find("WarningLow");
1336 if (warningHigh != warningMap.end())
1337 {
1338 assertionEnabledLsb |= static_cast<uint8_t>(
1339 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1340 deassertionEnabledLsb |= static_cast<uint8_t>(
1341 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1342 }
1343 if (warningLow != warningMap.end())
1344 {
1345 assertionEnabledLsb |= static_cast<uint8_t>(
1346 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1347 deassertionEnabledLsb |= static_cast<uint8_t>(
1348 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1349 }
1350 }
1351 if (criticalInterface != sensorMap.end())
1352 {
1353 auto& criticalMap = criticalInterface->second;
1354
1355 auto criticalHigh = criticalMap.find("CriticalHigh");
1356 auto criticalLow = criticalMap.find("CriticalLow");
1357
1358 if (criticalHigh != criticalMap.end())
1359 {
1360 assertionEnabledMsb |= static_cast<uint8_t>(
1361 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1362 deassertionEnabledMsb |= static_cast<uint8_t>(
1363 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1364 }
1365 if (criticalLow != criticalMap.end())
1366 {
1367 assertionEnabledLsb |= static_cast<uint8_t>(
1368 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1369 deassertionEnabledLsb |= static_cast<uint8_t>(
1370 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1371 }
1372 }
1373 }
1374
1375 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1376 assertionEnabledMsb, deassertionEnabledLsb,
1377 deassertionEnabledMsb);
1378}
1379
1380/** @brief implements the get Sensor event status command
1381 * @param sensorNumber - sensor number, FFh = reserved
1382 *
1383 * @returns IPMI completion code plus response data
1384 * - sensorEventStatus - Sensor Event messages state
1385 * - assertions - Assertion event messages
1386 * - deassertions - Deassertion event messages
1387 */
1388ipmi::RspType<uint8_t, // sensorEventStatus
1389 std::bitset<16>, // assertions
1390 std::bitset<16> // deassertion
1391 >
1392 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1393{
1394 if (sensorNum == reservedSensorNumber)
1395 {
1396 return ipmi::responseInvalidFieldRequest();
1397 }
1398
1399 std::string connection;
1400 std::string path;
1401 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1402 if (status)
1403 {
1404 phosphor::logging::log<phosphor::logging::level::ERR>(
1405 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1406 phosphor::logging::entry("SENSOR=%d", sensorNum));
1407 return ipmi::response(status);
1408 }
1409
Scron Chang2703b022021-07-06 15:47:45 +08001410#ifdef FEATURE_HYBRID_SENSORS
1411 if (auto sensor = findStaticSensor(path);
1412 sensor != ipmi::sensor::sensors.end() &&
1413 getSensorEventTypeFromPath(path) !=
1414 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1415 {
1416 auto response = ipmi::sensor::get::mapDbusToAssertion(
1417 sensor->second, path, sensor->second.sensorInterface);
1418 std::bitset<16> assertions;
1419 // deassertions are not used.
1420 std::bitset<16> deassertions = 0;
1421 uint8_t sensorEventStatus;
1422 if (response.readingOrStateUnavailable)
1423 {
1424 sensorEventStatus |= static_cast<uint8_t>(
1425 IPMISensorReadingByte2::readingStateUnavailable);
1426 }
1427 if (response.scanningEnabled)
1428 {
1429 sensorEventStatus |= static_cast<uint8_t>(
1430 IPMISensorReadingByte2::sensorScanningEnable);
1431 }
1432 if (response.allEventMessagesEnabled)
1433 {
1434 sensorEventStatus |= static_cast<uint8_t>(
1435 IPMISensorReadingByte2::eventMessagesEnable);
1436 }
1437 assertions |= response.discreteReadingSensorStates << 8;
1438 assertions |= response.thresholdLevelsStates;
1439 return ipmi::responseSuccess(sensorEventStatus, assertions,
1440 deassertions);
1441 }
1442#endif
1443
Willy Tude54f482021-01-26 15:59:09 -08001444 DbusInterfaceMap sensorMap;
1445 if (!getSensorMap(ctx, connection, path, sensorMap))
1446 {
1447 phosphor::logging::log<phosphor::logging::level::ERR>(
1448 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1449 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1450 return ipmi::responseResponseError();
1451 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001452
1453 uint8_t sensorEventStatus =
1454 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1455 std::bitset<16> assertions = 0;
1456 std::bitset<16> deassertions = 0;
1457
1458 // handle VR typed sensor
1459 auto vrInterface = sensorMap.find(sensor::vrInterface);
1460 if (vrInterface != sensorMap.end())
1461 {
1462 if (!sensor::getVrEventStatus(ctx, connection, path,
1463 vrInterface->second, assertions))
1464 {
1465 return ipmi::responseResponseError();
1466 }
1467
1468 // both Event Message and Sensor Scanning are disable for VR.
1469 sensorEventStatus = 0;
1470 return ipmi::responseSuccess(sensorEventStatus, assertions,
1471 deassertions);
1472 }
1473
Willy Tude54f482021-01-26 15:59:09 -08001474 auto warningInterface =
1475 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1476 auto criticalInterface =
1477 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1478
Willy Tude54f482021-01-26 15:59:09 -08001479 std::optional<bool> criticalDeassertHigh =
1480 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1481 std::optional<bool> criticalDeassertLow =
1482 thresholdDeassertMap[path]["CriticalAlarmLow"];
1483 std::optional<bool> warningDeassertHigh =
1484 thresholdDeassertMap[path]["WarningAlarmHigh"];
1485 std::optional<bool> warningDeassertLow =
1486 thresholdDeassertMap[path]["WarningAlarmLow"];
1487
Willy Tude54f482021-01-26 15:59:09 -08001488 if (criticalDeassertHigh && !*criticalDeassertHigh)
1489 {
1490 deassertions.set(static_cast<size_t>(
1491 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1492 }
1493 if (criticalDeassertLow && !*criticalDeassertLow)
1494 {
1495 deassertions.set(static_cast<size_t>(
1496 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1497 }
1498 if (warningDeassertHigh && !*warningDeassertHigh)
1499 {
1500 deassertions.set(static_cast<size_t>(
1501 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1502 }
1503 if (warningDeassertLow && !*warningDeassertLow)
1504 {
1505 deassertions.set(static_cast<size_t>(
1506 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1507 }
1508 if ((warningInterface != sensorMap.end()) ||
1509 (criticalInterface != sensorMap.end()))
1510 {
1511 sensorEventStatus = static_cast<size_t>(
1512 IPMISensorEventEnableByte2::eventMessagesEnable);
1513 if (warningInterface != sensorMap.end())
1514 {
1515 auto& warningMap = warningInterface->second;
1516
1517 auto warningHigh = warningMap.find("WarningAlarmHigh");
1518 auto warningLow = warningMap.find("WarningAlarmLow");
1519 auto warningHighAlarm = false;
1520 auto warningLowAlarm = false;
1521
1522 if (warningHigh != warningMap.end())
1523 {
1524 warningHighAlarm = std::get<bool>(warningHigh->second);
1525 }
1526 if (warningLow != warningMap.end())
1527 {
1528 warningLowAlarm = std::get<bool>(warningLow->second);
1529 }
1530 if (warningHighAlarm)
1531 {
1532 assertions.set(
1533 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1534 upperNonCriticalGoingHigh));
1535 }
1536 if (warningLowAlarm)
1537 {
1538 assertions.set(
1539 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1540 lowerNonCriticalGoingLow));
1541 }
1542 }
1543 if (criticalInterface != sensorMap.end())
1544 {
1545 auto& criticalMap = criticalInterface->second;
1546
1547 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1548 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1549 auto criticalHighAlarm = false;
1550 auto criticalLowAlarm = false;
1551
1552 if (criticalHigh != criticalMap.end())
1553 {
1554 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1555 }
1556 if (criticalLow != criticalMap.end())
1557 {
1558 criticalLowAlarm = std::get<bool>(criticalLow->second);
1559 }
1560 if (criticalHighAlarm)
1561 {
1562 assertions.set(
1563 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1564 upperCriticalGoingHigh));
1565 }
1566 if (criticalLowAlarm)
1567 {
1568 assertions.set(static_cast<size_t>(
1569 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1570 }
1571 }
1572 }
1573
1574 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1575}
1576
Willy Tu38e7a2b2021-03-29 15:09:56 -07001577// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001578void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1579 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001580{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001581 get_sdr::header::set_record_id(
1582 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1583
Willy Tu38e7a2b2021-03-29 15:09:56 -07001584 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1585 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1586
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001587 record.header.sdr_version = ipmiSdrVersion;
1588 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1589 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1590 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001591 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001592 record.key.owner_lun = lun;
1593 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001594}
1595bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1596 uint16_t recordID, const std::string& service,
1597 const std::string& path,
1598 get_sdr::SensorDataFullRecord& record)
1599{
1600 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1601 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1602
1603 DbusInterfaceMap sensorMap;
1604 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1605 {
1606 phosphor::logging::log<phosphor::logging::level::ERR>(
1607 "Failed to update sensor map for threshold sensor",
1608 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1609 phosphor::logging::entry("PATH=%s", path.c_str()));
1610 return false;
1611 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001612
1613 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1614 record.body.sensor_type = getSensorTypeFromPath(path);
1615 std::string type = getSensorTypeStringFromPath(path);
1616 auto typeCstr = type.c_str();
1617 auto findUnits = sensorUnits.find(typeCstr);
1618 if (findUnits != sensorUnits.end())
1619 {
1620 record.body.sensor_units_2_base =
1621 static_cast<uint8_t>(findUnits->second);
1622 } // else default 0x0 unspecified
1623
1624 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1625
Hao Jiangd2afd052020-12-10 15:09:32 -08001626 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001627 if (sensorObject == sensorMap.end())
1628 {
1629 phosphor::logging::log<phosphor::logging::level::ERR>(
1630 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001631 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001632 }
1633
1634 uint8_t entityId = 0;
1635 uint8_t entityInstance = 0x01;
1636
1637 // follow the association chain to get the parent board's entityid and
1638 // entityInstance
1639 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1640
1641 record.body.entity_id = entityId;
1642 record.body.entity_instance = entityInstance;
1643
Shakeeb Pasha93889722021-10-14 10:20:13 +05301644 double max = 0;
1645 double min = 0;
1646 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001647
1648 int16_t mValue = 0;
1649 int8_t rExp = 0;
1650 int16_t bValue = 0;
1651 int8_t bExp = 0;
1652 bool bSigned = false;
1653
1654 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1655 {
1656 phosphor::logging::log<phosphor::logging::level::ERR>(
1657 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001658 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001659 }
1660
1661 // The record.body is a struct SensorDataFullRecordBody
1662 // from sensorhandler.hpp in phosphor-ipmi-host.
1663 // The meaning of these bits appears to come from
1664 // table 43.1 of the IPMI spec.
1665 // The above 5 sensor attributes are stuffed in as follows:
1666 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1667 // Byte 22-24 are for other purposes
1668 // Byte 25 = MMMMMMMM = LSB of M
1669 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1670 // Byte 27 = BBBBBBBB = LSB of B
1671 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1672 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1673 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1674
1675 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1676 record.body.m_lsb = mValue & 0xFF;
1677
1678 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1679 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1680
1681 // move the smallest bit of the MSB into place (bit 9)
1682 // the MSbs are bits 7:8 in m_msb_and_tolerance
1683 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1684
1685 record.body.b_lsb = bValue & 0xFF;
1686
1687 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1688 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1689
1690 // move the smallest bit of the MSB into place (bit 9)
1691 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1692 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1693
1694 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1695 uint8_t rExpBits = rExp & 0x07;
1696
1697 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1698 uint8_t bExpBits = bExp & 0x07;
1699
1700 // move rExp and bExp into place
1701 record.body.r_b_exponents =
1702 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1703
1704 // Set the analog reading byte interpretation accordingly
1705 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1706
1707 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1708 // These seem redundant, but derivable from the above 5 attributes
1709 // Original comment said "todo fill out rest of units"
1710
1711 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001712 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001713 record.body.id_string_info = name.size();
1714 std::strncpy(record.body.id_string, name.c_str(),
1715 sizeof(record.body.id_string));
1716
Josh Lehana55c9532020-10-28 21:59:06 -07001717 // Remember the sensor name, as determined for this sensor number
1718 details::sdrStatsTable.updateName(sensornumber, name);
1719
Jie Yangf0a89942021-07-29 15:30:25 -07001720 bool sensorSettable = false;
1721 auto mutability =
1722 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1723 if (mutability != sensorMap.end())
1724 {
1725 sensorSettable =
1726 mappedVariant<bool>(mutability->second, "Mutable", false);
1727 }
1728 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1729
1730 // Grant write permission to sensors deemed externally settable
1731 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001732
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001733 IPMIThresholds thresholdData;
1734 try
1735 {
1736 thresholdData = getIPMIThresholds(sensorMap);
1737 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001738 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001739 {
1740 phosphor::logging::log<phosphor::logging::level::ERR>(
1741 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001742 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001743 }
1744
1745 if (thresholdData.criticalHigh)
1746 {
1747 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1748 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1749 IPMISensorEventEnableThresholds::criticalThreshold);
1750 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1752 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1753 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1754 record.body.discrete_reading_setting_mask[0] |=
1755 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1756 }
1757 if (thresholdData.warningHigh)
1758 {
1759 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1760 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1761 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1762 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1763 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1764 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1765 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1766 record.body.discrete_reading_setting_mask[0] |=
1767 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1768 }
1769 if (thresholdData.criticalLow)
1770 {
1771 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1772 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1773 IPMISensorEventEnableThresholds::criticalThreshold);
1774 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1775 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1776 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1777 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1778 record.body.discrete_reading_setting_mask[0] |=
1779 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1780 }
1781 if (thresholdData.warningLow)
1782 {
1783 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1784 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1785 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1786 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1787 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1788 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1789 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1790 record.body.discrete_reading_setting_mask[0] |=
1791 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1792 }
1793
1794 // everything that is readable is setable
1795 record.body.discrete_reading_setting_mask[1] =
1796 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001797 return true;
1798}
1799
Scron Chang2703b022021-07-06 15:47:45 +08001800#ifdef FEATURE_HYBRID_SENSORS
1801// Construct a type 1 SDR for discrete Sensor typed sensor.
1802void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1803 uint16_t recordID,
1804 ipmi::sensor::IdInfoMap::const_iterator sensor,
1805 get_sdr::SensorDataFullRecord& record)
1806{
1807 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1808
1809 record.body.entity_id = sensor->second.entityType;
1810 record.body.sensor_type = sensor->second.sensorType;
1811 record.body.event_reading_type = sensor->second.sensorReadingType;
1812 record.body.entity_instance = sensor->second.instance;
1813 if (ipmi::sensor::Mutability::Write ==
1814 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1815 {
1816 get_sdr::body::init_settable_state(true, &(record.body));
1817 }
1818
1819 auto id_string = sensor->second.sensorName;
1820
1821 if (id_string.empty())
1822 {
1823 id_string = sensor->second.sensorNameFunc(sensor->second);
1824 }
1825
1826 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1827 {
1828 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1829 &(record.body));
1830 }
1831 else
1832 {
1833 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1834 }
1835 std::strncpy(record.body.id_string, id_string.c_str(),
1836 get_sdr::body::get_id_strlen(&(record.body)));
1837}
1838#endif
1839
Hao Jiange39d4d82021-04-16 17:02:40 -07001840// Construct type 3 SDR header and key (for VR and other discrete sensors)
1841void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1842 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001843{
1844 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1845 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1846
1847 get_sdr::header::set_record_id(
1848 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1849
1850 record.header.sdr_version = ipmiSdrVersion;
1851 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1852 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1853 sizeof(get_sdr::SensorDataRecordHeader);
1854 record.key.owner_id = bmcI2CAddr;
1855 record.key.owner_lun = lun;
1856 record.key.sensor_number = sensornumber;
1857
1858 record.body.entity_id = 0x00;
1859 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001860}
Willy Tu61992ad2021-03-29 15:33:20 -07001861
Hao Jiange39d4d82021-04-16 17:02:40 -07001862// Construct a type 3 SDR for VR typed sensor(daemon).
1863bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1864 uint16_t recordID, const std::string& service,
1865 const std::string& path,
1866 get_sdr::SensorDataEventRecord& record)
1867{
1868 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1869 constructEventSdrHeaderKey(sensorNum, recordID, record);
1870
1871 DbusInterfaceMap sensorMap;
1872 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1873 {
1874 phosphor::logging::log<phosphor::logging::level::ERR>(
1875 "Failed to update sensor map for VR sensor",
1876 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1877 phosphor::logging::entry("PATH=%s", path.c_str()));
1878 return false;
1879 }
Willy Tu61992ad2021-03-29 15:33:20 -07001880 // follow the association chain to get the parent board's entityid and
1881 // entityInstance
1882 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1883 record.body.entity_instance);
1884
1885 // Sensor type is hardcoded as a module/board type instead of parsing from
1886 // sensor path. This is because VR control is allocated in an independent
1887 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1888 // types.
1889 static constexpr const uint8_t module_board_type = 0x15;
1890 record.body.sensor_type = module_board_type;
1891 record.body.event_reading_type = 0x00;
1892
1893 record.body.sensor_record_sharing_1 = 0x00;
1894 record.body.sensor_record_sharing_2 = 0x00;
1895
1896 // populate sensor name from path
1897 auto name = sensor::parseSdrIdFromPath(path);
1898 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1899 record.body.id_string_info = nameSize;
1900 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1901 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1902
1903 // Remember the sensor name, as determined for this sensor number
1904 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001905
1906 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001907}
1908
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001909static inline uint16_t getNumberOfSensors()
1910{
1911 return std::min(getSensorTree().size(), maxIPMISensors);
1912}
1913
Hao Jiange39d4d82021-04-16 17:02:40 -07001914static int
1915 getSensorDataRecord(ipmi::Context::ptr ctx,
1916 std::vector<uint8_t>& recordData, uint16_t recordID,
1917 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001918{
1919 size_t fruCount = 0;
1920 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1921 if (ret != ipmi::ccSuccess)
1922 {
1923 phosphor::logging::log<phosphor::logging::level::ERR>(
1924 "getSensorDataRecord: getFruSdrCount error");
1925 return GENERAL_ERROR;
1926 }
1927
Willy Tu38e7a2b2021-03-29 15:09:56 -07001928 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001929 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001930 if (recordID == lastRecordIndex)
1931 {
1932 recordID = lastRecord;
1933 }
1934 if (recordID > lastRecord)
1935 {
1936 phosphor::logging::log<phosphor::logging::level::ERR>(
1937 "getSensorDataRecord: recordID > lastRecord error");
1938 return GENERAL_ERROR;
1939 }
1940
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001941 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001942 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001943 size_t fruIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001944
1945 if (fruIndex >= fruCount)
1946 {
1947 // handle type 12 hardcoded records
1948 size_t type12Index = fruIndex - fruCount;
1949 if (type12Index >= ipmi::storage::type12Count)
1950 {
1951 phosphor::logging::log<phosphor::logging::level::ERR>(
1952 "getSensorDataRecord: type12Index error");
1953 return GENERAL_ERROR;
1954 }
1955 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1956 }
1957 else
1958 {
1959 // handle fru records
1960 get_sdr::SensorDataFruRecord data;
1961 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1962 if (ret != IPMI_CC_OK)
1963 {
1964 return GENERAL_ERROR;
1965 }
1966 data.header.record_id_msb = recordID >> 8;
1967 data.header.record_id_lsb = recordID & 0xFF;
1968 recordData.insert(recordData.end(), (uint8_t*)&data,
1969 ((uint8_t*)&data) + sizeof(data));
1970 }
1971
1972 return 0;
1973 }
1974
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001975 // Perform a incremental scan of the SDR Record ID's and translate the
1976 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1977 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1978 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1979 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07001980 std::string connection;
1981 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001982 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07001983 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001984 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07001985 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001986 // LUN 0 has one reserved sensor number. Compensate here by adding one
1987 // to the record ID
1988 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07001989 ctx->lun = 1;
1990 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001991 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07001992 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001993 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
1994 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
1995 // rules governing its use.
1996 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07001997 ctx->lun = 3;
1998 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001999
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002000 auto status =
2001 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2002 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07002003 if (status)
2004 {
2005 phosphor::logging::log<phosphor::logging::level::ERR>(
2006 "getSensorDataRecord: getSensorConnection error");
2007 return GENERAL_ERROR;
2008 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002009 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002010 // Return an error on LUN 2 assingments, and any sensor number beyond the
2011 // range of LUN 3
2012 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2013 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002014 {
2015 phosphor::logging::log<phosphor::logging::level::ERR>(
2016 "getSensorDataRecord: invalidSensorNumber");
2017 return GENERAL_ERROR;
2018 }
Johnathan Manteyce982772021-07-28 15:08:30 -07002019 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2020 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2021
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002022 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2023 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07002024 {
2025 phosphor::logging::log<phosphor::logging::level::ERR>(
2026 "getSensorDataRecord: sensor record mismatch");
2027 return GENERAL_ERROR;
2028 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002029
Willy Tu38e7a2b2021-03-29 15:09:56 -07002030 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002031 if (std::find(interfaces.begin(), interfaces.end(),
2032 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002033 {
2034 get_sdr::SensorDataFullRecord record = {0};
2035
Hao Jiange39d4d82021-04-16 17:02:40 -07002036 // If the request doesn't read SDR body, construct only header and key
2037 // part to avoid additional DBus transaction.
2038 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2039 {
2040 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2041 }
2042 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2043 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002044 {
2045 return GENERAL_ERROR;
2046 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002047
Willy Tu38e7a2b2021-03-29 15:09:56 -07002048 recordData.insert(recordData.end(), (uint8_t*)&record,
2049 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002050
2051 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002052 }
Willy Tu61992ad2021-03-29 15:33:20 -07002053
Scron Chang2703b022021-07-06 15:47:45 +08002054#ifdef FEATURE_HYBRID_SENSORS
2055 if (auto sensor = findStaticSensor(path);
2056 sensor != ipmi::sensor::sensors.end() &&
2057 getSensorEventTypeFromPath(path) !=
2058 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2059 {
2060 get_sdr::SensorDataFullRecord record = {0};
2061
2062 // If the request doesn't read SDR body, construct only header and key
2063 // part to avoid additional DBus transaction.
2064 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2065 {
2066 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2067 }
2068 else
2069 {
2070 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2071 }
2072
2073 recordData.insert(recordData.end(), (uint8_t*)&record,
2074 ((uint8_t*)&record) + sizeof(record));
2075
2076 return 0;
2077 }
2078#endif
2079
Willy Tu61992ad2021-03-29 15:33:20 -07002080 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002081 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2082 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002083 {
2084 get_sdr::SensorDataEventRecord record = {0};
2085
Hao Jiange39d4d82021-04-16 17:02:40 -07002086 // If the request doesn't read SDR body, construct only header and key
2087 // part to avoid additional DBus transaction.
2088 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2089 {
2090 constructEventSdrHeaderKey(sensorNum, recordID, record);
2091 }
2092 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2093 record))
2094 {
2095 return GENERAL_ERROR;
2096 }
Willy Tu61992ad2021-03-29 15:33:20 -07002097 recordData.insert(recordData.end(), (uint8_t*)&record,
2098 ((uint8_t*)&record) + sizeof(record));
2099 }
2100
Willy Tude54f482021-01-26 15:59:09 -08002101 return 0;
2102}
2103
2104/** @brief implements the get SDR Info command
2105 * @param count - Operation
2106 *
2107 * @returns IPMI completion code plus response data
2108 * - sdrCount - sensor/SDR count
2109 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2110 */
2111static ipmi::RspType<uint8_t, // respcount
2112 uint8_t, // dynamic population flags
2113 uint32_t // last time a sensor was added
2114 >
2115 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2116 std::optional<uint8_t> count)
2117{
2118 auto& sensorTree = getSensorTree();
2119 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002120 uint16_t recordID = 0;
2121 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002122 // Sensors are dynamically allocated, and there is at least one LUN
2123 uint8_t lunsAndDynamicPopulation = 0x80;
2124 constexpr uint8_t getSdrCount = 0x01;
2125 constexpr uint8_t getSensorCount = 0x00;
2126
2127 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2128 {
2129 return ipmi::responseResponseError();
2130 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002131 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002132 if (count.value_or(0) == getSdrCount)
2133 {
2134 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002135 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002136 {
2137 get_sdr::SensorDataRecordHeader* hdr =
2138 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002139 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002140 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2141 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002142 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002143 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002144 record.data());
2145 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002146 {
2147 sdrCount++;
2148 }
2149 }
2150 }
2151 }
2152 else if (count.value_or(0) == getSensorCount)
2153 {
2154 // Return the number of sensors attached to the LUN
2155 if ((ctx->lun == 0) && (numSensors > 0))
2156 {
2157 sdrCount =
2158 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2159 }
2160 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2161 {
2162 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2163 ? maxSensorsPerLUN
2164 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2165 }
2166 else if (ctx->lun == 3)
2167 {
2168 if (numSensors <= maxIPMISensors)
2169 {
2170 sdrCount =
2171 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2172 }
2173 else
2174 {
2175 // error
2176 throw std::out_of_range(
2177 "Maximum number of IPMI sensors exceeded.");
2178 }
2179 }
2180 }
2181 else
2182 {
2183 return ipmi::responseInvalidFieldRequest();
2184 }
2185
2186 // Get Sensor count. This returns the number of sensors
2187 if (numSensors > 0)
2188 {
2189 lunsAndDynamicPopulation |= 1;
2190 }
2191 if (numSensors > maxSensorsPerLUN)
2192 {
2193 lunsAndDynamicPopulation |= 2;
2194 }
2195 if (numSensors >= (maxSensorsPerLUN * 2))
2196 {
2197 lunsAndDynamicPopulation |= 8;
2198 }
2199 if (numSensors > maxIPMISensors)
2200 {
2201 // error
2202 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2203 }
2204
2205 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2206 sdrLastAdd);
2207}
2208
2209/* end sensor commands */
2210
2211/* storage commands */
2212
2213ipmi::RspType<uint8_t, // sdr version
2214 uint16_t, // record count
2215 uint16_t, // free space
2216 uint32_t, // most recent addition
2217 uint32_t, // most recent erase
2218 uint8_t // operationSupport
2219 >
2220 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2221{
2222 auto& sensorTree = getSensorTree();
2223 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002224 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002225 {
2226 return ipmi::responseResponseError();
2227 }
2228
2229 size_t fruCount = 0;
2230 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2231 if (ret != ipmi::ccSuccess)
2232 {
2233 return ipmi::response(ret);
2234 }
2235
2236 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002237 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002238
2239 uint8_t operationSupport = static_cast<uint8_t>(
2240 SdrRepositoryInfoOps::overflow); // write not supported
2241
2242 operationSupport |=
2243 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2244 operationSupport |= static_cast<uint8_t>(
2245 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2246 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2247 unspecifiedFreeSpace, sdrLastAdd,
2248 sdrLastRemove, operationSupport);
2249}
2250
2251/** @brief implements the get SDR allocation info command
2252 *
2253 * @returns IPMI completion code plus response data
2254 * - allocUnits - Number of possible allocation units
2255 * - allocUnitSize - Allocation unit size in bytes.
2256 * - allocUnitFree - Number of free allocation units
2257 * - allocUnitLargestFree - Largest free block in allocation units
2258 * - maxRecordSize - Maximum record size in allocation units.
2259 */
2260ipmi::RspType<uint16_t, // allocUnits
2261 uint16_t, // allocUnitSize
2262 uint16_t, // allocUnitFree
2263 uint16_t, // allocUnitLargestFree
2264 uint8_t // maxRecordSize
2265 >
2266 ipmiStorageGetSDRAllocationInfo()
2267{
2268 // 0000h unspecified number of alloc units
2269 constexpr uint16_t allocUnits = 0;
2270
2271 constexpr uint16_t allocUnitFree = 0;
2272 constexpr uint16_t allocUnitLargestFree = 0;
2273 // only allow one block at a time
2274 constexpr uint8_t maxRecordSize = 1;
2275
2276 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2277 allocUnitLargestFree, maxRecordSize);
2278}
2279
2280/** @brief implements the reserve SDR command
2281 * @returns IPMI completion code plus response data
2282 * - sdrReservationID
2283 */
2284ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2285{
2286 sdrReservationID++;
2287 if (sdrReservationID == 0)
2288 {
2289 sdrReservationID++;
2290 }
2291
2292 return ipmi::responseSuccess(sdrReservationID);
2293}
2294
2295ipmi::RspType<uint16_t, // next record ID
2296 std::vector<uint8_t> // payload
2297 >
2298 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2299 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2300{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002301 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002302 // reservation required for partial reads with non zero offset into
2303 // record
2304 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2305 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002306 phosphor::logging::log<phosphor::logging::level::ERR>(
2307 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002308 return ipmi::responseInvalidReservationId();
2309 }
Willy Tude54f482021-01-26 15:59:09 -08002310 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2311 if (ret != ipmi::ccSuccess)
2312 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002313 phosphor::logging::log<phosphor::logging::level::ERR>(
2314 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002315 return ipmi::response(ret);
2316 }
2317
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002318 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002319 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002320 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002321 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2322
2323 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002324 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002325 phosphor::logging::log<phosphor::logging::level::ERR>(
2326 "ipmiStorageGetSDR: getSensorSubtree error");
2327 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002328 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002329
2330 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002331 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002332 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002333 phosphor::logging::log<phosphor::logging::level::ERR>(
2334 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002335 return ipmi::responseInvalidFieldRequest();
2336 }
Willy Tude54f482021-01-26 15:59:09 -08002337 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002338 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002339 if (!hdr)
2340 {
2341 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002342 "ipmiStorageGetSDR: record header is null");
2343 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002344 }
2345
2346 size_t sdrLength =
2347 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2348 if (sdrLength < (offset + bytesToRead))
2349 {
2350 bytesToRead = sdrLength - offset;
2351 }
2352
2353 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2354 if (!respStart)
2355 {
2356 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002357 "ipmiStorageGetSDR: record is null");
2358 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002359 }
2360
2361 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002362
Willy Tude54f482021-01-26 15:59:09 -08002363 return ipmi::responseSuccess(nextRecordId, recordData);
2364}
2365/* end storage commands */
2366
2367void registerSensorFunctions()
2368{
2369 // <Platform Event>
2370 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2371 ipmi::sensor_event::cmdPlatformEvent,
2372 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2373
Willy Tudbafbce2021-03-29 00:37:05 -07002374 // <Set Sensor Reading and Event Status>
2375 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2376 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2377 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002378
Willy Tude54f482021-01-26 15:59:09 -08002379 // <Get Sensor Reading>
2380 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2381 ipmi::sensor_event::cmdGetSensorReading,
2382 ipmi::Privilege::User, ipmiSenGetSensorReading);
2383
2384 // <Get Sensor Threshold>
2385 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2386 ipmi::sensor_event::cmdGetSensorThreshold,
2387 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2388
2389 // <Set Sensor Threshold>
2390 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2391 ipmi::sensor_event::cmdSetSensorThreshold,
2392 ipmi::Privilege::Operator,
2393 ipmiSenSetSensorThresholds);
2394
2395 // <Get Sensor Event Enable>
2396 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2397 ipmi::sensor_event::cmdGetSensorEventEnable,
2398 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2399
2400 // <Get Sensor Event Status>
2401 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2402 ipmi::sensor_event::cmdGetSensorEventStatus,
2403 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2404
2405 // register all storage commands for both Sensor and Storage command
2406 // versions
2407
2408 // <Get SDR Repository Info>
2409 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2410 ipmi::storage::cmdGetSdrRepositoryInfo,
2411 ipmi::Privilege::User,
2412 ipmiStorageGetSDRRepositoryInfo);
2413
2414 // <Get Device SDR Info>
2415 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2416 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2417 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2418
2419 // <Get SDR Allocation Info>
2420 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2421 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2422 ipmi::Privilege::User,
2423 ipmiStorageGetSDRAllocationInfo);
2424
2425 // <Reserve SDR Repo>
2426 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2427 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2428 ipmi::Privilege::User, ipmiStorageReserveSDR);
2429
2430 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2431 ipmi::storage::cmdReserveSdrRepository,
2432 ipmi::Privilege::User, ipmiStorageReserveSDR);
2433
2434 // <Get Sdr>
2435 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2436 ipmi::sensor_event::cmdGetDeviceSdr,
2437 ipmi::Privilege::User, ipmiStorageGetSDR);
2438
2439 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2440 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2441 ipmiStorageGetSDR);
2442}
2443} // namespace ipmi