blob: 0bf639bdc074c30c1b3062f6c800fec56c9560f3 [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);
226 min = std::min(value, min);
227 }
228 if (upper != critical->second.end())
229 {
230 double value = std::visit(VariantToDoubleVisitor(), upper->second);
231 max = std::max(value, max);
232 }
233 }
234 if (warning != sensorMap.end())
235 {
236
237 auto lower = warning->second.find("WarningLow");
238 auto upper = warning->second.find("WarningHigh");
239 if (lower != warning->second.end())
240 {
241 double value = std::visit(VariantToDoubleVisitor(), lower->second);
242 min = std::min(value, min);
243 }
244 if (upper != warning->second.end())
245 {
246 double value = std::visit(VariantToDoubleVisitor(), upper->second);
247 max = std::max(value, max);
248 }
249 }
250}
251
252static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700253 std::string sensorPath, DbusInterfaceMap& sensorMap,
254 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800255{
Scron Chang2703b022021-07-06 15:47:45 +0800256#ifdef FEATURE_HYBRID_SENSORS
257 if (auto sensor = findStaticSensor(sensorPath);
258 sensor != ipmi::sensor::sensors.end() &&
259 getSensorEventTypeFromPath(sensorPath) !=
260 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
261 {
262 // If the incoming sensor is a discrete sensor, it might fail in
263 // getManagedObjects(), return true, and use its own getFunc to get
264 // value.
265 return true;
266 }
267#endif
268
Willy Tude54f482021-01-26 15:59:09 -0800269 static boost::container::flat_map<
270 std::string, std::chrono::time_point<std::chrono::steady_clock>>
271 updateTimeMap;
272
273 auto updateFind = updateTimeMap.find(sensorConnection);
274 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
275 if (updateFind != updateTimeMap.end())
276 {
277 lastUpdate = updateFind->second;
278 }
279
280 auto now = std::chrono::steady_clock::now();
281
282 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700283 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800284 {
Willy Tude54f482021-01-26 15:59:09 -0800285 ObjectValueTree managedObjects;
286 boost::system::error_code ec = getManagedObjects(
287 ctx, sensorConnection.c_str(), "/", managedObjects);
288 if (ec)
289 {
290 phosphor::logging::log<phosphor::logging::level::ERR>(
291 "GetMangagedObjects for getSensorMap failed",
292 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
293
294 return false;
295 }
296
297 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700298 // Update time after finish building the map which allow the
299 // data to be cached for updatePeriod plus the build time.
300 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800301 }
302 auto connection = SensorCache.find(sensorConnection);
303 if (connection == SensorCache.end())
304 {
305 return false;
306 }
307 auto path = connection->second.find(sensorPath);
308 if (path == connection->second.end())
309 {
310 return false;
311 }
312 sensorMap = path->second;
313
314 return true;
315}
316
Hao Jiangd2afd052020-12-10 15:09:32 -0800317namespace sensor
318{
Hao Jiangd48c9212021-02-03 15:45:06 -0800319// Read VR profiles from sensor(daemon) interface
320static std::optional<std::vector<std::string>>
321 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800322{
323 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800324 auto supportedProperty = object.find("Supported");
325 if (supportedProperty == object.end() ||
326 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800327 {
328 phosphor::logging::log<phosphor::logging::level::ERR>(
329 "Missing the required Supported and Selected properties");
330 return std::nullopt;
331 }
332
333 const auto profilesPtr =
334 std::get_if<std::vector<std::string>>(&supportedProperty->second);
335
336 if (profilesPtr == nullptr)
337 {
338 phosphor::logging::log<phosphor::logging::level::ERR>(
339 "property is not array of string");
340 return std::nullopt;
341 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800342 return *profilesPtr;
343}
344
345// Calculate VR Mode from input IPMI discrete event bytes
346static std::optional<std::string>
347 calculateVRMode(uint15_t assertOffset,
348 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
349{
350 // get VR mode profiles from Supported Interface
351 auto profiles = getSupportedVrProfiles(VRObject);
352 if (!profiles)
353 {
354 return std::nullopt;
355 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800356
357 // interpret IPMI cmd bits into profiles' index
358 long unsigned int index = 0;
359 // only one bit should be set and the highest bit should not be used.
360 if (assertOffset == 0 || assertOffset == (1u << 15) ||
361 (assertOffset & (assertOffset - 1)))
362 {
363 phosphor::logging::log<phosphor::logging::level::ERR>(
364 "IPMI cmd format incorrect",
365
366 phosphor::logging::entry("BYTES=%#02x",
367 static_cast<uint16_t>(assertOffset)));
368 return std::nullopt;
369 }
370
371 while (assertOffset != 1)
372 {
373 assertOffset >>= 1;
374 index++;
375 }
376
Hao Jiangd48c9212021-02-03 15:45:06 -0800377 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800378 {
379 phosphor::logging::log<phosphor::logging::level::ERR>(
380 "profile index out of boundary");
381 return std::nullopt;
382 }
383
Hao Jiangd48c9212021-02-03 15:45:06 -0800384 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800385}
386
387// Calculate sensor value from IPMI reading byte
388static std::optional<double>
389 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
390 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
391{
392 if (valueObject.find("Value") == valueObject.end())
393 {
394 phosphor::logging::log<phosphor::logging::level::ERR>(
395 "Missing the required Value property");
396 return std::nullopt;
397 }
398
399 double max = 0;
400 double min = 0;
401 getSensorMaxMin(sensorMap, max, min);
402
403 int16_t mValue = 0;
404 int16_t bValue = 0;
405 int8_t rExp = 0;
406 int8_t bExp = 0;
407 bool bSigned = false;
408
409 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
410 {
411 return std::nullopt;
412 }
413
414 double value = bSigned ? ((int8_t)reading) : reading;
415
416 value *= ((double)mValue);
417 value += ((double)bValue) * std::pow(10.0, bExp);
418 value *= std::pow(10.0, rExp);
419
420 return value;
421}
422
Willy Tu38e7a2b2021-03-29 15:09:56 -0700423// Extract file name from sensor path as the sensors SDR ID. Simplify the name
424// if it is too long.
425std::string parseSdrIdFromPath(const std::string& path)
426{
427 std::string name;
428 size_t nameStart = path.rfind("/");
429 if (nameStart != std::string::npos)
430 {
431 name = path.substr(nameStart + 1, std::string::npos - nameStart);
432 }
433
Willy Tu38e7a2b2021-03-29 15:09:56 -0700434 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
435 {
436 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800437 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700438 {
JeffLind950f412021-10-20 18:49:34 +0800439 if (boost::ends_with(name, suffix))
440 {
441 boost::replace_all(name, suffix, "");
442 break;
443 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700444 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700445 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
446 }
JeffLind950f412021-10-20 18:49:34 +0800447 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700448 return name;
449}
450
Hao Jiangd48c9212021-02-03 15:45:06 -0800451bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
452 const std::string& path,
453 const ipmi::DbusInterfaceMap::mapped_type& object,
454 std::bitset<16>& assertions)
455{
456 auto profiles = sensor::getSupportedVrProfiles(object);
457 if (!profiles)
458 {
459 return false;
460 }
461 ipmi::Value modeVariant;
462
463 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
464 "Selected", modeVariant);
465 if (ec)
466 {
467 log<level::ERR>("Failed to get property",
468 entry("PROPERTY=%s", "Selected"),
469 entry("PATH=%s", path.c_str()),
470 entry("INTERFACE=%s", sensor::sensorInterface),
471 entry("WHAT=%s", ec.message().c_str()));
472 return false;
473 }
474
475 auto mode = std::get_if<std::string>(&modeVariant);
476 if (mode == nullptr)
477 {
478 log<level::ERR>("property is not a string",
479 entry("PROPERTY=%s", "Selected"),
480 entry("PATH=%s", path.c_str()),
481 entry("INTERFACE=%s", sensor::sensorInterface));
482 return false;
483 }
484
485 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
486 if (itr == profiles->end())
487 {
488 using namespace phosphor::logging;
489 log<level::ERR>("VR mode doesn't match any of its profiles",
490 entry("PATH=%s", path.c_str()));
491 return false;
492 }
493 std::size_t index =
494 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
495
496 // map index to reponse event assertion bit.
497 if (index < 8)
498 {
499 assertions.set(1u << index);
500 }
501 else if (index < 15)
502 {
503 assertions.set(1u << (index - 8));
504 }
505 else
506 {
507 log<level::ERR>("VR profile index reaches max assertion bit",
508 entry("PATH=%s", path.c_str()),
509 entry("INDEX=%uz", index));
510 return false;
511 }
512 if constexpr (debug)
513 {
514 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
515 << " mode is: [" << index << "] " << *mode << std::endl;
516 }
517 return true;
518}
Hao Jiangd2afd052020-12-10 15:09:32 -0800519} // namespace sensor
520
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000521ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
522 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800523{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000524 constexpr const uint8_t validEnvmRev = 0x04;
525 constexpr const uint8_t lastSensorType = 0x2C;
526 constexpr const uint8_t oemReserved = 0xC0;
527
528 uint8_t generatorID = 0;
529 uint8_t evmRev = 0;
530 uint8_t sensorType = 0;
531 uint8_t sensorNum = 0;
532 uint8_t eventType = 0;
533 uint8_t eventData1 = 0;
534 std::optional<uint8_t> eventData2 = 0;
535 std::optional<uint8_t> eventData3 = 0;
536 ipmi::ChannelInfo chInfo;
537
538 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
539 {
540 phosphor::logging::log<phosphor::logging::level::ERR>(
541 "Failed to get Channel Info",
542 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
543 return ipmi::responseUnspecifiedError();
544 }
545
546 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
547 ipmi::EChannelMediumType::systemInterface)
548 {
549
550 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
551 eventData1, eventData2, eventData3);
552 }
553 else
554 {
555
556 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
557 eventData2, eventData3);
558 generatorID = ctx->rqSA;
559 }
560
561 if (!p.fullyUnpacked())
562 {
563 return ipmi::responseReqDataLenInvalid();
564 }
565
566 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
567 if (evmRev != validEnvmRev)
568 {
569 return ipmi::responseInvalidFieldRequest();
570 }
571 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
572 {
573 return ipmi::responseInvalidFieldRequest();
574 }
575
Willy Tude54f482021-01-26 15:59:09 -0800576 return ipmi::responseSuccess();
577}
578
Willy Tudbafbce2021-03-29 00:37:05 -0700579ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
580 uint8_t sensorNumber, uint8_t operation,
581 uint8_t reading, uint15_t assertOffset,
582 bool resvd1, uint15_t deassertOffset,
583 bool resvd2, uint8_t eventData1,
584 uint8_t eventData2, uint8_t eventData3)
585{
586 std::string connection;
587 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700588 std::vector<std::string> interfaces;
589
590 ipmi::Cc status =
591 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700592 if (status)
593 {
594 return ipmi::response(status);
595 }
596
Hao Jiangd2afd052020-12-10 15:09:32 -0800597 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700598 if (std::find(interfaces.begin(), interfaces.end(),
599 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700600 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700601 DbusInterfaceMap sensorMap;
602 if (!getSensorMap(ctx, connection, path, sensorMap))
603 {
604 return ipmi::responseResponseError();
605 }
606 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800607 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700608 {
609 return ipmi::responseResponseError();
610 }
611
Jie Yangf0a89942021-07-29 15:30:25 -0700612 // Only allow external SetSensor if write permission granted
613 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
614 {
615 return ipmi::responseResponseError();
616 }
617
Hao Jiangd2afd052020-12-10 15:09:32 -0800618 auto value =
619 sensor::calculateValue(reading, sensorMap, sensorObject->second);
620 if (!value)
621 {
622 return ipmi::responseResponseError();
623 }
624
625 if constexpr (debug)
626 {
627 phosphor::logging::log<phosphor::logging::level::INFO>(
628 "IPMI SET_SENSOR",
629 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
630 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
631 phosphor::logging::entry("VALUE=%f", *value));
632 }
633
634 boost::system::error_code ec =
635 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
636 "Value", ipmi::Value(*value));
637
638 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500639 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800640 // callback functions for now (e.g. ipmiSetSensorReading).
641 if (ec)
642 {
643 using namespace phosphor::logging;
644 log<level::ERR>("Failed to set property",
645 entry("PROPERTY=%s", "Value"),
646 entry("PATH=%s", path.c_str()),
647 entry("INTERFACE=%s", sensor::sensorInterface),
648 entry("WHAT=%s", ec.message().c_str()));
649 return ipmi::responseResponseError();
650 }
651 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700652 }
653
Hao Jiange39d4d82021-04-16 17:02:40 -0700654 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
655 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700656 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700657 DbusInterfaceMap sensorMap;
658 if (!getSensorMap(ctx, connection, path, sensorMap))
659 {
660 return ipmi::responseResponseError();
661 }
662 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800663 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700664 {
665 return ipmi::responseResponseError();
666 }
667
Hao Jiangd2afd052020-12-10 15:09:32 -0800668 // VR sensors are treated as a special case and we will not check the
669 // write permission for VR sensors, since they always deemed writable
670 // and permission table are not applied to VR sensors.
671 auto vrMode =
672 sensor::calculateVRMode(assertOffset, sensorObject->second);
673 if (!vrMode)
674 {
675 return ipmi::responseResponseError();
676 }
677 boost::system::error_code ec = setDbusProperty(
678 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
679 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500680 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800681 // callback functions for now (e.g. ipmiSetSensorReading).
682 if (ec)
683 {
684 using namespace phosphor::logging;
685 log<level::ERR>("Failed to set property",
686 entry("PROPERTY=%s", "Selected"),
687 entry("PATH=%s", path.c_str()),
688 entry("INTERFACE=%s", sensor::sensorInterface),
689 entry("WHAT=%s", ec.message().c_str()));
690 return ipmi::responseResponseError();
691 }
692 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700693 }
694
Hao Jiangd2afd052020-12-10 15:09:32 -0800695 phosphor::logging::log<phosphor::logging::level::ERR>(
696 "unknown sensor type",
697 phosphor::logging::entry("PATH=%s", path.c_str()));
698 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700699}
700
Willy Tude54f482021-01-26 15:59:09 -0800701ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
702 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
703{
704 std::string connection;
705 std::string path;
706
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000707 if (sensnum == reservedSensorNumber)
708 {
709 return ipmi::responseInvalidFieldRequest();
710 }
711
Willy Tude54f482021-01-26 15:59:09 -0800712 auto status = getSensorConnection(ctx, sensnum, connection, path);
713 if (status)
714 {
715 return ipmi::response(status);
716 }
717
Scron Chang2703b022021-07-06 15:47:45 +0800718#ifdef FEATURE_HYBRID_SENSORS
719 if (auto sensor = findStaticSensor(path);
720 sensor != ipmi::sensor::sensors.end() &&
721 getSensorEventTypeFromPath(path) !=
722 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
723 {
724 if (ipmi::sensor::Mutability::Read !=
725 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
726 {
727 return ipmi::responseIllegalCommand();
728 }
729
730 uint8_t operation;
731 try
732 {
733 ipmi::sensor::GetSensorResponse getResponse =
734 sensor->second.getFunc(sensor->second);
735
736 if (getResponse.readingOrStateUnavailable)
737 {
738 operation |= static_cast<uint8_t>(
739 IPMISensorReadingByte2::readingStateUnavailable);
740 }
741 if (getResponse.scanningEnabled)
742 {
743 operation |= static_cast<uint8_t>(
744 IPMISensorReadingByte2::sensorScanningEnable);
745 }
746 if (getResponse.allEventMessagesEnabled)
747 {
748 operation |= static_cast<uint8_t>(
749 IPMISensorReadingByte2::eventMessagesEnable);
750 }
751 return ipmi::responseSuccess(
752 getResponse.reading, operation,
753 getResponse.thresholdLevelsStates,
754 getResponse.discreteReadingSensorStates);
755 }
756 catch (const std::exception& e)
757 {
758 operation |= static_cast<uint8_t>(
759 IPMISensorReadingByte2::readingStateUnavailable);
760 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
761 }
762 }
763#endif
764
Willy Tude54f482021-01-26 15:59:09 -0800765 DbusInterfaceMap sensorMap;
766 if (!getSensorMap(ctx, connection, path, sensorMap))
767 {
768 return ipmi::responseResponseError();
769 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800770 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800771
772 if (sensorObject == sensorMap.end() ||
773 sensorObject->second.find("Value") == sensorObject->second.end())
774 {
775 return ipmi::responseResponseError();
776 }
777 auto& valueVariant = sensorObject->second["Value"];
778 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
779
780 double max = 0;
781 double min = 0;
782 getSensorMaxMin(sensorMap, max, min);
783
784 int16_t mValue = 0;
785 int16_t bValue = 0;
786 int8_t rExp = 0;
787 int8_t bExp = 0;
788 bool bSigned = false;
789
790 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
791 {
792 return ipmi::responseResponseError();
793 }
794
795 uint8_t value =
796 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
797 uint8_t operation =
798 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
799 operation |=
800 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
801 bool notReading = std::isnan(reading);
802
803 if (!notReading)
804 {
805 auto availableObject =
806 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
807 if (availableObject != sensorMap.end())
808 {
809 auto findAvailable = availableObject->second.find("Available");
810 if (findAvailable != availableObject->second.end())
811 {
812 bool* available = std::get_if<bool>(&(findAvailable->second));
813 if (available && !(*available))
814 {
815 notReading = true;
816 }
817 }
818 }
819 }
820
821 if (notReading)
822 {
823 operation |= static_cast<uint8_t>(
824 IPMISensorReadingByte2::readingStateUnavailable);
825 }
826
Josh Lehana55c9532020-10-28 21:59:06 -0700827 if constexpr (details::enableInstrumentation)
828 {
829 int byteValue;
830 if (bSigned)
831 {
832 byteValue = static_cast<int>(static_cast<int8_t>(value));
833 }
834 else
835 {
836 byteValue = static_cast<int>(static_cast<uint8_t>(value));
837 }
838
839 // Keep stats on the reading just obtained, even if it is "NaN"
840 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
841 {
842 // This is the first reading, show the coefficients
843 double step = (max - min) / 255.0;
844 std::cerr << "IPMI sensor "
845 << details::sdrStatsTable.getName(sensnum)
846 << ": Range min=" << min << " max=" << max
847 << ", step=" << step
848 << ", Coefficients mValue=" << static_cast<int>(mValue)
849 << " rExp=" << static_cast<int>(rExp)
850 << " bValue=" << static_cast<int>(bValue)
851 << " bExp=" << static_cast<int>(bExp)
852 << " bSigned=" << static_cast<int>(bSigned) << "\n";
853 }
854 }
855
Willy Tude54f482021-01-26 15:59:09 -0800856 uint8_t thresholds = 0;
857
858 auto warningObject =
859 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
860 if (warningObject != sensorMap.end())
861 {
862 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
863 auto alarmLow = warningObject->second.find("WarningAlarmLow");
864 if (alarmHigh != warningObject->second.end())
865 {
866 if (std::get<bool>(alarmHigh->second))
867 {
868 thresholds |= static_cast<uint8_t>(
869 IPMISensorReadingByte3::upperNonCritical);
870 }
871 }
872 if (alarmLow != warningObject->second.end())
873 {
874 if (std::get<bool>(alarmLow->second))
875 {
876 thresholds |= static_cast<uint8_t>(
877 IPMISensorReadingByte3::lowerNonCritical);
878 }
879 }
880 }
881
882 auto criticalObject =
883 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
884 if (criticalObject != sensorMap.end())
885 {
886 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
887 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
888 if (alarmHigh != criticalObject->second.end())
889 {
890 if (std::get<bool>(alarmHigh->second))
891 {
892 thresholds |=
893 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
894 }
895 }
896 if (alarmLow != criticalObject->second.end())
897 {
898 if (std::get<bool>(alarmLow->second))
899 {
900 thresholds |=
901 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
902 }
903 }
904 }
905
906 // no discrete as of today so optional byte is never returned
907 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
908}
909
910/** @brief implements the Set Sensor threshold command
911 * @param sensorNumber - sensor number
912 * @param lowerNonCriticalThreshMask
913 * @param lowerCriticalThreshMask
914 * @param lowerNonRecovThreshMask
915 * @param upperNonCriticalThreshMask
916 * @param upperCriticalThreshMask
917 * @param upperNonRecovThreshMask
918 * @param reserved
919 * @param lowerNonCritical - lower non-critical threshold
920 * @param lowerCritical - Lower critical threshold
921 * @param lowerNonRecoverable - Lower non recovarable threshold
922 * @param upperNonCritical - Upper non-critical threshold
923 * @param upperCritical - Upper critical
924 * @param upperNonRecoverable - Upper Non-recoverable
925 *
926 * @returns IPMI completion code
927 */
928ipmi::RspType<> ipmiSenSetSensorThresholds(
929 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
930 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
931 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
932 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
933 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
934 uint8_t upperNonCritical, uint8_t upperCritical,
935 uint8_t upperNonRecoverable)
936{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000937 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800938 {
939 return ipmi::responseInvalidFieldRequest();
940 }
941
942 // lower nc and upper nc not suppported on any sensor
943 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
944 {
945 return ipmi::responseInvalidFieldRequest();
946 }
947
948 // if none of the threshold mask are set, nothing to do
949 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
950 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
951 upperCriticalThreshMask | upperNonRecovThreshMask))
952 {
953 return ipmi::responseSuccess();
954 }
955
956 std::string connection;
957 std::string path;
958
959 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
960 if (status)
961 {
962 return ipmi::response(status);
963 }
964 DbusInterfaceMap sensorMap;
965 if (!getSensorMap(ctx, connection, path, sensorMap))
966 {
967 return ipmi::responseResponseError();
968 }
969
970 double max = 0;
971 double min = 0;
972 getSensorMaxMin(sensorMap, max, min);
973
974 int16_t mValue = 0;
975 int16_t bValue = 0;
976 int8_t rExp = 0;
977 int8_t bExp = 0;
978 bool bSigned = false;
979
980 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
981 {
982 return ipmi::responseResponseError();
983 }
984
985 // store a vector of property name, value to set, and interface
986 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
987
988 // define the indexes of the tuple
989 constexpr uint8_t propertyName = 0;
990 constexpr uint8_t thresholdValue = 1;
991 constexpr uint8_t interface = 2;
992 // verifiy all needed fields are present
993 if (lowerCriticalThreshMask || upperCriticalThreshMask)
994 {
995 auto findThreshold =
996 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
997 if (findThreshold == sensorMap.end())
998 {
999 return ipmi::responseInvalidFieldRequest();
1000 }
1001 if (lowerCriticalThreshMask)
1002 {
1003 auto findLower = findThreshold->second.find("CriticalLow");
1004 if (findLower == findThreshold->second.end())
1005 {
1006 return ipmi::responseInvalidFieldRequest();
1007 }
1008 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1009 findThreshold->first);
1010 }
1011 if (upperCriticalThreshMask)
1012 {
1013 auto findUpper = findThreshold->second.find("CriticalHigh");
1014 if (findUpper == findThreshold->second.end())
1015 {
1016 return ipmi::responseInvalidFieldRequest();
1017 }
1018 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1019 findThreshold->first);
1020 }
1021 }
1022 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1023 {
1024 auto findThreshold =
1025 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1026 if (findThreshold == sensorMap.end())
1027 {
1028 return ipmi::responseInvalidFieldRequest();
1029 }
1030 if (lowerNonCriticalThreshMask)
1031 {
1032 auto findLower = findThreshold->second.find("WarningLow");
1033 if (findLower == findThreshold->second.end())
1034 {
1035 return ipmi::responseInvalidFieldRequest();
1036 }
1037 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1038 findThreshold->first);
1039 }
1040 if (upperNonCriticalThreshMask)
1041 {
1042 auto findUpper = findThreshold->second.find("WarningHigh");
1043 if (findUpper == findThreshold->second.end())
1044 {
1045 return ipmi::responseInvalidFieldRequest();
1046 }
1047 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1048 findThreshold->first);
1049 }
1050 }
1051 for (const auto& property : thresholdsToSet)
1052 {
1053 // from section 36.3 in the IPMI Spec, assume all linear
1054 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1055 (bValue * std::pow(10.0, bExp))) *
1056 std::pow(10.0, rExp);
1057 setDbusProperty(
1058 *getSdBus(), connection, path, std::get<interface>(property),
1059 std::get<propertyName>(property), ipmi::Value(valueToSet));
1060 }
1061 return ipmi::responseSuccess();
1062}
1063
1064IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1065{
1066 IPMIThresholds resp;
1067 auto warningInterface =
1068 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1069 auto criticalInterface =
1070 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1071
1072 if ((warningInterface != sensorMap.end()) ||
1073 (criticalInterface != sensorMap.end()))
1074 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001075 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001076
1077 if (sensorPair == sensorMap.end())
1078 {
1079 // should not have been able to find a sensor not implementing
1080 // the sensor object
1081 throw std::runtime_error("Invalid sensor map");
1082 }
1083
1084 double max = 0;
1085 double min = 0;
1086 getSensorMaxMin(sensorMap, max, min);
1087
1088 int16_t mValue = 0;
1089 int16_t bValue = 0;
1090 int8_t rExp = 0;
1091 int8_t bExp = 0;
1092 bool bSigned = false;
1093
1094 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1095 {
1096 throw std::runtime_error("Invalid sensor atrributes");
1097 }
1098 if (warningInterface != sensorMap.end())
1099 {
1100 auto& warningMap = warningInterface->second;
1101
1102 auto warningHigh = warningMap.find("WarningHigh");
1103 auto warningLow = warningMap.find("WarningLow");
1104
1105 if (warningHigh != warningMap.end())
1106 {
1107
1108 double value =
1109 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1110 resp.warningHigh = scaleIPMIValueFromDouble(
1111 value, mValue, rExp, bValue, bExp, bSigned);
1112 }
1113 if (warningLow != warningMap.end())
1114 {
1115 double value =
1116 std::visit(VariantToDoubleVisitor(), warningLow->second);
1117 resp.warningLow = scaleIPMIValueFromDouble(
1118 value, mValue, rExp, bValue, bExp, bSigned);
1119 }
1120 }
1121 if (criticalInterface != sensorMap.end())
1122 {
1123 auto& criticalMap = criticalInterface->second;
1124
1125 auto criticalHigh = criticalMap.find("CriticalHigh");
1126 auto criticalLow = criticalMap.find("CriticalLow");
1127
1128 if (criticalHigh != criticalMap.end())
1129 {
1130 double value =
1131 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1132 resp.criticalHigh = scaleIPMIValueFromDouble(
1133 value, mValue, rExp, bValue, bExp, bSigned);
1134 }
1135 if (criticalLow != criticalMap.end())
1136 {
1137 double value =
1138 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1139 resp.criticalLow = scaleIPMIValueFromDouble(
1140 value, mValue, rExp, bValue, bExp, bSigned);
1141 }
1142 }
1143 }
1144 return resp;
1145}
1146
1147ipmi::RspType<uint8_t, // readable
1148 uint8_t, // lowerNCrit
1149 uint8_t, // lowerCrit
1150 uint8_t, // lowerNrecoverable
1151 uint8_t, // upperNC
1152 uint8_t, // upperCrit
1153 uint8_t> // upperNRecoverable
1154 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1155{
1156 std::string connection;
1157 std::string path;
1158
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001159 if (sensorNumber == reservedSensorNumber)
1160 {
1161 return ipmi::responseInvalidFieldRequest();
1162 }
1163
Willy Tude54f482021-01-26 15:59:09 -08001164 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1165 if (status)
1166 {
1167 return ipmi::response(status);
1168 }
1169
1170 DbusInterfaceMap sensorMap;
1171 if (!getSensorMap(ctx, connection, path, sensorMap))
1172 {
1173 return ipmi::responseResponseError();
1174 }
1175
1176 IPMIThresholds thresholdData;
1177 try
1178 {
1179 thresholdData = getIPMIThresholds(sensorMap);
1180 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001181 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001182 {
1183 return ipmi::responseResponseError();
1184 }
1185
1186 uint8_t readable = 0;
1187 uint8_t lowerNC = 0;
1188 uint8_t lowerCritical = 0;
1189 uint8_t lowerNonRecoverable = 0;
1190 uint8_t upperNC = 0;
1191 uint8_t upperCritical = 0;
1192 uint8_t upperNonRecoverable = 0;
1193
1194 if (thresholdData.warningHigh)
1195 {
1196 readable |=
1197 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1198 upperNC = *thresholdData.warningHigh;
1199 }
1200 if (thresholdData.warningLow)
1201 {
1202 readable |=
1203 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1204 lowerNC = *thresholdData.warningLow;
1205 }
1206
1207 if (thresholdData.criticalHigh)
1208 {
1209 readable |=
1210 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1211 upperCritical = *thresholdData.criticalHigh;
1212 }
1213 if (thresholdData.criticalLow)
1214 {
1215 readable |=
1216 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1217 lowerCritical = *thresholdData.criticalLow;
1218 }
1219
1220 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1221 lowerNonRecoverable, upperNC, upperCritical,
1222 upperNonRecoverable);
1223}
1224
1225/** @brief implements the get Sensor event enable command
1226 * @param sensorNumber - sensor number
1227 *
1228 * @returns IPMI completion code plus response data
1229 * - enabled - Sensor Event messages
1230 * - assertionEnabledLsb - Assertion event messages
1231 * - assertionEnabledMsb - Assertion event messages
1232 * - deassertionEnabledLsb - Deassertion event messages
1233 * - deassertionEnabledMsb - Deassertion event messages
1234 */
1235
1236ipmi::RspType<uint8_t, // enabled
1237 uint8_t, // assertionEnabledLsb
1238 uint8_t, // assertionEnabledMsb
1239 uint8_t, // deassertionEnabledLsb
1240 uint8_t> // deassertionEnabledMsb
1241 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1242{
1243 std::string connection;
1244 std::string path;
1245
1246 uint8_t enabled = 0;
1247 uint8_t assertionEnabledLsb = 0;
1248 uint8_t assertionEnabledMsb = 0;
1249 uint8_t deassertionEnabledLsb = 0;
1250 uint8_t deassertionEnabledMsb = 0;
1251
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001252 if (sensorNum == reservedSensorNumber)
1253 {
1254 return ipmi::responseInvalidFieldRequest();
1255 }
1256
Willy Tude54f482021-01-26 15:59:09 -08001257 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1258 if (status)
1259 {
1260 return ipmi::response(status);
1261 }
1262
Scron Chang2703b022021-07-06 15:47:45 +08001263#ifdef FEATURE_HYBRID_SENSORS
1264 if (auto sensor = findStaticSensor(path);
1265 sensor != ipmi::sensor::sensors.end() &&
1266 getSensorEventTypeFromPath(path) !=
1267 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1268 {
1269 enabled = static_cast<uint8_t>(
1270 IPMISensorEventEnableByte2::sensorScanningEnable);
1271 uint16_t assertionEnabled = 0;
1272 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1273 ->second.begin()
1274 ->second.second)
1275 {
1276 assertionEnabled |= (1 << offsetValMap.first);
1277 }
1278 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1279 assertionEnabledMsb =
1280 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1281
1282 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1283 assertionEnabledMsb, deassertionEnabledLsb,
1284 deassertionEnabledMsb);
1285 }
1286#endif
1287
Willy Tude54f482021-01-26 15:59:09 -08001288 DbusInterfaceMap sensorMap;
1289 if (!getSensorMap(ctx, connection, path, sensorMap))
1290 {
1291 return ipmi::responseResponseError();
1292 }
1293
1294 auto warningInterface =
1295 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1296 auto criticalInterface =
1297 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1298 if ((warningInterface != sensorMap.end()) ||
1299 (criticalInterface != sensorMap.end()))
1300 {
1301 enabled = static_cast<uint8_t>(
1302 IPMISensorEventEnableByte2::sensorScanningEnable);
1303 if (warningInterface != sensorMap.end())
1304 {
1305 auto& warningMap = warningInterface->second;
1306
1307 auto warningHigh = warningMap.find("WarningHigh");
1308 auto warningLow = warningMap.find("WarningLow");
1309 if (warningHigh != warningMap.end())
1310 {
1311 assertionEnabledLsb |= static_cast<uint8_t>(
1312 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1313 deassertionEnabledLsb |= static_cast<uint8_t>(
1314 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1315 }
1316 if (warningLow != warningMap.end())
1317 {
1318 assertionEnabledLsb |= static_cast<uint8_t>(
1319 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1320 deassertionEnabledLsb |= static_cast<uint8_t>(
1321 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1322 }
1323 }
1324 if (criticalInterface != sensorMap.end())
1325 {
1326 auto& criticalMap = criticalInterface->second;
1327
1328 auto criticalHigh = criticalMap.find("CriticalHigh");
1329 auto criticalLow = criticalMap.find("CriticalLow");
1330
1331 if (criticalHigh != criticalMap.end())
1332 {
1333 assertionEnabledMsb |= static_cast<uint8_t>(
1334 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1335 deassertionEnabledMsb |= static_cast<uint8_t>(
1336 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1337 }
1338 if (criticalLow != criticalMap.end())
1339 {
1340 assertionEnabledLsb |= static_cast<uint8_t>(
1341 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1342 deassertionEnabledLsb |= static_cast<uint8_t>(
1343 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1344 }
1345 }
1346 }
1347
1348 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1349 assertionEnabledMsb, deassertionEnabledLsb,
1350 deassertionEnabledMsb);
1351}
1352
1353/** @brief implements the get Sensor event status command
1354 * @param sensorNumber - sensor number, FFh = reserved
1355 *
1356 * @returns IPMI completion code plus response data
1357 * - sensorEventStatus - Sensor Event messages state
1358 * - assertions - Assertion event messages
1359 * - deassertions - Deassertion event messages
1360 */
1361ipmi::RspType<uint8_t, // sensorEventStatus
1362 std::bitset<16>, // assertions
1363 std::bitset<16> // deassertion
1364 >
1365 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1366{
1367 if (sensorNum == reservedSensorNumber)
1368 {
1369 return ipmi::responseInvalidFieldRequest();
1370 }
1371
1372 std::string connection;
1373 std::string path;
1374 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1375 if (status)
1376 {
1377 phosphor::logging::log<phosphor::logging::level::ERR>(
1378 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1379 phosphor::logging::entry("SENSOR=%d", sensorNum));
1380 return ipmi::response(status);
1381 }
1382
Scron Chang2703b022021-07-06 15:47:45 +08001383#ifdef FEATURE_HYBRID_SENSORS
1384 if (auto sensor = findStaticSensor(path);
1385 sensor != ipmi::sensor::sensors.end() &&
1386 getSensorEventTypeFromPath(path) !=
1387 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1388 {
1389 auto response = ipmi::sensor::get::mapDbusToAssertion(
1390 sensor->second, path, sensor->second.sensorInterface);
1391 std::bitset<16> assertions;
1392 // deassertions are not used.
1393 std::bitset<16> deassertions = 0;
1394 uint8_t sensorEventStatus;
1395 if (response.readingOrStateUnavailable)
1396 {
1397 sensorEventStatus |= static_cast<uint8_t>(
1398 IPMISensorReadingByte2::readingStateUnavailable);
1399 }
1400 if (response.scanningEnabled)
1401 {
1402 sensorEventStatus |= static_cast<uint8_t>(
1403 IPMISensorReadingByte2::sensorScanningEnable);
1404 }
1405 if (response.allEventMessagesEnabled)
1406 {
1407 sensorEventStatus |= static_cast<uint8_t>(
1408 IPMISensorReadingByte2::eventMessagesEnable);
1409 }
1410 assertions |= response.discreteReadingSensorStates << 8;
1411 assertions |= response.thresholdLevelsStates;
1412 return ipmi::responseSuccess(sensorEventStatus, assertions,
1413 deassertions);
1414 }
1415#endif
1416
Willy Tude54f482021-01-26 15:59:09 -08001417 DbusInterfaceMap sensorMap;
1418 if (!getSensorMap(ctx, connection, path, sensorMap))
1419 {
1420 phosphor::logging::log<phosphor::logging::level::ERR>(
1421 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1422 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1423 return ipmi::responseResponseError();
1424 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001425
1426 uint8_t sensorEventStatus =
1427 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1428 std::bitset<16> assertions = 0;
1429 std::bitset<16> deassertions = 0;
1430
1431 // handle VR typed sensor
1432 auto vrInterface = sensorMap.find(sensor::vrInterface);
1433 if (vrInterface != sensorMap.end())
1434 {
1435 if (!sensor::getVrEventStatus(ctx, connection, path,
1436 vrInterface->second, assertions))
1437 {
1438 return ipmi::responseResponseError();
1439 }
1440
1441 // both Event Message and Sensor Scanning are disable for VR.
1442 sensorEventStatus = 0;
1443 return ipmi::responseSuccess(sensorEventStatus, assertions,
1444 deassertions);
1445 }
1446
Willy Tude54f482021-01-26 15:59:09 -08001447 auto warningInterface =
1448 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1449 auto criticalInterface =
1450 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1451
Willy Tude54f482021-01-26 15:59:09 -08001452 std::optional<bool> criticalDeassertHigh =
1453 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1454 std::optional<bool> criticalDeassertLow =
1455 thresholdDeassertMap[path]["CriticalAlarmLow"];
1456 std::optional<bool> warningDeassertHigh =
1457 thresholdDeassertMap[path]["WarningAlarmHigh"];
1458 std::optional<bool> warningDeassertLow =
1459 thresholdDeassertMap[path]["WarningAlarmLow"];
1460
Willy Tude54f482021-01-26 15:59:09 -08001461 if (criticalDeassertHigh && !*criticalDeassertHigh)
1462 {
1463 deassertions.set(static_cast<size_t>(
1464 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1465 }
1466 if (criticalDeassertLow && !*criticalDeassertLow)
1467 {
1468 deassertions.set(static_cast<size_t>(
1469 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1470 }
1471 if (warningDeassertHigh && !*warningDeassertHigh)
1472 {
1473 deassertions.set(static_cast<size_t>(
1474 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1475 }
1476 if (warningDeassertLow && !*warningDeassertLow)
1477 {
1478 deassertions.set(static_cast<size_t>(
1479 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1480 }
1481 if ((warningInterface != sensorMap.end()) ||
1482 (criticalInterface != sensorMap.end()))
1483 {
1484 sensorEventStatus = static_cast<size_t>(
1485 IPMISensorEventEnableByte2::eventMessagesEnable);
1486 if (warningInterface != sensorMap.end())
1487 {
1488 auto& warningMap = warningInterface->second;
1489
1490 auto warningHigh = warningMap.find("WarningAlarmHigh");
1491 auto warningLow = warningMap.find("WarningAlarmLow");
1492 auto warningHighAlarm = false;
1493 auto warningLowAlarm = false;
1494
1495 if (warningHigh != warningMap.end())
1496 {
1497 warningHighAlarm = std::get<bool>(warningHigh->second);
1498 }
1499 if (warningLow != warningMap.end())
1500 {
1501 warningLowAlarm = std::get<bool>(warningLow->second);
1502 }
1503 if (warningHighAlarm)
1504 {
1505 assertions.set(
1506 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1507 upperNonCriticalGoingHigh));
1508 }
1509 if (warningLowAlarm)
1510 {
1511 assertions.set(
1512 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1513 lowerNonCriticalGoingLow));
1514 }
1515 }
1516 if (criticalInterface != sensorMap.end())
1517 {
1518 auto& criticalMap = criticalInterface->second;
1519
1520 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1521 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1522 auto criticalHighAlarm = false;
1523 auto criticalLowAlarm = false;
1524
1525 if (criticalHigh != criticalMap.end())
1526 {
1527 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1528 }
1529 if (criticalLow != criticalMap.end())
1530 {
1531 criticalLowAlarm = std::get<bool>(criticalLow->second);
1532 }
1533 if (criticalHighAlarm)
1534 {
1535 assertions.set(
1536 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1537 upperCriticalGoingHigh));
1538 }
1539 if (criticalLowAlarm)
1540 {
1541 assertions.set(static_cast<size_t>(
1542 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1543 }
1544 }
1545 }
1546
1547 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1548}
1549
Willy Tu38e7a2b2021-03-29 15:09:56 -07001550// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001551void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1552 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001553{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001554 get_sdr::header::set_record_id(
1555 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1556
Willy Tu38e7a2b2021-03-29 15:09:56 -07001557 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1558 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1559
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001560 record.header.sdr_version = ipmiSdrVersion;
1561 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1562 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1563 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001564 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001565 record.key.owner_lun = lun;
1566 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001567}
1568bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1569 uint16_t recordID, const std::string& service,
1570 const std::string& path,
1571 get_sdr::SensorDataFullRecord& record)
1572{
1573 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1574 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1575
1576 DbusInterfaceMap sensorMap;
1577 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1578 {
1579 phosphor::logging::log<phosphor::logging::level::ERR>(
1580 "Failed to update sensor map for threshold sensor",
1581 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1582 phosphor::logging::entry("PATH=%s", path.c_str()));
1583 return false;
1584 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001585
1586 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1587 record.body.sensor_type = getSensorTypeFromPath(path);
1588 std::string type = getSensorTypeStringFromPath(path);
1589 auto typeCstr = type.c_str();
1590 auto findUnits = sensorUnits.find(typeCstr);
1591 if (findUnits != sensorUnits.end())
1592 {
1593 record.body.sensor_units_2_base =
1594 static_cast<uint8_t>(findUnits->second);
1595 } // else default 0x0 unspecified
1596
1597 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1598
Hao Jiangd2afd052020-12-10 15:09:32 -08001599 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001600 if (sensorObject == sensorMap.end())
1601 {
1602 phosphor::logging::log<phosphor::logging::level::ERR>(
1603 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001604 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001605 }
1606
1607 uint8_t entityId = 0;
1608 uint8_t entityInstance = 0x01;
1609
1610 // follow the association chain to get the parent board's entityid and
1611 // entityInstance
1612 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1613
1614 record.body.entity_id = entityId;
1615 record.body.entity_instance = entityInstance;
1616
Shakeeb Pasha93889722021-10-14 10:20:13 +05301617 double max = 0;
1618 double min = 0;
1619 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001620
1621 int16_t mValue = 0;
1622 int8_t rExp = 0;
1623 int16_t bValue = 0;
1624 int8_t bExp = 0;
1625 bool bSigned = false;
1626
1627 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1628 {
1629 phosphor::logging::log<phosphor::logging::level::ERR>(
1630 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001631 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001632 }
1633
1634 // The record.body is a struct SensorDataFullRecordBody
1635 // from sensorhandler.hpp in phosphor-ipmi-host.
1636 // The meaning of these bits appears to come from
1637 // table 43.1 of the IPMI spec.
1638 // The above 5 sensor attributes are stuffed in as follows:
1639 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1640 // Byte 22-24 are for other purposes
1641 // Byte 25 = MMMMMMMM = LSB of M
1642 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1643 // Byte 27 = BBBBBBBB = LSB of B
1644 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1645 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1646 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1647
1648 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1649 record.body.m_lsb = mValue & 0xFF;
1650
1651 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1652 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1653
1654 // move the smallest bit of the MSB into place (bit 9)
1655 // the MSbs are bits 7:8 in m_msb_and_tolerance
1656 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1657
1658 record.body.b_lsb = bValue & 0xFF;
1659
1660 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1661 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1662
1663 // move the smallest bit of the MSB into place (bit 9)
1664 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1665 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1666
1667 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1668 uint8_t rExpBits = rExp & 0x07;
1669
1670 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1671 uint8_t bExpBits = bExp & 0x07;
1672
1673 // move rExp and bExp into place
1674 record.body.r_b_exponents =
1675 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1676
1677 // Set the analog reading byte interpretation accordingly
1678 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1679
1680 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1681 // These seem redundant, but derivable from the above 5 attributes
1682 // Original comment said "todo fill out rest of units"
1683
1684 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001685 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001686 record.body.id_string_info = name.size();
1687 std::strncpy(record.body.id_string, name.c_str(),
1688 sizeof(record.body.id_string));
1689
Josh Lehana55c9532020-10-28 21:59:06 -07001690 // Remember the sensor name, as determined for this sensor number
1691 details::sdrStatsTable.updateName(sensornumber, name);
1692
Jie Yangf0a89942021-07-29 15:30:25 -07001693 bool sensorSettable = false;
1694 auto mutability =
1695 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1696 if (mutability != sensorMap.end())
1697 {
1698 sensorSettable =
1699 mappedVariant<bool>(mutability->second, "Mutable", false);
1700 }
1701 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1702
1703 // Grant write permission to sensors deemed externally settable
1704 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001705
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001706 IPMIThresholds thresholdData;
1707 try
1708 {
1709 thresholdData = getIPMIThresholds(sensorMap);
1710 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001711 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001712 {
1713 phosphor::logging::log<phosphor::logging::level::ERR>(
1714 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001715 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001716 }
1717
1718 if (thresholdData.criticalHigh)
1719 {
1720 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1721 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1722 IPMISensorEventEnableThresholds::criticalThreshold);
1723 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1724 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1725 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1726 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1727 record.body.discrete_reading_setting_mask[0] |=
1728 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1729 }
1730 if (thresholdData.warningHigh)
1731 {
1732 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1733 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1734 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1735 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1736 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1737 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1738 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1739 record.body.discrete_reading_setting_mask[0] |=
1740 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1741 }
1742 if (thresholdData.criticalLow)
1743 {
1744 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1745 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1746 IPMISensorEventEnableThresholds::criticalThreshold);
1747 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1748 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1749 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1750 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1751 record.body.discrete_reading_setting_mask[0] |=
1752 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1753 }
1754 if (thresholdData.warningLow)
1755 {
1756 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1757 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1758 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1759 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1760 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1761 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1762 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1763 record.body.discrete_reading_setting_mask[0] |=
1764 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1765 }
1766
1767 // everything that is readable is setable
1768 record.body.discrete_reading_setting_mask[1] =
1769 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001770 return true;
1771}
1772
Scron Chang2703b022021-07-06 15:47:45 +08001773#ifdef FEATURE_HYBRID_SENSORS
1774// Construct a type 1 SDR for discrete Sensor typed sensor.
1775void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1776 uint16_t recordID,
1777 ipmi::sensor::IdInfoMap::const_iterator sensor,
1778 get_sdr::SensorDataFullRecord& record)
1779{
1780 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1781
1782 record.body.entity_id = sensor->second.entityType;
1783 record.body.sensor_type = sensor->second.sensorType;
1784 record.body.event_reading_type = sensor->second.sensorReadingType;
1785 record.body.entity_instance = sensor->second.instance;
1786 if (ipmi::sensor::Mutability::Write ==
1787 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1788 {
1789 get_sdr::body::init_settable_state(true, &(record.body));
1790 }
1791
1792 auto id_string = sensor->second.sensorName;
1793
1794 if (id_string.empty())
1795 {
1796 id_string = sensor->second.sensorNameFunc(sensor->second);
1797 }
1798
1799 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1800 {
1801 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1802 &(record.body));
1803 }
1804 else
1805 {
1806 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1807 }
1808 std::strncpy(record.body.id_string, id_string.c_str(),
1809 get_sdr::body::get_id_strlen(&(record.body)));
1810}
1811#endif
1812
Hao Jiange39d4d82021-04-16 17:02:40 -07001813// Construct type 3 SDR header and key (for VR and other discrete sensors)
1814void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1815 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001816{
1817 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1818 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1819
1820 get_sdr::header::set_record_id(
1821 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1822
1823 record.header.sdr_version = ipmiSdrVersion;
1824 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1825 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1826 sizeof(get_sdr::SensorDataRecordHeader);
1827 record.key.owner_id = bmcI2CAddr;
1828 record.key.owner_lun = lun;
1829 record.key.sensor_number = sensornumber;
1830
1831 record.body.entity_id = 0x00;
1832 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001833}
Willy Tu61992ad2021-03-29 15:33:20 -07001834
Hao Jiange39d4d82021-04-16 17:02:40 -07001835// Construct a type 3 SDR for VR typed sensor(daemon).
1836bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1837 uint16_t recordID, const std::string& service,
1838 const std::string& path,
1839 get_sdr::SensorDataEventRecord& record)
1840{
1841 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1842 constructEventSdrHeaderKey(sensorNum, recordID, record);
1843
1844 DbusInterfaceMap sensorMap;
1845 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1846 {
1847 phosphor::logging::log<phosphor::logging::level::ERR>(
1848 "Failed to update sensor map for VR sensor",
1849 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1850 phosphor::logging::entry("PATH=%s", path.c_str()));
1851 return false;
1852 }
Willy Tu61992ad2021-03-29 15:33:20 -07001853 // follow the association chain to get the parent board's entityid and
1854 // entityInstance
1855 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1856 record.body.entity_instance);
1857
1858 // Sensor type is hardcoded as a module/board type instead of parsing from
1859 // sensor path. This is because VR control is allocated in an independent
1860 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1861 // types.
1862 static constexpr const uint8_t module_board_type = 0x15;
1863 record.body.sensor_type = module_board_type;
1864 record.body.event_reading_type = 0x00;
1865
1866 record.body.sensor_record_sharing_1 = 0x00;
1867 record.body.sensor_record_sharing_2 = 0x00;
1868
1869 // populate sensor name from path
1870 auto name = sensor::parseSdrIdFromPath(path);
1871 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1872 record.body.id_string_info = nameSize;
1873 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1874 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1875
1876 // Remember the sensor name, as determined for this sensor number
1877 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001878
1879 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001880}
1881
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001882static inline uint16_t getNumberOfSensors()
1883{
1884 return std::min(getSensorTree().size(), maxIPMISensors);
1885}
1886
Hao Jiange39d4d82021-04-16 17:02:40 -07001887static int
1888 getSensorDataRecord(ipmi::Context::ptr ctx,
1889 std::vector<uint8_t>& recordData, uint16_t recordID,
1890 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001891{
1892 size_t fruCount = 0;
1893 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1894 if (ret != ipmi::ccSuccess)
1895 {
1896 phosphor::logging::log<phosphor::logging::level::ERR>(
1897 "getSensorDataRecord: getFruSdrCount error");
1898 return GENERAL_ERROR;
1899 }
1900
Willy Tu38e7a2b2021-03-29 15:09:56 -07001901 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001902 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001903 if (recordID == lastRecordIndex)
1904 {
1905 recordID = lastRecord;
1906 }
1907 if (recordID > lastRecord)
1908 {
1909 phosphor::logging::log<phosphor::logging::level::ERR>(
1910 "getSensorDataRecord: recordID > lastRecord error");
1911 return GENERAL_ERROR;
1912 }
1913
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001914 if (recordID >= getNumberOfSensors())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001915 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001916 size_t fruIndex = recordID - getNumberOfSensors();
Willy Tu38e7a2b2021-03-29 15:09:56 -07001917
1918 if (fruIndex >= fruCount)
1919 {
1920 // handle type 12 hardcoded records
1921 size_t type12Index = fruIndex - fruCount;
1922 if (type12Index >= ipmi::storage::type12Count)
1923 {
1924 phosphor::logging::log<phosphor::logging::level::ERR>(
1925 "getSensorDataRecord: type12Index error");
1926 return GENERAL_ERROR;
1927 }
1928 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1929 }
1930 else
1931 {
1932 // handle fru records
1933 get_sdr::SensorDataFruRecord data;
1934 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1935 if (ret != IPMI_CC_OK)
1936 {
1937 return GENERAL_ERROR;
1938 }
1939 data.header.record_id_msb = recordID >> 8;
1940 data.header.record_id_lsb = recordID & 0xFF;
1941 recordData.insert(recordData.end(), (uint8_t*)&data,
1942 ((uint8_t*)&data) + sizeof(data));
1943 }
1944
1945 return 0;
1946 }
1947
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001948 // Perform a incremental scan of the SDR Record ID's and translate the
1949 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
1950 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
1951 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
1952 // which has special meaning.
Willy Tu38e7a2b2021-03-29 15:09:56 -07001953 std::string connection;
1954 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001955 std::vector<std::string> interfaces;
Johnathan Manteyce982772021-07-28 15:08:30 -07001956 uint16_t sensNumFromRecID{recordID};
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001957 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
Johnathan Manteyce982772021-07-28 15:08:30 -07001958 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001959 // LUN 0 has one reserved sensor number. Compensate here by adding one
1960 // to the record ID
1961 sensNumFromRecID = recordID + 1;
Johnathan Manteyce982772021-07-28 15:08:30 -07001962 ctx->lun = 1;
1963 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001964 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
Johnathan Manteyce982772021-07-28 15:08:30 -07001965 {
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001966 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
1967 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
1968 // rules governing its use.
1969 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
Johnathan Manteyce982772021-07-28 15:08:30 -07001970 ctx->lun = 3;
1971 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001972
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001973 auto status =
1974 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
1975 connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001976 if (status)
1977 {
1978 phosphor::logging::log<phosphor::logging::level::ERR>(
1979 "getSensorDataRecord: getSensorConnection error");
1980 return GENERAL_ERROR;
1981 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001982 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001983 // Return an error on LUN 2 assingments, and any sensor number beyond the
1984 // range of LUN 3
1985 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
1986 (sensorNum > lun3MaxSensorNum))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001987 {
1988 phosphor::logging::log<phosphor::logging::level::ERR>(
1989 "getSensorDataRecord: invalidSensorNumber");
1990 return GENERAL_ERROR;
1991 }
Johnathan Manteyce982772021-07-28 15:08:30 -07001992 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1993 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1994
Johnathan Mantey6619ae42021-08-06 11:21:10 -07001995 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
1996 (lun != ctx->lun))
Johnathan Manteyce982772021-07-28 15:08:30 -07001997 {
1998 phosphor::logging::log<phosphor::logging::level::ERR>(
1999 "getSensorDataRecord: sensor record mismatch");
2000 return GENERAL_ERROR;
2001 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07002002
Willy Tu38e7a2b2021-03-29 15:09:56 -07002003 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07002004 if (std::find(interfaces.begin(), interfaces.end(),
2005 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07002006 {
2007 get_sdr::SensorDataFullRecord record = {0};
2008
Hao Jiange39d4d82021-04-16 17:02:40 -07002009 // If the request doesn't read SDR body, construct only header and key
2010 // part to avoid additional DBus transaction.
2011 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2012 {
2013 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2014 }
2015 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
2016 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07002017 {
2018 return GENERAL_ERROR;
2019 }
Hao Jiange39d4d82021-04-16 17:02:40 -07002020
Willy Tu38e7a2b2021-03-29 15:09:56 -07002021 recordData.insert(recordData.end(), (uint8_t*)&record,
2022 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07002023
2024 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002025 }
Willy Tu61992ad2021-03-29 15:33:20 -07002026
Scron Chang2703b022021-07-06 15:47:45 +08002027#ifdef FEATURE_HYBRID_SENSORS
2028 if (auto sensor = findStaticSensor(path);
2029 sensor != ipmi::sensor::sensors.end() &&
2030 getSensorEventTypeFromPath(path) !=
2031 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2032 {
2033 get_sdr::SensorDataFullRecord record = {0};
2034
2035 // If the request doesn't read SDR body, construct only header and key
2036 // part to avoid additional DBus transaction.
2037 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2038 {
2039 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2040 }
2041 else
2042 {
2043 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2044 }
2045
2046 recordData.insert(recordData.end(), (uint8_t*)&record,
2047 ((uint8_t*)&record) + sizeof(record));
2048
2049 return 0;
2050 }
2051#endif
2052
Willy Tu61992ad2021-03-29 15:33:20 -07002053 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002054 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2055 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002056 {
2057 get_sdr::SensorDataEventRecord record = {0};
2058
Hao Jiange39d4d82021-04-16 17:02:40 -07002059 // If the request doesn't read SDR body, construct only header and key
2060 // part to avoid additional DBus transaction.
2061 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2062 {
2063 constructEventSdrHeaderKey(sensorNum, recordID, record);
2064 }
2065 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2066 record))
2067 {
2068 return GENERAL_ERROR;
2069 }
Willy Tu61992ad2021-03-29 15:33:20 -07002070 recordData.insert(recordData.end(), (uint8_t*)&record,
2071 ((uint8_t*)&record) + sizeof(record));
2072 }
2073
Willy Tude54f482021-01-26 15:59:09 -08002074 return 0;
2075}
2076
2077/** @brief implements the get SDR Info command
2078 * @param count - Operation
2079 *
2080 * @returns IPMI completion code plus response data
2081 * - sdrCount - sensor/SDR count
2082 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2083 */
2084static ipmi::RspType<uint8_t, // respcount
2085 uint8_t, // dynamic population flags
2086 uint32_t // last time a sensor was added
2087 >
2088 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2089 std::optional<uint8_t> count)
2090{
2091 auto& sensorTree = getSensorTree();
2092 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002093 uint16_t recordID = 0;
2094 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002095 // Sensors are dynamically allocated, and there is at least one LUN
2096 uint8_t lunsAndDynamicPopulation = 0x80;
2097 constexpr uint8_t getSdrCount = 0x01;
2098 constexpr uint8_t getSensorCount = 0x00;
2099
2100 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2101 {
2102 return ipmi::responseResponseError();
2103 }
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002104 uint16_t numSensors = getNumberOfSensors();
Willy Tude54f482021-01-26 15:59:09 -08002105 if (count.value_or(0) == getSdrCount)
2106 {
2107 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002108 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002109 {
2110 get_sdr::SensorDataRecordHeader* hdr =
2111 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002112 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002113 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2114 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002115 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002116 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002117 record.data());
2118 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002119 {
2120 sdrCount++;
2121 }
2122 }
2123 }
2124 }
2125 else if (count.value_or(0) == getSensorCount)
2126 {
2127 // Return the number of sensors attached to the LUN
2128 if ((ctx->lun == 0) && (numSensors > 0))
2129 {
2130 sdrCount =
2131 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2132 }
2133 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2134 {
2135 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2136 ? maxSensorsPerLUN
2137 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2138 }
2139 else if (ctx->lun == 3)
2140 {
2141 if (numSensors <= maxIPMISensors)
2142 {
2143 sdrCount =
2144 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2145 }
2146 else
2147 {
2148 // error
2149 throw std::out_of_range(
2150 "Maximum number of IPMI sensors exceeded.");
2151 }
2152 }
2153 }
2154 else
2155 {
2156 return ipmi::responseInvalidFieldRequest();
2157 }
2158
2159 // Get Sensor count. This returns the number of sensors
2160 if (numSensors > 0)
2161 {
2162 lunsAndDynamicPopulation |= 1;
2163 }
2164 if (numSensors > maxSensorsPerLUN)
2165 {
2166 lunsAndDynamicPopulation |= 2;
2167 }
2168 if (numSensors >= (maxSensorsPerLUN * 2))
2169 {
2170 lunsAndDynamicPopulation |= 8;
2171 }
2172 if (numSensors > maxIPMISensors)
2173 {
2174 // error
2175 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2176 }
2177
2178 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2179 sdrLastAdd);
2180}
2181
2182/* end sensor commands */
2183
2184/* storage commands */
2185
2186ipmi::RspType<uint8_t, // sdr version
2187 uint16_t, // record count
2188 uint16_t, // free space
2189 uint32_t, // most recent addition
2190 uint32_t, // most recent erase
2191 uint8_t // operationSupport
2192 >
2193 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2194{
2195 auto& sensorTree = getSensorTree();
2196 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002197 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002198 {
2199 return ipmi::responseResponseError();
2200 }
2201
2202 size_t fruCount = 0;
2203 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2204 if (ret != ipmi::ccSuccess)
2205 {
2206 return ipmi::response(ret);
2207 }
2208
2209 uint16_t recordCount =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002210 getNumberOfSensors() + fruCount + ipmi::storage::type12Count;
Willy Tude54f482021-01-26 15:59:09 -08002211
2212 uint8_t operationSupport = static_cast<uint8_t>(
2213 SdrRepositoryInfoOps::overflow); // write not supported
2214
2215 operationSupport |=
2216 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2217 operationSupport |= static_cast<uint8_t>(
2218 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2219 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2220 unspecifiedFreeSpace, sdrLastAdd,
2221 sdrLastRemove, operationSupport);
2222}
2223
2224/** @brief implements the get SDR allocation info command
2225 *
2226 * @returns IPMI completion code plus response data
2227 * - allocUnits - Number of possible allocation units
2228 * - allocUnitSize - Allocation unit size in bytes.
2229 * - allocUnitFree - Number of free allocation units
2230 * - allocUnitLargestFree - Largest free block in allocation units
2231 * - maxRecordSize - Maximum record size in allocation units.
2232 */
2233ipmi::RspType<uint16_t, // allocUnits
2234 uint16_t, // allocUnitSize
2235 uint16_t, // allocUnitFree
2236 uint16_t, // allocUnitLargestFree
2237 uint8_t // maxRecordSize
2238 >
2239 ipmiStorageGetSDRAllocationInfo()
2240{
2241 // 0000h unspecified number of alloc units
2242 constexpr uint16_t allocUnits = 0;
2243
2244 constexpr uint16_t allocUnitFree = 0;
2245 constexpr uint16_t allocUnitLargestFree = 0;
2246 // only allow one block at a time
2247 constexpr uint8_t maxRecordSize = 1;
2248
2249 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2250 allocUnitLargestFree, maxRecordSize);
2251}
2252
2253/** @brief implements the reserve SDR command
2254 * @returns IPMI completion code plus response data
2255 * - sdrReservationID
2256 */
2257ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2258{
2259 sdrReservationID++;
2260 if (sdrReservationID == 0)
2261 {
2262 sdrReservationID++;
2263 }
2264
2265 return ipmi::responseSuccess(sdrReservationID);
2266}
2267
2268ipmi::RspType<uint16_t, // next record ID
2269 std::vector<uint8_t> // payload
2270 >
2271 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2272 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2273{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002274 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002275 // reservation required for partial reads with non zero offset into
2276 // record
2277 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2278 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002279 phosphor::logging::log<phosphor::logging::level::ERR>(
2280 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002281 return ipmi::responseInvalidReservationId();
2282 }
Willy Tude54f482021-01-26 15:59:09 -08002283 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2284 if (ret != ipmi::ccSuccess)
2285 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002286 phosphor::logging::log<phosphor::logging::level::ERR>(
2287 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002288 return ipmi::response(ret);
2289 }
2290
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002291 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002292 size_t lastRecord =
Johnathan Mantey6619ae42021-08-06 11:21:10 -07002293 getNumberOfSensors() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002294 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2295
2296 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002297 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002298 phosphor::logging::log<phosphor::logging::level::ERR>(
2299 "ipmiStorageGetSDR: getSensorSubtree error");
2300 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002301 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002302
2303 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002304 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002305 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002306 phosphor::logging::log<phosphor::logging::level::ERR>(
2307 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002308 return ipmi::responseInvalidFieldRequest();
2309 }
Willy Tude54f482021-01-26 15:59:09 -08002310 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002311 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002312 if (!hdr)
2313 {
2314 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002315 "ipmiStorageGetSDR: record header is null");
2316 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002317 }
2318
2319 size_t sdrLength =
2320 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2321 if (sdrLength < (offset + bytesToRead))
2322 {
2323 bytesToRead = sdrLength - offset;
2324 }
2325
2326 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2327 if (!respStart)
2328 {
2329 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002330 "ipmiStorageGetSDR: record is null");
2331 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002332 }
2333
2334 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002335
Willy Tude54f482021-01-26 15:59:09 -08002336 return ipmi::responseSuccess(nextRecordId, recordData);
2337}
2338/* end storage commands */
2339
2340void registerSensorFunctions()
2341{
2342 // <Platform Event>
2343 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2344 ipmi::sensor_event::cmdPlatformEvent,
2345 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2346
Willy Tudbafbce2021-03-29 00:37:05 -07002347 // <Set Sensor Reading and Event Status>
2348 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2349 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2350 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002351
Willy Tude54f482021-01-26 15:59:09 -08002352 // <Get Sensor Reading>
2353 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2354 ipmi::sensor_event::cmdGetSensorReading,
2355 ipmi::Privilege::User, ipmiSenGetSensorReading);
2356
2357 // <Get Sensor Threshold>
2358 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2359 ipmi::sensor_event::cmdGetSensorThreshold,
2360 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2361
2362 // <Set Sensor Threshold>
2363 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2364 ipmi::sensor_event::cmdSetSensorThreshold,
2365 ipmi::Privilege::Operator,
2366 ipmiSenSetSensorThresholds);
2367
2368 // <Get Sensor Event Enable>
2369 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2370 ipmi::sensor_event::cmdGetSensorEventEnable,
2371 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2372
2373 // <Get Sensor Event Status>
2374 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2375 ipmi::sensor_event::cmdGetSensorEventStatus,
2376 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2377
2378 // register all storage commands for both Sensor and Storage command
2379 // versions
2380
2381 // <Get SDR Repository Info>
2382 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2383 ipmi::storage::cmdGetSdrRepositoryInfo,
2384 ipmi::Privilege::User,
2385 ipmiStorageGetSDRRepositoryInfo);
2386
2387 // <Get Device SDR Info>
2388 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2389 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2390 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2391
2392 // <Get SDR Allocation Info>
2393 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2394 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2395 ipmi::Privilege::User,
2396 ipmiStorageGetSDRAllocationInfo);
2397
2398 // <Reserve SDR Repo>
2399 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2400 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2401 ipmi::Privilege::User, ipmiStorageReserveSDR);
2402
2403 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2404 ipmi::storage::cmdReserveSdrRepository,
2405 ipmi::Privilege::User, ipmiStorageReserveSDR);
2406
2407 // <Get Sdr>
2408 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2409 ipmi::sensor_event::cmdGetDeviceSdr,
2410 ipmi::Privilege::User, ipmiStorageGetSDR);
2411
2412 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2413 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2414 ipmiStorageGetSDR);
2415}
2416} // namespace ipmi