blob: 7b8895821ad55efdf1a26f802292520543556eab [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;
Johnathan Manteyce982772021-07-28 15:08:30 -07001936 uint16_t sensNumFromRecID{recordID};
1937 if ((recordID >= reservedSensorNumber) &&
1938 (recordID < (2 * maxSensorsPerLUN)))
1939 {
1940 sensNumFromRecID = (recordID + 1) & maxSensorsPerLUN;
1941 ctx->lun = 1;
1942 }
1943 else if ((recordID >= (2 * maxSensorsPerLUN)) &&
1944 (recordID < maxIPMISensors))
1945 {
1946 sensNumFromRecID = (recordID + 2) & maxSensorsPerLUN;
1947 ctx->lun = 3;
1948 }
1949 else if (recordID >= maxIPMISensors)
1950 {
1951 return GENERAL_ERROR;
1952 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001953
Johnathan Manteyce982772021-07-28 15:08:30 -07001954 auto status = getSensorConnection(ctx, sensNumFromRecID, connection, path,
1955 &interfaces);
Willy Tu38e7a2b2021-03-29 15:09:56 -07001956 if (status)
1957 {
1958 phosphor::logging::log<phosphor::logging::level::ERR>(
1959 "getSensorDataRecord: getSensorConnection error");
1960 return GENERAL_ERROR;
1961 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001962 uint16_t sensorNum = getSensorNumberFromPath(path);
Johnathan Manteyce982772021-07-28 15:08:30 -07001963 if (sensorNum >= maxIPMISensors)
Willy Tu38e7a2b2021-03-29 15:09:56 -07001964 {
1965 phosphor::logging::log<phosphor::logging::level::ERR>(
1966 "getSensorDataRecord: invalidSensorNumber");
1967 return GENERAL_ERROR;
1968 }
Johnathan Manteyce982772021-07-28 15:08:30 -07001969 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1970 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1971
1972 if ((sensornumber != sensNumFromRecID) && (lun != ctx->lun))
1973 {
1974 phosphor::logging::log<phosphor::logging::level::ERR>(
1975 "getSensorDataRecord: sensor record mismatch");
1976 return GENERAL_ERROR;
1977 }
Willy Tu38e7a2b2021-03-29 15:09:56 -07001978
Willy Tu38e7a2b2021-03-29 15:09:56 -07001979 // Construct full record (SDR type 1) for the threshold sensors
Hao Jiange39d4d82021-04-16 17:02:40 -07001980 if (std::find(interfaces.begin(), interfaces.end(),
1981 sensor::sensorInterface) != interfaces.end())
Willy Tu38e7a2b2021-03-29 15:09:56 -07001982 {
1983 get_sdr::SensorDataFullRecord record = {0};
1984
Hao Jiange39d4d82021-04-16 17:02:40 -07001985 // 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 if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path,
1992 record))
Willy Tu38e7a2b2021-03-29 15:09:56 -07001993 {
1994 return GENERAL_ERROR;
1995 }
Hao Jiange39d4d82021-04-16 17:02:40 -07001996
Willy Tu38e7a2b2021-03-29 15:09:56 -07001997 recordData.insert(recordData.end(), (uint8_t*)&record,
1998 ((uint8_t*)&record) + sizeof(record));
Willy Tu61992ad2021-03-29 15:33:20 -07001999
2000 return 0;
Willy Tu38e7a2b2021-03-29 15:09:56 -07002001 }
Willy Tu61992ad2021-03-29 15:33:20 -07002002
Scron Chang2703b022021-07-06 15:47:45 +08002003#ifdef FEATURE_HYBRID_SENSORS
2004 if (auto sensor = findStaticSensor(path);
2005 sensor != ipmi::sensor::sensors.end() &&
2006 getSensorEventTypeFromPath(path) !=
2007 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2008 {
2009 get_sdr::SensorDataFullRecord record = {0};
2010
2011 // If the request doesn't read SDR body, construct only header and key
2012 // part to avoid additional DBus transaction.
2013 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2014 {
2015 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2016 }
2017 else
2018 {
2019 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2020 }
2021
2022 recordData.insert(recordData.end(), (uint8_t*)&record,
2023 ((uint8_t*)&record) + sizeof(record));
2024
2025 return 0;
2026 }
2027#endif
2028
Willy Tu61992ad2021-03-29 15:33:20 -07002029 // Contruct SDR type 3 record for VR sensor (daemon)
Hao Jiange39d4d82021-04-16 17:02:40 -07002030 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2031 interfaces.end())
Willy Tu61992ad2021-03-29 15:33:20 -07002032 {
2033 get_sdr::SensorDataEventRecord record = {0};
2034
Hao Jiange39d4d82021-04-16 17:02:40 -07002035 // 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 constructEventSdrHeaderKey(sensorNum, recordID, record);
2040 }
2041 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path,
2042 record))
2043 {
2044 return GENERAL_ERROR;
2045 }
Willy Tu61992ad2021-03-29 15:33:20 -07002046 recordData.insert(recordData.end(), (uint8_t*)&record,
2047 ((uint8_t*)&record) + sizeof(record));
2048 }
2049
Willy Tude54f482021-01-26 15:59:09 -08002050 return 0;
2051}
2052
2053/** @brief implements the get SDR Info command
2054 * @param count - Operation
2055 *
2056 * @returns IPMI completion code plus response data
2057 * - sdrCount - sensor/SDR count
2058 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2059 */
2060static ipmi::RspType<uint8_t, // respcount
2061 uint8_t, // dynamic population flags
2062 uint32_t // last time a sensor was added
2063 >
2064 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2065 std::optional<uint8_t> count)
2066{
2067 auto& sensorTree = getSensorTree();
2068 uint8_t sdrCount = 0;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002069 uint16_t recordID = 0;
2070 std::vector<uint8_t> record;
Willy Tude54f482021-01-26 15:59:09 -08002071 // Sensors are dynamically allocated, and there is at least one LUN
2072 uint8_t lunsAndDynamicPopulation = 0x80;
2073 constexpr uint8_t getSdrCount = 0x01;
2074 constexpr uint8_t getSensorCount = 0x00;
2075
2076 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2077 {
2078 return ipmi::responseResponseError();
2079 }
Willy Tude54f482021-01-26 15:59:09 -08002080 uint16_t numSensors = sensorTree.size();
2081 if (count.value_or(0) == getSdrCount)
2082 {
2083 // Count the number of Type 1 SDR entries assigned to the LUN
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002084 while (!getSensorDataRecord(ctx, record, recordID++))
Willy Tude54f482021-01-26 15:59:09 -08002085 {
2086 get_sdr::SensorDataRecordHeader* hdr =
2087 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002088 record.data());
Willy Tude54f482021-01-26 15:59:09 -08002089 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD)
2090 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002091 get_sdr::SensorDataFullRecord* recordData =
Willy Tude54f482021-01-26 15:59:09 -08002092 reinterpret_cast<get_sdr::SensorDataFullRecord*>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002093 record.data());
2094 if (ctx->lun == recordData->key.owner_lun)
Willy Tude54f482021-01-26 15:59:09 -08002095 {
2096 sdrCount++;
2097 }
2098 }
2099 }
2100 }
2101 else if (count.value_or(0) == getSensorCount)
2102 {
2103 // Return the number of sensors attached to the LUN
2104 if ((ctx->lun == 0) && (numSensors > 0))
2105 {
2106 sdrCount =
2107 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2108 }
2109 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN))
2110 {
2111 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2112 ? maxSensorsPerLUN
2113 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2114 }
2115 else if (ctx->lun == 3)
2116 {
2117 if (numSensors <= maxIPMISensors)
2118 {
2119 sdrCount =
2120 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN;
2121 }
2122 else
2123 {
2124 // error
2125 throw std::out_of_range(
2126 "Maximum number of IPMI sensors exceeded.");
2127 }
2128 }
2129 }
2130 else
2131 {
2132 return ipmi::responseInvalidFieldRequest();
2133 }
2134
2135 // Get Sensor count. This returns the number of sensors
2136 if (numSensors > 0)
2137 {
2138 lunsAndDynamicPopulation |= 1;
2139 }
2140 if (numSensors > maxSensorsPerLUN)
2141 {
2142 lunsAndDynamicPopulation |= 2;
2143 }
2144 if (numSensors >= (maxSensorsPerLUN * 2))
2145 {
2146 lunsAndDynamicPopulation |= 8;
2147 }
2148 if (numSensors > maxIPMISensors)
2149 {
2150 // error
2151 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2152 }
2153
2154 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2155 sdrLastAdd);
2156}
2157
2158/* end sensor commands */
2159
2160/* storage commands */
2161
2162ipmi::RspType<uint8_t, // sdr version
2163 uint16_t, // record count
2164 uint16_t, // free space
2165 uint32_t, // most recent addition
2166 uint32_t, // most recent erase
2167 uint8_t // operationSupport
2168 >
2169 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2170{
2171 auto& sensorTree = getSensorTree();
2172 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002173 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002174 {
2175 return ipmi::responseResponseError();
2176 }
2177
2178 size_t fruCount = 0;
2179 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2180 if (ret != ipmi::ccSuccess)
2181 {
2182 return ipmi::response(ret);
2183 }
2184
2185 uint16_t recordCount =
2186 sensorTree.size() + fruCount + ipmi::storage::type12Count;
2187
2188 uint8_t operationSupport = static_cast<uint8_t>(
2189 SdrRepositoryInfoOps::overflow); // write not supported
2190
2191 operationSupport |=
2192 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2193 operationSupport |= static_cast<uint8_t>(
2194 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2195 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2196 unspecifiedFreeSpace, sdrLastAdd,
2197 sdrLastRemove, operationSupport);
2198}
2199
2200/** @brief implements the get SDR allocation info command
2201 *
2202 * @returns IPMI completion code plus response data
2203 * - allocUnits - Number of possible allocation units
2204 * - allocUnitSize - Allocation unit size in bytes.
2205 * - allocUnitFree - Number of free allocation units
2206 * - allocUnitLargestFree - Largest free block in allocation units
2207 * - maxRecordSize - Maximum record size in allocation units.
2208 */
2209ipmi::RspType<uint16_t, // allocUnits
2210 uint16_t, // allocUnitSize
2211 uint16_t, // allocUnitFree
2212 uint16_t, // allocUnitLargestFree
2213 uint8_t // maxRecordSize
2214 >
2215 ipmiStorageGetSDRAllocationInfo()
2216{
2217 // 0000h unspecified number of alloc units
2218 constexpr uint16_t allocUnits = 0;
2219
2220 constexpr uint16_t allocUnitFree = 0;
2221 constexpr uint16_t allocUnitLargestFree = 0;
2222 // only allow one block at a time
2223 constexpr uint8_t maxRecordSize = 1;
2224
2225 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2226 allocUnitLargestFree, maxRecordSize);
2227}
2228
2229/** @brief implements the reserve SDR command
2230 * @returns IPMI completion code plus response data
2231 * - sdrReservationID
2232 */
2233ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2234{
2235 sdrReservationID++;
2236 if (sdrReservationID == 0)
2237 {
2238 sdrReservationID++;
2239 }
2240
2241 return ipmi::responseSuccess(sdrReservationID);
2242}
2243
2244ipmi::RspType<uint16_t, // next record ID
2245 std::vector<uint8_t> // payload
2246 >
2247 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2248 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2249{
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002250 size_t fruCount = 0;
Willy Tude54f482021-01-26 15:59:09 -08002251 // reservation required for partial reads with non zero offset into
2252 // record
2253 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2254 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002255 phosphor::logging::log<phosphor::logging::level::ERR>(
2256 "ipmiStorageGetSDR: responseInvalidReservationId");
Willy Tude54f482021-01-26 15:59:09 -08002257 return ipmi::responseInvalidReservationId();
2258 }
Willy Tude54f482021-01-26 15:59:09 -08002259 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
2260 if (ret != ipmi::ccSuccess)
2261 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002262 phosphor::logging::log<phosphor::logging::level::ERR>(
2263 "ipmiStorageGetSDR: getFruSdrCount error");
Willy Tude54f482021-01-26 15:59:09 -08002264 return ipmi::response(ret);
2265 }
2266
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002267 auto& sensorTree = getSensorTree();
Willy Tude54f482021-01-26 15:59:09 -08002268 size_t lastRecord =
2269 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1;
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002270 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF;
2271
2272 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Willy Tude54f482021-01-26 15:59:09 -08002273 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002274 phosphor::logging::log<phosphor::logging::level::ERR>(
2275 "ipmiStorageGetSDR: getSensorSubtree error");
2276 return ipmi::responseResponseError();
Willy Tude54f482021-01-26 15:59:09 -08002277 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002278
2279 std::vector<uint8_t> record;
Hao Jiange39d4d82021-04-16 17:02:40 -07002280 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead))
Willy Tude54f482021-01-26 15:59:09 -08002281 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002282 phosphor::logging::log<phosphor::logging::level::ERR>(
2283 "ipmiStorageGetSDR: fail to get SDR");
Willy Tude54f482021-01-26 15:59:09 -08002284 return ipmi::responseInvalidFieldRequest();
2285 }
Willy Tude54f482021-01-26 15:59:09 -08002286 get_sdr::SensorDataRecordHeader* hdr =
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002287 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
Willy Tude54f482021-01-26 15:59:09 -08002288 if (!hdr)
2289 {
2290 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002291 "ipmiStorageGetSDR: record header is null");
2292 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002293 }
2294
2295 size_t sdrLength =
2296 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2297 if (sdrLength < (offset + bytesToRead))
2298 {
2299 bytesToRead = sdrLength - offset;
2300 }
2301
2302 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2303 if (!respStart)
2304 {
2305 phosphor::logging::log<phosphor::logging::level::ERR>(
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002306 "ipmiStorageGetSDR: record is null");
2307 return ipmi::responseSuccess(nextRecordId, record);
Willy Tude54f482021-01-26 15:59:09 -08002308 }
2309
2310 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
Kuiying Wanga8b5b262021-02-06 23:38:22 +08002311
Willy Tude54f482021-01-26 15:59:09 -08002312 return ipmi::responseSuccess(nextRecordId, recordData);
2313}
2314/* end storage commands */
2315
2316void registerSensorFunctions()
2317{
2318 // <Platform Event>
2319 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2320 ipmi::sensor_event::cmdPlatformEvent,
2321 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2322
Willy Tudbafbce2021-03-29 00:37:05 -07002323 // <Set Sensor Reading and Event Status>
2324 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2325 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2326 ipmi::Privilege::Operator, ipmiSetSensorReading);
Willy Tudbafbce2021-03-29 00:37:05 -07002327
Willy Tude54f482021-01-26 15:59:09 -08002328 // <Get Sensor Reading>
2329 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2330 ipmi::sensor_event::cmdGetSensorReading,
2331 ipmi::Privilege::User, ipmiSenGetSensorReading);
2332
2333 // <Get Sensor Threshold>
2334 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2335 ipmi::sensor_event::cmdGetSensorThreshold,
2336 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2337
2338 // <Set Sensor Threshold>
2339 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2340 ipmi::sensor_event::cmdSetSensorThreshold,
2341 ipmi::Privilege::Operator,
2342 ipmiSenSetSensorThresholds);
2343
2344 // <Get Sensor Event Enable>
2345 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2346 ipmi::sensor_event::cmdGetSensorEventEnable,
2347 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2348
2349 // <Get Sensor Event Status>
2350 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2351 ipmi::sensor_event::cmdGetSensorEventStatus,
2352 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2353
2354 // register all storage commands for both Sensor and Storage command
2355 // versions
2356
2357 // <Get SDR Repository Info>
2358 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2359 ipmi::storage::cmdGetSdrRepositoryInfo,
2360 ipmi::Privilege::User,
2361 ipmiStorageGetSDRRepositoryInfo);
2362
2363 // <Get Device SDR Info>
2364 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2365 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2366 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2367
2368 // <Get SDR Allocation Info>
2369 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2370 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2371 ipmi::Privilege::User,
2372 ipmiStorageGetSDRAllocationInfo);
2373
2374 // <Reserve SDR Repo>
2375 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2376 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2377 ipmi::Privilege::User, ipmiStorageReserveSDR);
2378
2379 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2380 ipmi::storage::cmdReserveSdrRepository,
2381 ipmi::Privilege::User, ipmiStorageReserveSDR);
2382
2383 // <Get Sdr>
2384 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2385 ipmi::sensor_event::cmdGetDeviceSdr,
2386 ipmi::Privilege::User, ipmiStorageGetSDR);
2387
2388 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2389 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2390 ipmiStorageGetSDR);
2391}
2392} // namespace ipmi