blob: 0271c777b4f128b91cda705b069dc371964f84f7 [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;
81static constexpr int GENERAL_ERROR = -1;
82
Willy Tude54f482021-01-26 15:59:09 -080083static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
84
85// Specify the comparison required to sort and find char* map objects
86struct CmpStr
87{
88 bool operator()(const char* a, const char* b) const
89 {
90 return std::strcmp(a, b) < 0;
91 }
92};
93const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
94 sensorUnits{{{"temperature", SensorUnits::degreesC},
95 {"voltage", SensorUnits::volts},
96 {"current", SensorUnits::amps},
97 {"fan_tach", SensorUnits::rpm},
98 {"power", SensorUnits::watts}}};
99
100void registerSensorFunctions() __attribute__((constructor));
101
102static sdbusplus::bus::match::match sensorAdded(
103 *getSdBus(),
104 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
105 "sensors/'",
106 [](sdbusplus::message::message& m) {
107 getSensorTree().clear();
108 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
109 std::chrono::system_clock::now().time_since_epoch())
110 .count();
111 });
112
113static sdbusplus::bus::match::match sensorRemoved(
114 *getSdBus(),
115 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
116 "sensors/'",
117 [](sdbusplus::message::message& m) {
118 getSensorTree().clear();
119 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
120 std::chrono::system_clock::now().time_since_epoch())
121 .count();
122 });
123
124// this keeps track of deassertions for sensor event status command. A
125// deasertion can only happen if an assertion was seen first.
126static boost::container::flat_map<
127 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
128 thresholdDeassertMap;
129
130static sdbusplus::bus::match::match thresholdChanged(
131 *getSdBus(),
132 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
133 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
134 [](sdbusplus::message::message& m) {
135 boost::container::flat_map<std::string, std::variant<bool, double>>
136 values;
137 m.read(std::string(), values);
138
139 auto findAssert =
140 std::find_if(values.begin(), values.end(), [](const auto& pair) {
141 return pair.first.find("Alarm") != std::string::npos;
142 });
143 if (findAssert != values.end())
144 {
145 auto ptr = std::get_if<bool>(&(findAssert->second));
146 if (ptr == nullptr)
147 {
148 phosphor::logging::log<phosphor::logging::level::ERR>(
149 "thresholdChanged: Assert non bool");
150 return;
151 }
152 if (*ptr)
153 {
154 phosphor::logging::log<phosphor::logging::level::INFO>(
155 "thresholdChanged: Assert",
156 phosphor::logging::entry("SENSOR=%s", m.get_path()));
157 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
158 }
159 else
160 {
161 auto& value =
162 thresholdDeassertMap[m.get_path()][findAssert->first];
163 if (value)
164 {
165 phosphor::logging::log<phosphor::logging::level::INFO>(
166 "thresholdChanged: deassert",
167 phosphor::logging::entry("SENSOR=%s", m.get_path()));
168 value = *ptr;
169 }
170 }
171 }
172 });
173
Hao Jiangd2afd052020-12-10 15:09:32 -0800174namespace sensor
175{
176static constexpr const char* vrInterface =
177 "xyz.openbmc_project.Control.VoltageRegulatorMode";
178static constexpr const char* sensorInterface =
179 "xyz.openbmc_project.Sensor.Value";
180} // namespace sensor
181
Willy Tude54f482021-01-26 15:59:09 -0800182static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
183 double& min)
184{
185 max = 127;
186 min = -128;
187
Hao Jiangd2afd052020-12-10 15:09:32 -0800188 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800189 auto critical =
190 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
191 auto warning =
192 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
193
194 if (sensorObject != sensorMap.end())
195 {
196 auto maxMap = sensorObject->second.find("MaxValue");
197 auto minMap = sensorObject->second.find("MinValue");
198
199 if (maxMap != sensorObject->second.end())
200 {
201 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
202 }
203 if (minMap != sensorObject->second.end())
204 {
205 min = std::visit(VariantToDoubleVisitor(), minMap->second);
206 }
207 }
208 if (critical != sensorMap.end())
209 {
210 auto lower = critical->second.find("CriticalLow");
211 auto upper = critical->second.find("CriticalHigh");
212 if (lower != critical->second.end())
213 {
214 double value = std::visit(VariantToDoubleVisitor(), lower->second);
215 min = std::min(value, min);
216 }
217 if (upper != critical->second.end())
218 {
219 double value = std::visit(VariantToDoubleVisitor(), upper->second);
220 max = std::max(value, max);
221 }
222 }
223 if (warning != sensorMap.end())
224 {
225
226 auto lower = warning->second.find("WarningLow");
227 auto upper = warning->second.find("WarningHigh");
228 if (lower != warning->second.end())
229 {
230 double value = std::visit(VariantToDoubleVisitor(), lower->second);
231 min = std::min(value, min);
232 }
233 if (upper != warning->second.end())
234 {
235 double value = std::visit(VariantToDoubleVisitor(), upper->second);
236 max = std::max(value, max);
237 }
238 }
239}
240
241static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
Alex Qiu9ab2f942020-07-15 17:56:21 -0700242 std::string sensorPath, DbusInterfaceMap& sensorMap,
243 int updatePeriod = sensorMapUpdatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800244{
Scron Chang2703b022021-07-06 15:47:45 +0800245#ifdef FEATURE_HYBRID_SENSORS
246 if (auto sensor = findStaticSensor(sensorPath);
247 sensor != ipmi::sensor::sensors.end() &&
248 getSensorEventTypeFromPath(sensorPath) !=
249 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
250 {
251 // If the incoming sensor is a discrete sensor, it might fail in
252 // getManagedObjects(), return true, and use its own getFunc to get
253 // value.
254 return true;
255 }
256#endif
257
Willy Tude54f482021-01-26 15:59:09 -0800258 static boost::container::flat_map<
259 std::string, std::chrono::time_point<std::chrono::steady_clock>>
260 updateTimeMap;
261
262 auto updateFind = updateTimeMap.find(sensorConnection);
263 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
264 if (updateFind != updateTimeMap.end())
265 {
266 lastUpdate = updateFind->second;
267 }
268
269 auto now = std::chrono::steady_clock::now();
270
271 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
Alex Qiu9ab2f942020-07-15 17:56:21 -0700272 .count() > updatePeriod)
Willy Tude54f482021-01-26 15:59:09 -0800273 {
Willy Tude54f482021-01-26 15:59:09 -0800274 ObjectValueTree managedObjects;
275 boost::system::error_code ec = getManagedObjects(
276 ctx, sensorConnection.c_str(), "/", managedObjects);
277 if (ec)
278 {
279 phosphor::logging::log<phosphor::logging::level::ERR>(
280 "GetMangagedObjects for getSensorMap failed",
281 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
282
283 return false;
284 }
285
286 SensorCache[sensorConnection] = managedObjects;
Alex Qiu9ab2f942020-07-15 17:56:21 -0700287 // Update time after finish building the map which allow the
288 // data to be cached for updatePeriod plus the build time.
289 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
Willy Tude54f482021-01-26 15:59:09 -0800290 }
291 auto connection = SensorCache.find(sensorConnection);
292 if (connection == SensorCache.end())
293 {
294 return false;
295 }
296 auto path = connection->second.find(sensorPath);
297 if (path == connection->second.end())
298 {
299 return false;
300 }
301 sensorMap = path->second;
302
303 return true;
304}
305
Hao Jiangd2afd052020-12-10 15:09:32 -0800306namespace sensor
307{
Hao Jiangd48c9212021-02-03 15:45:06 -0800308// Read VR profiles from sensor(daemon) interface
309static std::optional<std::vector<std::string>>
310 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
Hao Jiangd2afd052020-12-10 15:09:32 -0800311{
312 // get VR mode profiles from Supported Interface
Hao Jiangd48c9212021-02-03 15:45:06 -0800313 auto supportedProperty = object.find("Supported");
314 if (supportedProperty == object.end() ||
315 object.find("Selected") == object.end())
Hao Jiangd2afd052020-12-10 15:09:32 -0800316 {
317 phosphor::logging::log<phosphor::logging::level::ERR>(
318 "Missing the required Supported and Selected properties");
319 return std::nullopt;
320 }
321
322 const auto profilesPtr =
323 std::get_if<std::vector<std::string>>(&supportedProperty->second);
324
325 if (profilesPtr == nullptr)
326 {
327 phosphor::logging::log<phosphor::logging::level::ERR>(
328 "property is not array of string");
329 return std::nullopt;
330 }
Hao Jiangd48c9212021-02-03 15:45:06 -0800331 return *profilesPtr;
332}
333
334// Calculate VR Mode from input IPMI discrete event bytes
335static std::optional<std::string>
336 calculateVRMode(uint15_t assertOffset,
337 const ipmi::DbusInterfaceMap::mapped_type& VRObject)
338{
339 // get VR mode profiles from Supported Interface
340 auto profiles = getSupportedVrProfiles(VRObject);
341 if (!profiles)
342 {
343 return std::nullopt;
344 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800345
346 // interpret IPMI cmd bits into profiles' index
347 long unsigned int index = 0;
348 // only one bit should be set and the highest bit should not be used.
349 if (assertOffset == 0 || assertOffset == (1u << 15) ||
350 (assertOffset & (assertOffset - 1)))
351 {
352 phosphor::logging::log<phosphor::logging::level::ERR>(
353 "IPMI cmd format incorrect",
354
355 phosphor::logging::entry("BYTES=%#02x",
356 static_cast<uint16_t>(assertOffset)));
357 return std::nullopt;
358 }
359
360 while (assertOffset != 1)
361 {
362 assertOffset >>= 1;
363 index++;
364 }
365
Hao Jiangd48c9212021-02-03 15:45:06 -0800366 if (index >= profiles->size())
Hao Jiangd2afd052020-12-10 15:09:32 -0800367 {
368 phosphor::logging::log<phosphor::logging::level::ERR>(
369 "profile index out of boundary");
370 return std::nullopt;
371 }
372
Hao Jiangd48c9212021-02-03 15:45:06 -0800373 return profiles->at(index);
Hao Jiangd2afd052020-12-10 15:09:32 -0800374}
375
376// Calculate sensor value from IPMI reading byte
377static std::optional<double>
378 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
379 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
380{
381 if (valueObject.find("Value") == valueObject.end())
382 {
383 phosphor::logging::log<phosphor::logging::level::ERR>(
384 "Missing the required Value property");
385 return std::nullopt;
386 }
387
388 double max = 0;
389 double min = 0;
390 getSensorMaxMin(sensorMap, max, min);
391
392 int16_t mValue = 0;
393 int16_t bValue = 0;
394 int8_t rExp = 0;
395 int8_t bExp = 0;
396 bool bSigned = false;
397
398 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
399 {
400 return std::nullopt;
401 }
402
403 double value = bSigned ? ((int8_t)reading) : reading;
404
405 value *= ((double)mValue);
406 value += ((double)bValue) * std::pow(10.0, bExp);
407 value *= std::pow(10.0, rExp);
408
409 return value;
410}
411
Willy Tu38e7a2b2021-03-29 15:09:56 -0700412// Extract file name from sensor path as the sensors SDR ID. Simplify the name
413// if it is too long.
414std::string parseSdrIdFromPath(const std::string& path)
415{
416 std::string name;
417 size_t nameStart = path.rfind("/");
418 if (nameStart != std::string::npos)
419 {
420 name = path.substr(nameStart + 1, std::string::npos - nameStart);
421 }
422
Willy Tu38e7a2b2021-03-29 15:09:56 -0700423 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
424 {
425 // try to not truncate by replacing common words
JeffLind950f412021-10-20 18:49:34 +0800426 for (const auto& suffix : suffixes)
Willy Tu38e7a2b2021-03-29 15:09:56 -0700427 {
JeffLind950f412021-10-20 18:49:34 +0800428 if (boost::ends_with(name, suffix))
429 {
430 boost::replace_all(name, suffix, "");
431 break;
432 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700433 }
Willy Tu38e7a2b2021-03-29 15:09:56 -0700434 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
435 }
JeffLind950f412021-10-20 18:49:34 +0800436 std::replace(name.begin(), name.end(), '_', ' ');
Willy Tu38e7a2b2021-03-29 15:09:56 -0700437 return name;
438}
439
Hao Jiangd48c9212021-02-03 15:45:06 -0800440bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
441 const std::string& path,
442 const ipmi::DbusInterfaceMap::mapped_type& object,
443 std::bitset<16>& assertions)
444{
445 auto profiles = sensor::getSupportedVrProfiles(object);
446 if (!profiles)
447 {
448 return false;
449 }
450 ipmi::Value modeVariant;
451
452 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
453 "Selected", modeVariant);
454 if (ec)
455 {
456 log<level::ERR>("Failed to get property",
457 entry("PROPERTY=%s", "Selected"),
458 entry("PATH=%s", path.c_str()),
459 entry("INTERFACE=%s", sensor::sensorInterface),
460 entry("WHAT=%s", ec.message().c_str()));
461 return false;
462 }
463
464 auto mode = std::get_if<std::string>(&modeVariant);
465 if (mode == nullptr)
466 {
467 log<level::ERR>("property is not a string",
468 entry("PROPERTY=%s", "Selected"),
469 entry("PATH=%s", path.c_str()),
470 entry("INTERFACE=%s", sensor::sensorInterface));
471 return false;
472 }
473
474 auto itr = std::find(profiles->begin(), profiles->end(), *mode);
475 if (itr == profiles->end())
476 {
477 using namespace phosphor::logging;
478 log<level::ERR>("VR mode doesn't match any of its profiles",
479 entry("PATH=%s", path.c_str()));
480 return false;
481 }
482 std::size_t index =
483 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
484
485 // map index to reponse event assertion bit.
486 if (index < 8)
487 {
488 assertions.set(1u << index);
489 }
490 else if (index < 15)
491 {
492 assertions.set(1u << (index - 8));
493 }
494 else
495 {
496 log<level::ERR>("VR profile index reaches max assertion bit",
497 entry("PATH=%s", path.c_str()),
498 entry("INDEX=%uz", index));
499 return false;
500 }
501 if constexpr (debug)
502 {
503 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
504 << " mode is: [" << index << "] " << *mode << std::endl;
505 }
506 return true;
507}
Hao Jiangd2afd052020-12-10 15:09:32 -0800508} // namespace sensor
509
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000510ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
511 ipmi::message::Payload& p)
Willy Tude54f482021-01-26 15:59:09 -0800512{
Chalapathi Venkataramashetty6f43f4a2021-06-20 19:50:33 +0000513 constexpr const uint8_t validEnvmRev = 0x04;
514 constexpr const uint8_t lastSensorType = 0x2C;
515 constexpr const uint8_t oemReserved = 0xC0;
516
517 uint8_t generatorID = 0;
518 uint8_t evmRev = 0;
519 uint8_t sensorType = 0;
520 uint8_t sensorNum = 0;
521 uint8_t eventType = 0;
522 uint8_t eventData1 = 0;
523 std::optional<uint8_t> eventData2 = 0;
524 std::optional<uint8_t> eventData3 = 0;
525 ipmi::ChannelInfo chInfo;
526
527 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
528 {
529 phosphor::logging::log<phosphor::logging::level::ERR>(
530 "Failed to get Channel Info",
531 phosphor::logging::entry("CHANNEL=%d", ctx->channel));
532 return ipmi::responseUnspecifiedError();
533 }
534
535 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
536 ipmi::EChannelMediumType::systemInterface)
537 {
538
539 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType,
540 eventData1, eventData2, eventData3);
541 }
542 else
543 {
544
545 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
546 eventData2, eventData3);
547 generatorID = ctx->rqSA;
548 }
549
550 if (!p.fullyUnpacked())
551 {
552 return ipmi::responseReqDataLenInvalid();
553 }
554
555 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
556 if (evmRev != validEnvmRev)
557 {
558 return ipmi::responseInvalidFieldRequest();
559 }
560 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
561 {
562 return ipmi::responseInvalidFieldRequest();
563 }
564
Willy Tude54f482021-01-26 15:59:09 -0800565 return ipmi::responseSuccess();
566}
567
Willy Tudbafbce2021-03-29 00:37:05 -0700568ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx,
569 uint8_t sensorNumber, uint8_t operation,
570 uint8_t reading, uint15_t assertOffset,
571 bool resvd1, uint15_t deassertOffset,
572 bool resvd2, uint8_t eventData1,
573 uint8_t eventData2, uint8_t eventData3)
574{
575 std::string connection;
576 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -0700577 std::vector<std::string> interfaces;
578
579 ipmi::Cc status =
580 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
Willy Tudbafbce2021-03-29 00:37:05 -0700581 if (status)
582 {
583 return ipmi::response(status);
584 }
585
Hao Jiangd2afd052020-12-10 15:09:32 -0800586 // we can tell the sensor type by its interface type
Hao Jiange39d4d82021-04-16 17:02:40 -0700587 if (std::find(interfaces.begin(), interfaces.end(),
588 sensor::sensorInterface) != interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700589 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700590 DbusInterfaceMap sensorMap;
591 if (!getSensorMap(ctx, connection, path, sensorMap))
592 {
593 return ipmi::responseResponseError();
594 }
595 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800596 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700597 {
598 return ipmi::responseResponseError();
599 }
600
Jie Yangf0a89942021-07-29 15:30:25 -0700601 // Only allow external SetSensor if write permission granted
602 if (!details::sdrWriteTable.getWritePermission(sensorNumber))
603 {
604 return ipmi::responseResponseError();
605 }
606
Hao Jiangd2afd052020-12-10 15:09:32 -0800607 auto value =
608 sensor::calculateValue(reading, sensorMap, sensorObject->second);
609 if (!value)
610 {
611 return ipmi::responseResponseError();
612 }
613
614 if constexpr (debug)
615 {
616 phosphor::logging::log<phosphor::logging::level::INFO>(
617 "IPMI SET_SENSOR",
618 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber),
619 phosphor::logging::entry("BYTE=%u", (unsigned int)reading),
620 phosphor::logging::entry("VALUE=%f", *value));
621 }
622
623 boost::system::error_code ec =
624 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
625 "Value", ipmi::Value(*value));
626
627 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500628 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800629 // callback functions for now (e.g. ipmiSetSensorReading).
630 if (ec)
631 {
632 using namespace phosphor::logging;
633 log<level::ERR>("Failed to set property",
634 entry("PROPERTY=%s", "Value"),
635 entry("PATH=%s", path.c_str()),
636 entry("INTERFACE=%s", sensor::sensorInterface),
637 entry("WHAT=%s", ec.message().c_str()));
638 return ipmi::responseResponseError();
639 }
640 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700641 }
642
Hao Jiange39d4d82021-04-16 17:02:40 -0700643 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
644 interfaces.end())
Willy Tudbafbce2021-03-29 00:37:05 -0700645 {
Hao Jiange39d4d82021-04-16 17:02:40 -0700646 DbusInterfaceMap sensorMap;
647 if (!getSensorMap(ctx, connection, path, sensorMap))
648 {
649 return ipmi::responseResponseError();
650 }
651 auto sensorObject = sensorMap.find(sensor::vrInterface);
Harvey Wuf61c0862021-09-15 08:48:40 +0800652 if (sensorObject == sensorMap.end())
Hao Jiange39d4d82021-04-16 17:02:40 -0700653 {
654 return ipmi::responseResponseError();
655 }
656
Hao Jiangd2afd052020-12-10 15:09:32 -0800657 // VR sensors are treated as a special case and we will not check the
658 // write permission for VR sensors, since they always deemed writable
659 // and permission table are not applied to VR sensors.
660 auto vrMode =
661 sensor::calculateVRMode(assertOffset, sensorObject->second);
662 if (!vrMode)
663 {
664 return ipmi::responseResponseError();
665 }
666 boost::system::error_code ec = setDbusProperty(
667 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
668 // setDbusProperty intended to resolve dbus exception/rc within the
Patrick Williamsef1259b2021-09-02 09:12:33 -0500669 // function but failed to achieve that. Catch exception in the ipmi
Hao Jiangd2afd052020-12-10 15:09:32 -0800670 // callback functions for now (e.g. ipmiSetSensorReading).
671 if (ec)
672 {
673 using namespace phosphor::logging;
674 log<level::ERR>("Failed to set property",
675 entry("PROPERTY=%s", "Selected"),
676 entry("PATH=%s", path.c_str()),
677 entry("INTERFACE=%s", sensor::sensorInterface),
678 entry("WHAT=%s", ec.message().c_str()));
679 return ipmi::responseResponseError();
680 }
681 return ipmi::responseSuccess();
Willy Tudbafbce2021-03-29 00:37:05 -0700682 }
683
Hao Jiangd2afd052020-12-10 15:09:32 -0800684 phosphor::logging::log<phosphor::logging::level::ERR>(
685 "unknown sensor type",
686 phosphor::logging::entry("PATH=%s", path.c_str()));
687 return ipmi::responseResponseError();
Willy Tudbafbce2021-03-29 00:37:05 -0700688}
689
Willy Tude54f482021-01-26 15:59:09 -0800690ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
691 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
692{
693 std::string connection;
694 std::string path;
695
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000696 if (sensnum == reservedSensorNumber)
697 {
698 return ipmi::responseInvalidFieldRequest();
699 }
700
Willy Tude54f482021-01-26 15:59:09 -0800701 auto status = getSensorConnection(ctx, sensnum, connection, path);
702 if (status)
703 {
704 return ipmi::response(status);
705 }
706
Scron Chang2703b022021-07-06 15:47:45 +0800707#ifdef FEATURE_HYBRID_SENSORS
708 if (auto sensor = findStaticSensor(path);
709 sensor != ipmi::sensor::sensors.end() &&
710 getSensorEventTypeFromPath(path) !=
711 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
712 {
713 if (ipmi::sensor::Mutability::Read !=
714 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
715 {
716 return ipmi::responseIllegalCommand();
717 }
718
719 uint8_t operation;
720 try
721 {
722 ipmi::sensor::GetSensorResponse getResponse =
723 sensor->second.getFunc(sensor->second);
724
725 if (getResponse.readingOrStateUnavailable)
726 {
727 operation |= static_cast<uint8_t>(
728 IPMISensorReadingByte2::readingStateUnavailable);
729 }
730 if (getResponse.scanningEnabled)
731 {
732 operation |= static_cast<uint8_t>(
733 IPMISensorReadingByte2::sensorScanningEnable);
734 }
735 if (getResponse.allEventMessagesEnabled)
736 {
737 operation |= static_cast<uint8_t>(
738 IPMISensorReadingByte2::eventMessagesEnable);
739 }
740 return ipmi::responseSuccess(
741 getResponse.reading, operation,
742 getResponse.thresholdLevelsStates,
743 getResponse.discreteReadingSensorStates);
744 }
745 catch (const std::exception& e)
746 {
747 operation |= static_cast<uint8_t>(
748 IPMISensorReadingByte2::readingStateUnavailable);
749 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
750 }
751 }
752#endif
753
Willy Tude54f482021-01-26 15:59:09 -0800754 DbusInterfaceMap sensorMap;
755 if (!getSensorMap(ctx, connection, path, sensorMap))
756 {
757 return ipmi::responseResponseError();
758 }
Hao Jiangd2afd052020-12-10 15:09:32 -0800759 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -0800760
761 if (sensorObject == sensorMap.end() ||
762 sensorObject->second.find("Value") == sensorObject->second.end())
763 {
764 return ipmi::responseResponseError();
765 }
766 auto& valueVariant = sensorObject->second["Value"];
767 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
768
769 double max = 0;
770 double min = 0;
771 getSensorMaxMin(sensorMap, max, min);
772
773 int16_t mValue = 0;
774 int16_t bValue = 0;
775 int8_t rExp = 0;
776 int8_t bExp = 0;
777 bool bSigned = false;
778
779 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
780 {
781 return ipmi::responseResponseError();
782 }
783
784 uint8_t value =
785 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
786 uint8_t operation =
787 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
788 operation |=
789 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
790 bool notReading = std::isnan(reading);
791
792 if (!notReading)
793 {
794 auto availableObject =
795 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
796 if (availableObject != sensorMap.end())
797 {
798 auto findAvailable = availableObject->second.find("Available");
799 if (findAvailable != availableObject->second.end())
800 {
801 bool* available = std::get_if<bool>(&(findAvailable->second));
802 if (available && !(*available))
803 {
804 notReading = true;
805 }
806 }
807 }
808 }
809
810 if (notReading)
811 {
812 operation |= static_cast<uint8_t>(
813 IPMISensorReadingByte2::readingStateUnavailable);
814 }
815
Josh Lehana55c9532020-10-28 21:59:06 -0700816 if constexpr (details::enableInstrumentation)
817 {
818 int byteValue;
819 if (bSigned)
820 {
821 byteValue = static_cast<int>(static_cast<int8_t>(value));
822 }
823 else
824 {
825 byteValue = static_cast<int>(static_cast<uint8_t>(value));
826 }
827
828 // Keep stats on the reading just obtained, even if it is "NaN"
829 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue))
830 {
831 // This is the first reading, show the coefficients
832 double step = (max - min) / 255.0;
833 std::cerr << "IPMI sensor "
834 << details::sdrStatsTable.getName(sensnum)
835 << ": Range min=" << min << " max=" << max
836 << ", step=" << step
837 << ", Coefficients mValue=" << static_cast<int>(mValue)
838 << " rExp=" << static_cast<int>(rExp)
839 << " bValue=" << static_cast<int>(bValue)
840 << " bExp=" << static_cast<int>(bExp)
841 << " bSigned=" << static_cast<int>(bSigned) << "\n";
842 }
843 }
844
Willy Tude54f482021-01-26 15:59:09 -0800845 uint8_t thresholds = 0;
846
847 auto warningObject =
848 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
849 if (warningObject != sensorMap.end())
850 {
851 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
852 auto alarmLow = warningObject->second.find("WarningAlarmLow");
853 if (alarmHigh != warningObject->second.end())
854 {
855 if (std::get<bool>(alarmHigh->second))
856 {
857 thresholds |= static_cast<uint8_t>(
858 IPMISensorReadingByte3::upperNonCritical);
859 }
860 }
861 if (alarmLow != warningObject->second.end())
862 {
863 if (std::get<bool>(alarmLow->second))
864 {
865 thresholds |= static_cast<uint8_t>(
866 IPMISensorReadingByte3::lowerNonCritical);
867 }
868 }
869 }
870
871 auto criticalObject =
872 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
873 if (criticalObject != sensorMap.end())
874 {
875 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
876 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
877 if (alarmHigh != criticalObject->second.end())
878 {
879 if (std::get<bool>(alarmHigh->second))
880 {
881 thresholds |=
882 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
883 }
884 }
885 if (alarmLow != criticalObject->second.end())
886 {
887 if (std::get<bool>(alarmLow->second))
888 {
889 thresholds |=
890 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
891 }
892 }
893 }
894
895 // no discrete as of today so optional byte is never returned
896 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
897}
898
899/** @brief implements the Set Sensor threshold command
900 * @param sensorNumber - sensor number
901 * @param lowerNonCriticalThreshMask
902 * @param lowerCriticalThreshMask
903 * @param lowerNonRecovThreshMask
904 * @param upperNonCriticalThreshMask
905 * @param upperCriticalThreshMask
906 * @param upperNonRecovThreshMask
907 * @param reserved
908 * @param lowerNonCritical - lower non-critical threshold
909 * @param lowerCritical - Lower critical threshold
910 * @param lowerNonRecoverable - Lower non recovarable threshold
911 * @param upperNonCritical - Upper non-critical threshold
912 * @param upperCritical - Upper critical
913 * @param upperNonRecoverable - Upper Non-recoverable
914 *
915 * @returns IPMI completion code
916 */
917ipmi::RspType<> ipmiSenSetSensorThresholds(
918 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
919 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
920 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
921 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
922 uint8_t lowerCritical, uint8_t lowerNonRecoverable,
923 uint8_t upperNonCritical, uint8_t upperCritical,
924 uint8_t upperNonRecoverable)
925{
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +0000926 if (sensorNum == reservedSensorNumber || reserved)
Willy Tude54f482021-01-26 15:59:09 -0800927 {
928 return ipmi::responseInvalidFieldRequest();
929 }
930
931 // lower nc and upper nc not suppported on any sensor
932 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
933 {
934 return ipmi::responseInvalidFieldRequest();
935 }
936
937 // if none of the threshold mask are set, nothing to do
938 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
939 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
940 upperCriticalThreshMask | upperNonRecovThreshMask))
941 {
942 return ipmi::responseSuccess();
943 }
944
945 std::string connection;
946 std::string path;
947
948 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
949 if (status)
950 {
951 return ipmi::response(status);
952 }
953 DbusInterfaceMap sensorMap;
954 if (!getSensorMap(ctx, connection, path, sensorMap))
955 {
956 return ipmi::responseResponseError();
957 }
958
959 double max = 0;
960 double min = 0;
961 getSensorMaxMin(sensorMap, max, min);
962
963 int16_t mValue = 0;
964 int16_t bValue = 0;
965 int8_t rExp = 0;
966 int8_t bExp = 0;
967 bool bSigned = false;
968
969 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
970 {
971 return ipmi::responseResponseError();
972 }
973
974 // store a vector of property name, value to set, and interface
975 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
976
977 // define the indexes of the tuple
978 constexpr uint8_t propertyName = 0;
979 constexpr uint8_t thresholdValue = 1;
980 constexpr uint8_t interface = 2;
981 // verifiy all needed fields are present
982 if (lowerCriticalThreshMask || upperCriticalThreshMask)
983 {
984 auto findThreshold =
985 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
986 if (findThreshold == sensorMap.end())
987 {
988 return ipmi::responseInvalidFieldRequest();
989 }
990 if (lowerCriticalThreshMask)
991 {
992 auto findLower = findThreshold->second.find("CriticalLow");
993 if (findLower == findThreshold->second.end())
994 {
995 return ipmi::responseInvalidFieldRequest();
996 }
997 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
998 findThreshold->first);
999 }
1000 if (upperCriticalThreshMask)
1001 {
1002 auto findUpper = findThreshold->second.find("CriticalHigh");
1003 if (findUpper == findThreshold->second.end())
1004 {
1005 return ipmi::responseInvalidFieldRequest();
1006 }
1007 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1008 findThreshold->first);
1009 }
1010 }
1011 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1012 {
1013 auto findThreshold =
1014 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1015 if (findThreshold == sensorMap.end())
1016 {
1017 return ipmi::responseInvalidFieldRequest();
1018 }
1019 if (lowerNonCriticalThreshMask)
1020 {
1021 auto findLower = findThreshold->second.find("WarningLow");
1022 if (findLower == findThreshold->second.end())
1023 {
1024 return ipmi::responseInvalidFieldRequest();
1025 }
1026 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1027 findThreshold->first);
1028 }
1029 if (upperNonCriticalThreshMask)
1030 {
1031 auto findUpper = findThreshold->second.find("WarningHigh");
1032 if (findUpper == findThreshold->second.end())
1033 {
1034 return ipmi::responseInvalidFieldRequest();
1035 }
1036 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1037 findThreshold->first);
1038 }
1039 }
1040 for (const auto& property : thresholdsToSet)
1041 {
1042 // from section 36.3 in the IPMI Spec, assume all linear
1043 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1044 (bValue * std::pow(10.0, bExp))) *
1045 std::pow(10.0, rExp);
1046 setDbusProperty(
1047 *getSdBus(), connection, path, std::get<interface>(property),
1048 std::get<propertyName>(property), ipmi::Value(valueToSet));
1049 }
1050 return ipmi::responseSuccess();
1051}
1052
1053IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1054{
1055 IPMIThresholds resp;
1056 auto warningInterface =
1057 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1058 auto criticalInterface =
1059 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1060
1061 if ((warningInterface != sensorMap.end()) ||
1062 (criticalInterface != sensorMap.end()))
1063 {
Hao Jiangd2afd052020-12-10 15:09:32 -08001064 auto sensorPair = sensorMap.find(sensor::sensorInterface);
Willy Tude54f482021-01-26 15:59:09 -08001065
1066 if (sensorPair == sensorMap.end())
1067 {
1068 // should not have been able to find a sensor not implementing
1069 // the sensor object
1070 throw std::runtime_error("Invalid sensor map");
1071 }
1072
1073 double max = 0;
1074 double min = 0;
1075 getSensorMaxMin(sensorMap, max, min);
1076
1077 int16_t mValue = 0;
1078 int16_t bValue = 0;
1079 int8_t rExp = 0;
1080 int8_t bExp = 0;
1081 bool bSigned = false;
1082
1083 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1084 {
1085 throw std::runtime_error("Invalid sensor atrributes");
1086 }
1087 if (warningInterface != sensorMap.end())
1088 {
1089 auto& warningMap = warningInterface->second;
1090
1091 auto warningHigh = warningMap.find("WarningHigh");
1092 auto warningLow = warningMap.find("WarningLow");
1093
1094 if (warningHigh != warningMap.end())
1095 {
1096
1097 double value =
1098 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1099 resp.warningHigh = scaleIPMIValueFromDouble(
1100 value, mValue, rExp, bValue, bExp, bSigned);
1101 }
1102 if (warningLow != warningMap.end())
1103 {
1104 double value =
1105 std::visit(VariantToDoubleVisitor(), warningLow->second);
1106 resp.warningLow = scaleIPMIValueFromDouble(
1107 value, mValue, rExp, bValue, bExp, bSigned);
1108 }
1109 }
1110 if (criticalInterface != sensorMap.end())
1111 {
1112 auto& criticalMap = criticalInterface->second;
1113
1114 auto criticalHigh = criticalMap.find("CriticalHigh");
1115 auto criticalLow = criticalMap.find("CriticalLow");
1116
1117 if (criticalHigh != criticalMap.end())
1118 {
1119 double value =
1120 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1121 resp.criticalHigh = scaleIPMIValueFromDouble(
1122 value, mValue, rExp, bValue, bExp, bSigned);
1123 }
1124 if (criticalLow != criticalMap.end())
1125 {
1126 double value =
1127 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1128 resp.criticalLow = scaleIPMIValueFromDouble(
1129 value, mValue, rExp, bValue, bExp, bSigned);
1130 }
1131 }
1132 }
1133 return resp;
1134}
1135
1136ipmi::RspType<uint8_t, // readable
1137 uint8_t, // lowerNCrit
1138 uint8_t, // lowerCrit
1139 uint8_t, // lowerNrecoverable
1140 uint8_t, // upperNC
1141 uint8_t, // upperCrit
1142 uint8_t> // upperNRecoverable
1143 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1144{
1145 std::string connection;
1146 std::string path;
1147
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001148 if (sensorNumber == reservedSensorNumber)
1149 {
1150 return ipmi::responseInvalidFieldRequest();
1151 }
1152
Willy Tude54f482021-01-26 15:59:09 -08001153 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1154 if (status)
1155 {
1156 return ipmi::response(status);
1157 }
1158
1159 DbusInterfaceMap sensorMap;
1160 if (!getSensorMap(ctx, connection, path, sensorMap))
1161 {
1162 return ipmi::responseResponseError();
1163 }
1164
1165 IPMIThresholds thresholdData;
1166 try
1167 {
1168 thresholdData = getIPMIThresholds(sensorMap);
1169 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001170 catch (const std::exception&)
Willy Tude54f482021-01-26 15:59:09 -08001171 {
1172 return ipmi::responseResponseError();
1173 }
1174
1175 uint8_t readable = 0;
1176 uint8_t lowerNC = 0;
1177 uint8_t lowerCritical = 0;
1178 uint8_t lowerNonRecoverable = 0;
1179 uint8_t upperNC = 0;
1180 uint8_t upperCritical = 0;
1181 uint8_t upperNonRecoverable = 0;
1182
1183 if (thresholdData.warningHigh)
1184 {
1185 readable |=
1186 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1187 upperNC = *thresholdData.warningHigh;
1188 }
1189 if (thresholdData.warningLow)
1190 {
1191 readable |=
1192 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1193 lowerNC = *thresholdData.warningLow;
1194 }
1195
1196 if (thresholdData.criticalHigh)
1197 {
1198 readable |=
1199 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1200 upperCritical = *thresholdData.criticalHigh;
1201 }
1202 if (thresholdData.criticalLow)
1203 {
1204 readable |=
1205 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1206 lowerCritical = *thresholdData.criticalLow;
1207 }
1208
1209 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1210 lowerNonRecoverable, upperNC, upperCritical,
1211 upperNonRecoverable);
1212}
1213
1214/** @brief implements the get Sensor event enable command
1215 * @param sensorNumber - sensor number
1216 *
1217 * @returns IPMI completion code plus response data
1218 * - enabled - Sensor Event messages
1219 * - assertionEnabledLsb - Assertion event messages
1220 * - assertionEnabledMsb - Assertion event messages
1221 * - deassertionEnabledLsb - Deassertion event messages
1222 * - deassertionEnabledMsb - Deassertion event messages
1223 */
1224
1225ipmi::RspType<uint8_t, // enabled
1226 uint8_t, // assertionEnabledLsb
1227 uint8_t, // assertionEnabledMsb
1228 uint8_t, // deassertionEnabledLsb
1229 uint8_t> // deassertionEnabledMsb
1230 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1231{
1232 std::string connection;
1233 std::string path;
1234
1235 uint8_t enabled = 0;
1236 uint8_t assertionEnabledLsb = 0;
1237 uint8_t assertionEnabledMsb = 0;
1238 uint8_t deassertionEnabledLsb = 0;
1239 uint8_t deassertionEnabledMsb = 0;
1240
Chalapathi Venkataramashetty8c2f3c42021-06-13 19:51:17 +00001241 if (sensorNum == reservedSensorNumber)
1242 {
1243 return ipmi::responseInvalidFieldRequest();
1244 }
1245
Willy Tude54f482021-01-26 15:59:09 -08001246 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1247 if (status)
1248 {
1249 return ipmi::response(status);
1250 }
1251
Scron Chang2703b022021-07-06 15:47:45 +08001252#ifdef FEATURE_HYBRID_SENSORS
1253 if (auto sensor = findStaticSensor(path);
1254 sensor != ipmi::sensor::sensors.end() &&
1255 getSensorEventTypeFromPath(path) !=
1256 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1257 {
1258 enabled = static_cast<uint8_t>(
1259 IPMISensorEventEnableByte2::sensorScanningEnable);
1260 uint16_t assertionEnabled = 0;
1261 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1262 ->second.begin()
1263 ->second.second)
1264 {
1265 assertionEnabled |= (1 << offsetValMap.first);
1266 }
1267 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1268 assertionEnabledMsb =
1269 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1270
1271 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1272 assertionEnabledMsb, deassertionEnabledLsb,
1273 deassertionEnabledMsb);
1274 }
1275#endif
1276
Willy Tude54f482021-01-26 15:59:09 -08001277 DbusInterfaceMap sensorMap;
1278 if (!getSensorMap(ctx, connection, path, sensorMap))
1279 {
1280 return ipmi::responseResponseError();
1281 }
1282
1283 auto warningInterface =
1284 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1285 auto criticalInterface =
1286 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1287 if ((warningInterface != sensorMap.end()) ||
1288 (criticalInterface != sensorMap.end()))
1289 {
1290 enabled = static_cast<uint8_t>(
1291 IPMISensorEventEnableByte2::sensorScanningEnable);
1292 if (warningInterface != sensorMap.end())
1293 {
1294 auto& warningMap = warningInterface->second;
1295
1296 auto warningHigh = warningMap.find("WarningHigh");
1297 auto warningLow = warningMap.find("WarningLow");
1298 if (warningHigh != warningMap.end())
1299 {
1300 assertionEnabledLsb |= static_cast<uint8_t>(
1301 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1302 deassertionEnabledLsb |= static_cast<uint8_t>(
1303 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
1304 }
1305 if (warningLow != warningMap.end())
1306 {
1307 assertionEnabledLsb |= static_cast<uint8_t>(
1308 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1309 deassertionEnabledLsb |= static_cast<uint8_t>(
1310 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
1311 }
1312 }
1313 if (criticalInterface != sensorMap.end())
1314 {
1315 auto& criticalMap = criticalInterface->second;
1316
1317 auto criticalHigh = criticalMap.find("CriticalHigh");
1318 auto criticalLow = criticalMap.find("CriticalLow");
1319
1320 if (criticalHigh != criticalMap.end())
1321 {
1322 assertionEnabledMsb |= static_cast<uint8_t>(
1323 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1324 deassertionEnabledMsb |= static_cast<uint8_t>(
1325 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1326 }
1327 if (criticalLow != criticalMap.end())
1328 {
1329 assertionEnabledLsb |= static_cast<uint8_t>(
1330 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1331 deassertionEnabledLsb |= static_cast<uint8_t>(
1332 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
1333 }
1334 }
1335 }
1336
1337 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1338 assertionEnabledMsb, deassertionEnabledLsb,
1339 deassertionEnabledMsb);
1340}
1341
1342/** @brief implements the get Sensor event status command
1343 * @param sensorNumber - sensor number, FFh = reserved
1344 *
1345 * @returns IPMI completion code plus response data
1346 * - sensorEventStatus - Sensor Event messages state
1347 * - assertions - Assertion event messages
1348 * - deassertions - Deassertion event messages
1349 */
1350ipmi::RspType<uint8_t, // sensorEventStatus
1351 std::bitset<16>, // assertions
1352 std::bitset<16> // deassertion
1353 >
1354 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1355{
1356 if (sensorNum == reservedSensorNumber)
1357 {
1358 return ipmi::responseInvalidFieldRequest();
1359 }
1360
1361 std::string connection;
1362 std::string path;
1363 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1364 if (status)
1365 {
1366 phosphor::logging::log<phosphor::logging::level::ERR>(
1367 "ipmiSenGetSensorEventStatus: Sensor connection Error",
1368 phosphor::logging::entry("SENSOR=%d", sensorNum));
1369 return ipmi::response(status);
1370 }
1371
Scron Chang2703b022021-07-06 15:47:45 +08001372#ifdef FEATURE_HYBRID_SENSORS
1373 if (auto sensor = findStaticSensor(path);
1374 sensor != ipmi::sensor::sensors.end() &&
1375 getSensorEventTypeFromPath(path) !=
1376 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1377 {
1378 auto response = ipmi::sensor::get::mapDbusToAssertion(
1379 sensor->second, path, sensor->second.sensorInterface);
1380 std::bitset<16> assertions;
1381 // deassertions are not used.
1382 std::bitset<16> deassertions = 0;
1383 uint8_t sensorEventStatus;
1384 if (response.readingOrStateUnavailable)
1385 {
1386 sensorEventStatus |= static_cast<uint8_t>(
1387 IPMISensorReadingByte2::readingStateUnavailable);
1388 }
1389 if (response.scanningEnabled)
1390 {
1391 sensorEventStatus |= static_cast<uint8_t>(
1392 IPMISensorReadingByte2::sensorScanningEnable);
1393 }
1394 if (response.allEventMessagesEnabled)
1395 {
1396 sensorEventStatus |= static_cast<uint8_t>(
1397 IPMISensorReadingByte2::eventMessagesEnable);
1398 }
1399 assertions |= response.discreteReadingSensorStates << 8;
1400 assertions |= response.thresholdLevelsStates;
1401 return ipmi::responseSuccess(sensorEventStatus, assertions,
1402 deassertions);
1403 }
1404#endif
1405
Willy Tude54f482021-01-26 15:59:09 -08001406 DbusInterfaceMap sensorMap;
1407 if (!getSensorMap(ctx, connection, path, sensorMap))
1408 {
1409 phosphor::logging::log<phosphor::logging::level::ERR>(
1410 "ipmiSenGetSensorEventStatus: Sensor Mapping Error",
1411 phosphor::logging::entry("SENSOR=%s", path.c_str()));
1412 return ipmi::responseResponseError();
1413 }
Hao Jiangd48c9212021-02-03 15:45:06 -08001414
1415 uint8_t sensorEventStatus =
1416 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1417 std::bitset<16> assertions = 0;
1418 std::bitset<16> deassertions = 0;
1419
1420 // handle VR typed sensor
1421 auto vrInterface = sensorMap.find(sensor::vrInterface);
1422 if (vrInterface != sensorMap.end())
1423 {
1424 if (!sensor::getVrEventStatus(ctx, connection, path,
1425 vrInterface->second, assertions))
1426 {
1427 return ipmi::responseResponseError();
1428 }
1429
1430 // both Event Message and Sensor Scanning are disable for VR.
1431 sensorEventStatus = 0;
1432 return ipmi::responseSuccess(sensorEventStatus, assertions,
1433 deassertions);
1434 }
1435
Willy Tude54f482021-01-26 15:59:09 -08001436 auto warningInterface =
1437 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1438 auto criticalInterface =
1439 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1440
Willy Tude54f482021-01-26 15:59:09 -08001441 std::optional<bool> criticalDeassertHigh =
1442 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1443 std::optional<bool> criticalDeassertLow =
1444 thresholdDeassertMap[path]["CriticalAlarmLow"];
1445 std::optional<bool> warningDeassertHigh =
1446 thresholdDeassertMap[path]["WarningAlarmHigh"];
1447 std::optional<bool> warningDeassertLow =
1448 thresholdDeassertMap[path]["WarningAlarmLow"];
1449
Willy Tude54f482021-01-26 15:59:09 -08001450 if (criticalDeassertHigh && !*criticalDeassertHigh)
1451 {
1452 deassertions.set(static_cast<size_t>(
1453 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1454 }
1455 if (criticalDeassertLow && !*criticalDeassertLow)
1456 {
1457 deassertions.set(static_cast<size_t>(
1458 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1459 }
1460 if (warningDeassertHigh && !*warningDeassertHigh)
1461 {
1462 deassertions.set(static_cast<size_t>(
1463 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1464 }
1465 if (warningDeassertLow && !*warningDeassertLow)
1466 {
1467 deassertions.set(static_cast<size_t>(
1468 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1469 }
1470 if ((warningInterface != sensorMap.end()) ||
1471 (criticalInterface != sensorMap.end()))
1472 {
1473 sensorEventStatus = static_cast<size_t>(
1474 IPMISensorEventEnableByte2::eventMessagesEnable);
1475 if (warningInterface != sensorMap.end())
1476 {
1477 auto& warningMap = warningInterface->second;
1478
1479 auto warningHigh = warningMap.find("WarningAlarmHigh");
1480 auto warningLow = warningMap.find("WarningAlarmLow");
1481 auto warningHighAlarm = false;
1482 auto warningLowAlarm = false;
1483
1484 if (warningHigh != warningMap.end())
1485 {
1486 warningHighAlarm = std::get<bool>(warningHigh->second);
1487 }
1488 if (warningLow != warningMap.end())
1489 {
1490 warningLowAlarm = std::get<bool>(warningLow->second);
1491 }
1492 if (warningHighAlarm)
1493 {
1494 assertions.set(
1495 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1496 upperNonCriticalGoingHigh));
1497 }
1498 if (warningLowAlarm)
1499 {
1500 assertions.set(
1501 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1502 lowerNonCriticalGoingLow));
1503 }
1504 }
1505 if (criticalInterface != sensorMap.end())
1506 {
1507 auto& criticalMap = criticalInterface->second;
1508
1509 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1510 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1511 auto criticalHighAlarm = false;
1512 auto criticalLowAlarm = false;
1513
1514 if (criticalHigh != criticalMap.end())
1515 {
1516 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1517 }
1518 if (criticalLow != criticalMap.end())
1519 {
1520 criticalLowAlarm = std::get<bool>(criticalLow->second);
1521 }
1522 if (criticalHighAlarm)
1523 {
1524 assertions.set(
1525 static_cast<size_t>(IPMIGetSensorEventEnableThresholds::
1526 upperCriticalGoingHigh));
1527 }
1528 if (criticalLowAlarm)
1529 {
1530 assertions.set(static_cast<size_t>(
1531 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1532 }
1533 }
1534 }
1535
1536 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1537}
1538
Willy Tu38e7a2b2021-03-29 15:09:56 -07001539// Construct a type 1 SDR for threshold sensor.
Hao Jiange39d4d82021-04-16 17:02:40 -07001540void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1541 get_sdr::SensorDataFullRecord& record)
Willy Tude54f482021-01-26 15:59:09 -08001542{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001543 get_sdr::header::set_record_id(
1544 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1545
Willy Tu38e7a2b2021-03-29 15:09:56 -07001546 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1547 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1548
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001549 record.header.sdr_version = ipmiSdrVersion;
1550 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1551 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1552 sizeof(get_sdr::SensorDataRecordHeader);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001553 record.key.owner_id = bmcI2CAddr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001554 record.key.owner_lun = lun;
1555 record.key.sensor_number = sensornumber;
Hao Jiange39d4d82021-04-16 17:02:40 -07001556}
1557bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1558 uint16_t recordID, const std::string& service,
1559 const std::string& path,
1560 get_sdr::SensorDataFullRecord& record)
1561{
1562 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1563 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1564
1565 DbusInterfaceMap sensorMap;
1566 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1567 {
1568 phosphor::logging::log<phosphor::logging::level::ERR>(
1569 "Failed to update sensor map for threshold sensor",
1570 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1571 phosphor::logging::entry("PATH=%s", path.c_str()));
1572 return false;
1573 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001574
1575 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1576 record.body.sensor_type = getSensorTypeFromPath(path);
1577 std::string type = getSensorTypeStringFromPath(path);
1578 auto typeCstr = type.c_str();
1579 auto findUnits = sensorUnits.find(typeCstr);
1580 if (findUnits != sensorUnits.end())
1581 {
1582 record.body.sensor_units_2_base =
1583 static_cast<uint8_t>(findUnits->second);
1584 } // else default 0x0 unspecified
1585
1586 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1587
Hao Jiangd2afd052020-12-10 15:09:32 -08001588 auto sensorObject = sensorMap.find(sensor::sensorInterface);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001589 if (sensorObject == sensorMap.end())
1590 {
1591 phosphor::logging::log<phosphor::logging::level::ERR>(
1592 "getSensorDataRecord: sensorObject error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001593 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001594 }
1595
1596 uint8_t entityId = 0;
1597 uint8_t entityInstance = 0x01;
1598
1599 // follow the association chain to get the parent board's entityid and
1600 // entityInstance
1601 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance);
1602
1603 record.body.entity_id = entityId;
1604 record.body.entity_instance = entityInstance;
1605
Shakeeb Pasha93889722021-10-14 10:20:13 +05301606 double max = 0;
1607 double min = 0;
1608 getSensorMaxMin(sensorMap, max, min);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001609
1610 int16_t mValue = 0;
1611 int8_t rExp = 0;
1612 int16_t bValue = 0;
1613 int8_t bExp = 0;
1614 bool bSigned = false;
1615
1616 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1617 {
1618 phosphor::logging::log<phosphor::logging::level::ERR>(
1619 "getSensorDataRecord: getSensorAttributes error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001620 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001621 }
1622
1623 // The record.body is a struct SensorDataFullRecordBody
1624 // from sensorhandler.hpp in phosphor-ipmi-host.
1625 // The meaning of these bits appears to come from
1626 // table 43.1 of the IPMI spec.
1627 // The above 5 sensor attributes are stuffed in as follows:
1628 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1629 // Byte 22-24 are for other purposes
1630 // Byte 25 = MMMMMMMM = LSB of M
1631 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1632 // Byte 27 = BBBBBBBB = LSB of B
1633 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1634 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1635 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1636
1637 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1638 record.body.m_lsb = mValue & 0xFF;
1639
1640 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1641 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1642
1643 // move the smallest bit of the MSB into place (bit 9)
1644 // the MSbs are bits 7:8 in m_msb_and_tolerance
1645 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1646
1647 record.body.b_lsb = bValue & 0xFF;
1648
1649 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1650 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1651
1652 // move the smallest bit of the MSB into place (bit 9)
1653 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1654 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1655
1656 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1657 uint8_t rExpBits = rExp & 0x07;
1658
1659 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1660 uint8_t bExpBits = bExp & 0x07;
1661
1662 // move rExp and bExp into place
1663 record.body.r_b_exponents =
1664 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1665
1666 // Set the analog reading byte interpretation accordingly
1667 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1668
1669 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1670 // These seem redundant, but derivable from the above 5 attributes
1671 // Original comment said "todo fill out rest of units"
1672
1673 // populate sensor name from path
Willy Tu38e7a2b2021-03-29 15:09:56 -07001674 auto name = sensor::parseSdrIdFromPath(path);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001675 record.body.id_string_info = name.size();
1676 std::strncpy(record.body.id_string, name.c_str(),
1677 sizeof(record.body.id_string));
1678
Josh Lehana55c9532020-10-28 21:59:06 -07001679 // Remember the sensor name, as determined for this sensor number
1680 details::sdrStatsTable.updateName(sensornumber, name);
1681
Jie Yangf0a89942021-07-29 15:30:25 -07001682 bool sensorSettable = false;
1683 auto mutability =
1684 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1685 if (mutability != sensorMap.end())
1686 {
1687 sensorSettable =
1688 mappedVariant<bool>(mutability->second, "Mutable", false);
1689 }
1690 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1691
1692 // Grant write permission to sensors deemed externally settable
1693 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable);
Willy Tu530e2772021-07-02 14:42:06 -07001694
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001695 IPMIThresholds thresholdData;
1696 try
1697 {
1698 thresholdData = getIPMIThresholds(sensorMap);
1699 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001700 catch (const std::exception&)
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001701 {
1702 phosphor::logging::log<phosphor::logging::level::ERR>(
1703 "getSensorDataRecord: getIPMIThresholds error");
Willy Tu38e7a2b2021-03-29 15:09:56 -07001704 return false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08001705 }
1706
1707 if (thresholdData.criticalHigh)
1708 {
1709 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1710 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1711 IPMISensorEventEnableThresholds::criticalThreshold);
1712 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1713 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1714 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1715 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1716 record.body.discrete_reading_setting_mask[0] |=
1717 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1718 }
1719 if (thresholdData.warningHigh)
1720 {
1721 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1722 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1723 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1724 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1725 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1726 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1727 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1728 record.body.discrete_reading_setting_mask[0] |=
1729 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1730 }
1731 if (thresholdData.criticalLow)
1732 {
1733 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1734 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1735 IPMISensorEventEnableThresholds::criticalThreshold);
1736 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1737 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1738 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1739 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1740 record.body.discrete_reading_setting_mask[0] |=
1741 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1742 }
1743 if (thresholdData.warningLow)
1744 {
1745 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1746 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1747 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1748 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1749 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1750 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1751 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1752 record.body.discrete_reading_setting_mask[0] |=
1753 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1754 }
1755
1756 // everything that is readable is setable
1757 record.body.discrete_reading_setting_mask[1] =
1758 record.body.discrete_reading_setting_mask[0];
Willy Tu38e7a2b2021-03-29 15:09:56 -07001759 return true;
1760}
1761
Scron Chang2703b022021-07-06 15:47:45 +08001762#ifdef FEATURE_HYBRID_SENSORS
1763// Construct a type 1 SDR for discrete Sensor typed sensor.
1764void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1765 uint16_t recordID,
1766 ipmi::sensor::IdInfoMap::const_iterator sensor,
1767 get_sdr::SensorDataFullRecord& record)
1768{
1769 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1770
1771 record.body.entity_id = sensor->second.entityType;
1772 record.body.sensor_type = sensor->second.sensorType;
1773 record.body.event_reading_type = sensor->second.sensorReadingType;
1774 record.body.entity_instance = sensor->second.instance;
1775 if (ipmi::sensor::Mutability::Write ==
1776 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1777 {
1778 get_sdr::body::init_settable_state(true, &(record.body));
1779 }
1780
1781 auto id_string = sensor->second.sensorName;
1782
1783 if (id_string.empty())
1784 {
1785 id_string = sensor->second.sensorNameFunc(sensor->second);
1786 }
1787
1788 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1789 {
1790 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
1791 &(record.body));
1792 }
1793 else
1794 {
1795 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
1796 }
1797 std::strncpy(record.body.id_string, id_string.c_str(),
1798 get_sdr::body::get_id_strlen(&(record.body)));
1799}
1800#endif
1801
Hao Jiange39d4d82021-04-16 17:02:40 -07001802// Construct type 3 SDR header and key (for VR and other discrete sensors)
1803void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1804 get_sdr::SensorDataEventRecord& record)
Willy Tu61992ad2021-03-29 15:33:20 -07001805{
1806 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1807 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1808
1809 get_sdr::header::set_record_id(
1810 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1811
1812 record.header.sdr_version = ipmiSdrVersion;
1813 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
1814 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
1815 sizeof(get_sdr::SensorDataRecordHeader);
1816 record.key.owner_id = bmcI2CAddr;
1817 record.key.owner_lun = lun;
1818 record.key.sensor_number = sensornumber;
1819
1820 record.body.entity_id = 0x00;
1821 record.body.entity_instance = 0x01;
Hao Jiange39d4d82021-04-16 17:02:40 -07001822}
Willy Tu61992ad2021-03-29 15:33:20 -07001823
Hao Jiange39d4d82021-04-16 17:02:40 -07001824// Construct a type 3 SDR for VR typed sensor(daemon).
1825bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum,
1826 uint16_t recordID, const std::string& service,
1827 const std::string& path,
1828 get_sdr::SensorDataEventRecord& record)
1829{
1830 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1831 constructEventSdrHeaderKey(sensorNum, recordID, record);
1832
1833 DbusInterfaceMap sensorMap;
1834 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1835 {
1836 phosphor::logging::log<phosphor::logging::level::ERR>(
1837 "Failed to update sensor map for VR sensor",
1838 phosphor::logging::entry("SERVICE=%s", service.c_str()),
1839 phosphor::logging::entry("PATH=%s", path.c_str()));
1840 return false;
1841 }
Willy Tu61992ad2021-03-29 15:33:20 -07001842 // follow the association chain to get the parent board's entityid and
1843 // entityInstance
1844 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id,
1845 record.body.entity_instance);
1846
1847 // Sensor type is hardcoded as a module/board type instead of parsing from
1848 // sensor path. This is because VR control is allocated in an independent
1849 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
1850 // types.
1851 static constexpr const uint8_t module_board_type = 0x15;
1852 record.body.sensor_type = module_board_type;
1853 record.body.event_reading_type = 0x00;
1854
1855 record.body.sensor_record_sharing_1 = 0x00;
1856 record.body.sensor_record_sharing_2 = 0x00;
1857
1858 // populate sensor name from path
1859 auto name = sensor::parseSdrIdFromPath(path);
1860 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
1861 record.body.id_string_info = nameSize;
1862 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
1863 std::memcpy(record.body.id_string, name.c_str(), nameSize);
1864
1865 // Remember the sensor name, as determined for this sensor number
1866 details::sdrStatsTable.updateName(sensornumber, name);
Hao Jiange39d4d82021-04-16 17:02:40 -07001867
1868 return true;
Willy Tu61992ad2021-03-29 15:33:20 -07001869}
1870
Hao Jiange39d4d82021-04-16 17:02:40 -07001871static int
1872 getSensorDataRecord(ipmi::Context::ptr ctx,
1873 std::vector<uint8_t>& recordData, uint16_t recordID,
1874 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001875{
1876 size_t fruCount = 0;
1877 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
1878 if (ret != ipmi::ccSuccess)
1879 {
1880 phosphor::logging::log<phosphor::logging::level::ERR>(
1881 "getSensorDataRecord: getFruSdrCount error");
1882 return GENERAL_ERROR;
1883 }
1884
1885 auto& sensorTree = getSensorTree();
1886 size_t lastRecord =
1887 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1;
1888 if (recordID == lastRecordIndex)
1889 {
1890 recordID = lastRecord;
1891 }
1892 if (recordID > lastRecord)
1893 {
1894 phosphor::logging::log<phosphor::logging::level::ERR>(
1895 "getSensorDataRecord: recordID > lastRecord error");
1896 return GENERAL_ERROR;
1897 }
1898
1899 if (recordID >= sensorTree.size())
1900 {
1901 size_t fruIndex = recordID - sensorTree.size();
1902
1903 if (fruIndex >= fruCount)
1904 {
1905 // handle type 12 hardcoded records
1906 size_t type12Index = fruIndex - fruCount;
1907 if (type12Index >= ipmi::storage::type12Count)
1908 {
1909 phosphor::logging::log<phosphor::logging::level::ERR>(
1910 "getSensorDataRecord: type12Index error");
1911 return GENERAL_ERROR;
1912 }
1913 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
1914 }
1915 else
1916 {
1917 // handle fru records
1918 get_sdr::SensorDataFruRecord data;
1919 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data);
1920 if (ret != IPMI_CC_OK)
1921 {
1922 return GENERAL_ERROR;
1923 }
1924 data.header.record_id_msb = recordID >> 8;
1925 data.header.record_id_lsb = recordID & 0xFF;
1926 recordData.insert(recordData.end(), (uint8_t*)&data,
1927 ((uint8_t*)&data) + sizeof(data));
1928 }
1929
1930 return 0;
1931 }
1932
1933 std::string connection;
1934 std::string path;
Hao Jiange39d4d82021-04-16 17:02:40 -07001935 std::vector<std::string> interfaces;
1936
1937 auto status =
1938 getSensorConnection(ctx, recordID, connection, path, &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001939 if (status)
1940 {
1941 phosphor::logging::log<phosphor::logging::level::ERR>(
1942 "getSensorDataRecord: getSensorConnection error");
1943 return GENERAL_ERROR;
1944 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001945 uint16_t sensorNum = getSensorNumberFromPath(path);
1946 if (sensorNum == invalidSensorNumber)
1947 {
1948 phosphor::logging::log<phosphor::logging::level::ERR>(
1949 "getSensorDataRecord: invalidSensorNumber");
1950 return GENERAL_ERROR;
1951 }
1952
Willy Tu38e7a2b2021-03-29 15:09:56 -07001953 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001954 if (std::find(interfaces.begin(), interfaces.end(),
1955 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001956 {
1957 get_sdr::SensorDataFullRecord record = {0};
1958
Hao Jiange39d4d82021-04-16 17:02:40 -07001959 // If the request doesn't read SDR body, construct only header and key
1960 // part to avoid additional DBus transaction.
1961 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1962 {
1963 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1964 }
1965 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1966 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001967 {
1968 return GENERAL_ERROR;
1969 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001970
Willy Tu38e7a2b2021-03-29 15:09:56 -07001971 recordData.insert(recordData.end(), (uint8_t*)&record,
1972 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001973
1974 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07001975 }
Willy Tu61992ad2021-03-29 15:33:20 -07001976
Scron Chang2703b022021-07-06 15:47:45 +08001977#ifdef FEATURE_HYBRID_SENSORS
1978 if (auto sensor = findStaticSensor(path);
1979 sensor != ipmi::sensor::sensors.end() &&
1980 getSensorEventTypeFromPath(path) !=
1981 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1982 {
1983 get_sdr::SensorDataFullRecord record = {0};
1984
1985 // If the request doesn't read SDR body, construct only header and key
1986 // part to avoid additional DBus transaction.
1987 if (readBytes <= sizeof(record.header) + sizeof(record.key))
1988 {
1989 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1990 }
1991 else
1992 {
1993 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
1994 }
1995
1996 recordData.insert(recordData.end(), (uint8_t*)&record,
1997 ((uint8_t*)&record) + sizeof(record));
1998
1999 return 0;
2000 }
2001#endif
2002
Willy Tu61992ad2021-03-29 15:33:20 -07002003 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002004 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2005 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002006 {
2007 get_sdr::SensorDataEventRecord 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 constructEventSdrHeaderKey(sensorNum, recordID, record);
2014 }
2015 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2016 record))
2017 {
2018 return GENERAL_ERROR;
2019 }
Willy Tu61992ad2021-03-29 15:33:20 -07002020 recordData.insert(recordData.end(), (uint8_t*)&record,
2021 ((uint8_t*)&record) + sizeof(record));
2022 }
2023
Willy Tude54f482021-01-26 15:59:09 -08002024 return 0;
2025}
2026
2027/** @brief implements the get SDR Info command
2028 * @param count - Operation
2029 *
2030 * @returns IPMI completion code plus response data
2031 * - sdrCount - sensor/SDR count
2032 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2033 */
2034static ipmi::RspType<uint8_t, // respcount
2035 uint8_t, // dynamic population flags
2036 uint32_t // last time a sensor was added
2037 >
2038 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2039 std::optional<uint8_t> count)
2040{
2041 auto& sensorTree = getSensorTree();
2042 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002043 uint16_t recordID = 0;
2044 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002045 // Sensors are dynamically allocated, and there is at least one LUN
2046 uint8_t lunsAndDynamicPopulation = 0x80;
2047 constexpr uint8_t getSdrCount = 0x01;
2048 constexpr uint8_t getSensorCount = 0x00;
2049
2050 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2051 {
2052 return ipmi::responseResponseError();
2053 }
Willy Tude54f482021-01-26 15:59:09 -08002054 uint16_t numSensors = sensorTree.size();
2055 if (count.value_or(0) == getSdrCount)
2056 {
2057 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002058 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002059 {
2060 get_sdr::SensorDataRecordHeader* hdr =
2061 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002062 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002063 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2064 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002065 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002066 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002067 record.data());
2068 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002069 {
2070 sdrCount++;
2071 }
2072 }
2073 }
2074 }
2075 else if (count.value_or(0) == getSensorCount)
2076 {
2077 // Return the number of sensors attached to the LUN
2078 if ((ctx->lun == 0) && (numSensors > 0))
2079 {
2080 sdrCount =
2081 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2082 }
2083 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2084 {
2085 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2086 ? maxSensorsPerLUN
2087 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2088 }
2089 else if (ctx->lun == 3)
2090 {
2091 if (numSensors <= maxIPMISensors)
2092 {
2093 sdrCount =
2094 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2095 }
2096 else
2097 {
2098 // error
2099 throw std::out_of_range(
2100 "Maximum number of IPMI sensors exceeded.");
2101 }
2102 }
2103 }
2104 else
2105 {
2106 return ipmi::responseInvalidFieldRequest();
2107 }
2108
2109 // Get Sensor count. This returns the number of sensors
2110 if (numSensors > 0)
2111 {
2112 lunsAndDynamicPopulation |= 1;
2113 }
2114 if (numSensors > maxSensorsPerLUN)
2115 {
2116 lunsAndDynamicPopulation |= 2;
2117 }
2118 if (numSensors >= (maxSensorsPerLUN * 2))
2119 {
2120 lunsAndDynamicPopulation |= 8;
2121 }
2122 if (numSensors > maxIPMISensors)
2123 {
2124 // error
2125 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2126 }
2127
2128 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2129 sdrLastAdd);
2130}
2131
2132/* end sensor commands */
2133
2134/* storage commands */
2135
2136ipmi::RspType<uint8_t, // sdr version
2137 uint16_t, // record count
2138 uint16_t, // free space
2139 uint32_t, // most recent addition
2140 uint32_t, // most recent erase
2141 uint8_t // operationSupport
2142 >
2143 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2144{
2145 auto& sensorTree = getSensorTree();
2146 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002147 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002148 {
2149 return ipmi::responseResponseError();
2150 }
2151
2152 size_t fruCount = 0;
2153 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2154 if (ret != ipmi::ccSuccess)
2155 {
2156 return ipmi::response(ret);
2157 }
2158
2159 uint16_t recordCount =
2160 sensorTree.size() + fruCount + ipmi::storage::type12Count;
2161
2162 uint8_t operationSupport = static_cast<uint8_t>(
2163 SdrRepositoryInfoOps::overflow); // write not supported
2164
2165 operationSupport |=
2166 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2167 operationSupport |= static_cast<uint8_t>(
2168 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2169 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2170 unspecifiedFreeSpace, sdrLastAdd,
2171 sdrLastRemove, operationSupport);
2172}
2173
2174/** @brief implements the get SDR allocation info command
2175 *
2176 * @returns IPMI completion code plus response data
2177 * - allocUnits - Number of possible allocation units
2178 * - allocUnitSize - Allocation unit size in bytes.
2179 * - allocUnitFree - Number of free allocation units
2180 * - allocUnitLargestFree - Largest free block in allocation units
2181 * - maxRecordSize - Maximum record size in allocation units.
2182 */
2183ipmi::RspType<uint16_t, // allocUnits
2184 uint16_t, // allocUnitSize
2185 uint16_t, // allocUnitFree
2186 uint16_t, // allocUnitLargestFree
2187 uint8_t // maxRecordSize
2188 >
2189 ipmiStorageGetSDRAllocationInfo()
2190{
2191 // 0000h unspecified number of alloc units
2192 constexpr uint16_t allocUnits = 0;
2193
2194 constexpr uint16_t allocUnitFree = 0;
2195 constexpr uint16_t allocUnitLargestFree = 0;
2196 // only allow one block at a time
2197 constexpr uint8_t maxRecordSize = 1;
2198
2199 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2200 allocUnitLargestFree, maxRecordSize);
2201}
2202
2203/** @brief implements the reserve SDR command
2204 * @returns IPMI completion code plus response data
2205 * - sdrReservationID
2206 */
2207ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2208{
2209 sdrReservationID++;
2210 if (sdrReservationID == 0)
2211 {
2212 sdrReservationID++;
2213 }
2214
2215 return ipmi::responseSuccess(sdrReservationID);
2216}
2217
2218ipmi::RspType<uint16_t, // next record ID
2219 std::vector<uint8_t> // payload
2220 >
2221 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2222 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2223{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002224 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002225 // reservation required for partial reads with non zero offset into
2226 // record
2227 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2228 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002229 phosphor::logging::log<phosphor::logging::level::ERR>(
2230 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002231 return ipmi::responseInvalidReservationId();
2232 }
Willy Tude54f482021-01-26 15:59:09 -08002233 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2234 if (ret != ipmi::ccSuccess)
2235 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002236 phosphor::logging::log<phosphor::logging::level::ERR>(
2237 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002238 return ipmi::response(ret);
2239 }
2240
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002241 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002242 size_t lastRecord =
2243 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002244 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2245
2246 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002247 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002248 phosphor::logging::log<phosphor::logging::level::ERR>(
2249 "ipmiStorageGetSDR: getSensorSubtree error");
2250 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002251 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002252
2253 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002254 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002255 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002256 phosphor::logging::log<phosphor::logging::level::ERR>(
2257 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002258 return ipmi::responseInvalidFieldRequest();
2259 }
Willy Tude54f482021-01-26 15:59:09 -08002260 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002261 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002262 if (!hdr)
2263 {
2264 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002265 "ipmiStorageGetSDR: record header is null");
2266 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002267 }
2268
2269 size_t sdrLength =
2270 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2271 if (sdrLength < (offset + bytesToRead))
2272 {
2273 bytesToRead = sdrLength - offset;
2274 }
2275
2276 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2277 if (!respStart)
2278 {
2279 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002280 "ipmiStorageGetSDR: record is null");
2281 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002282 }
2283
2284 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002285
Willy Tude54f482021-01-26 15:59:09 -08002286 return ipmi::responseSuccess(nextRecordId, recordData);
2287}
2288/* end storage commands */
2289
2290void registerSensorFunctions()
2291{
2292 // <Platform Event>
2293 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2294 ipmi::sensor_event::cmdPlatformEvent,
2295 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2296
Willy Tudbafbce2021-03-29 00:37:05 -07002297 // <Set Sensor Reading and Event Status>
2298 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2299 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2300 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002301
Willy Tude54f482021-01-26 15:59:09 -08002302 // <Get Sensor Reading>
2303 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2304 ipmi::sensor_event::cmdGetSensorReading,
2305 ipmi::Privilege::User, ipmiSenGetSensorReading);
2306
2307 // <Get Sensor Threshold>
2308 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2309 ipmi::sensor_event::cmdGetSensorThreshold,
2310 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2311
2312 // <Set Sensor Threshold>
2313 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2314 ipmi::sensor_event::cmdSetSensorThreshold,
2315 ipmi::Privilege::Operator,
2316 ipmiSenSetSensorThresholds);
2317
2318 // <Get Sensor Event Enable>
2319 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2320 ipmi::sensor_event::cmdGetSensorEventEnable,
2321 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2322
2323 // <Get Sensor Event Status>
2324 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2325 ipmi::sensor_event::cmdGetSensorEventStatus,
2326 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2327
2328 // register all storage commands for both Sensor and Storage command
2329 // versions
2330
2331 // <Get SDR Repository Info>
2332 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2333 ipmi::storage::cmdGetSdrRepositoryInfo,
2334 ipmi::Privilege::User,
2335 ipmiStorageGetSDRRepositoryInfo);
2336
2337 // <Get Device SDR Info>
2338 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2339 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2340 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2341
2342 // <Get SDR Allocation Info>
2343 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2344 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2345 ipmi::Privilege::User,
2346 ipmiStorageGetSDRAllocationInfo);
2347
2348 // <Reserve SDR Repo>
2349 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2350 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2351 ipmi::Privilege::User, ipmiStorageReserveSDR);
2352
2353 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2354 ipmi::storage::cmdReserveSdrRepository,
2355 ipmi::Privilege::User, ipmiStorageReserveSDR);
2356
2357 // <Get Sdr>
2358 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2359 ipmi::sensor_event::cmdGetDeviceSdr,
2360 ipmi::Privilege::User, ipmiStorageGetSDR);
2361
2362 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2363 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2364 ipmiStorageGetSDR);
2365}
2366} // namespace ipmi