1 /*
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 "config.h"
18
19 #include "dbus-sdr/sensorcommands.hpp"
20
21 #include "dbus-sdr/sdrutils.hpp"
22 #include "dbus-sdr/sensorutils.hpp"
23 #include "dbus-sdr/storagecommands.hpp"
24
25 #include <boost/algorithm/string.hpp>
26 #include <boost/container/flat_map.hpp>
27 #include <ipmid/api.hpp>
28 #include <ipmid/entity_map_json.hpp>
29 #include <ipmid/types.hpp>
30 #include <ipmid/utils.hpp>
31 #include <phosphor-logging/lg2.hpp>
32 #include <sdbusplus/bus.hpp>
33 #include <user_channel/channel_layer.hpp>
34
35 #include <algorithm>
36 #include <array>
37 #include <chrono>
38 #include <cmath>
39 #include <cstring>
40 #include <format>
41 #include <map>
42 #include <optional>
43 #include <stdexcept>
44 #include <string>
45 #include <utility>
46 #include <variant>
47
48 #ifdef FEATURE_HYBRID_SENSORS
49
50 #include "sensordatahandler.hpp"
51 namespace ipmi
52 {
53 namespace sensor
54 {
55 extern const IdInfoMap sensors;
56 } // namespace sensor
57 } // namespace ipmi
58 #endif
59 namespace ipmi
60 {
61 namespace dcmi
62 {
63 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
64 static const std::map<uint8_t, uint8_t> validEntityId{
65 {0x40, 0x37}, {0x37, 0x40}, {0x41, 0x03},
66 {0x03, 0x41}, {0x42, 0x07}, {0x07, 0x42}};
67 constexpr uint8_t temperatureSensorType = 0x01;
68 constexpr uint8_t maxRecords = 8;
69 } // namespace dcmi
70 } // namespace ipmi
71 constexpr std::array<const char*, 7> suffixes = {
72 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current",
73 "_Output_Power", "_Input_Power", "_Temperature"};
74 namespace ipmi
75 {
76
77 using phosphor::logging::entry;
78 using phosphor::logging::level;
79 using phosphor::logging::log;
80
81 static constexpr int sensorMapUpdatePeriod = 10;
82 static constexpr int sensorMapSdrUpdatePeriod = 60;
83
84 // BMC I2C address is generally at 0x20
85 static constexpr uint8_t bmcI2CAddr = 0x20;
86
87 constexpr size_t maxSDRTotalSize =
88 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
89 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
90
91 static uint16_t sdrReservationID;
92 static uint32_t sdrLastAdd = noTimestamp;
93 static uint32_t sdrLastRemove = noTimestamp;
94 static constexpr size_t lastRecordIndex = 0xFFFF;
95
96 // The IPMI spec defines four Logical Units (LUN), each capable of supporting
97 // 255 sensors. The 256 values assigned to LUN 2 are special and are not used
98 // for general purpose sensors. Each LUN reserves location 0xFF. The maximum
99 // number of IPMI sensors are LUN 0 + LUN 1 + LUN 3, less the reserved
100 // location.
101 static constexpr size_t maxIPMISensors = ((3 * 256) - (3 * 1));
102
103 static constexpr uint8_t lun0 = 0x0;
104 static constexpr uint8_t lun1 = 0x1;
105 static constexpr uint8_t lun3 = 0x3;
106
107 static constexpr size_t lun0MaxSensorNum = 0xfe;
108 static constexpr size_t lun1MaxSensorNum = 0x1fe;
109 static constexpr size_t lun3MaxSensorNum = 0x3fe;
110 static constexpr int GENERAL_ERROR = -1;
111
112 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache;
113
114 // Specify the comparison required to sort and find char* map objects
115 struct CmpStr
116 {
operator ()ipmi::CmpStr117 bool operator()(const char* a, const char* b) const
118 {
119 return std::strcmp(a, b) < 0;
120 }
121 };
122 const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
123 sensorUnits{{{"temperature", SensorUnits::degreesC},
124 {"voltage", SensorUnits::volts},
125 {"current", SensorUnits::amps},
126 {"fan_tach", SensorUnits::rpm},
127 {"power", SensorUnits::watts},
128 {"energy", SensorUnits::joules}}};
129
130 void registerSensorFunctions() __attribute__((constructor));
131
132 static sdbusplus::bus::match_t sensorAdded(
133 *getSdBus(),
134 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
135 "sensors/'",
__anon2f2fc9af0102(sdbusplus::message_t&) 136 [](sdbusplus::message_t&) {
137 getSensorTree().clear();
138 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
139 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
140 std::chrono::system_clock::now().time_since_epoch())
141 .count();
142 });
143
144 static sdbusplus::bus::match_t sensorRemoved(
145 *getSdBus(),
146 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
147 "sensors/'",
__anon2f2fc9af0202(sdbusplus::message_t&) 148 [](sdbusplus::message_t&) {
149 getSensorTree().clear();
150 getIpmiDecoratorPaths(/*ctx=*/std::nullopt).reset();
151 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
152 std::chrono::system_clock::now().time_since_epoch())
153 .count();
154 });
155
getSensorConnection(ipmi::Context::ptr ctx,uint8_t sensnum,std::string & connection,std::string & path,std::vector<std::string> * interfaces)156 ipmi_ret_t getSensorConnection(ipmi::Context::ptr ctx, uint8_t sensnum,
157 std::string& connection, std::string& path,
158 std::vector<std::string>* interfaces)
159 {
160 auto& sensorTree = getSensorTree();
161 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
162 {
163 return IPMI_CC_RESPONSE_ERROR;
164 }
165
166 if (ctx == nullptr)
167 {
168 return IPMI_CC_RESPONSE_ERROR;
169 }
170
171 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum);
172 if (path.empty())
173 {
174 return IPMI_CC_INVALID_FIELD_REQUEST;
175 }
176
177 for (const auto& sensor : sensorTree)
178 {
179 if (path == sensor.first)
180 {
181 connection = sensor.second.begin()->first;
182 if (interfaces)
183 *interfaces = sensor.second.begin()->second;
184 break;
185 }
186 }
187
188 return 0;
189 }
190
getSensorTree()191 SensorSubTree& getSensorTree()
192 {
193 static SensorSubTree sensorTree;
194 return sensorTree;
195 }
196
197 // this keeps track of deassertions for sensor event status command. A
198 // deasertion can only happen if an assertion was seen first.
199 static boost::container::flat_map<
200 std::string, boost::container::flat_map<std::string, std::optional<bool>>>
201 thresholdDeassertMap;
202
203 static sdbusplus::bus::match_t thresholdChanged(
204 *getSdBus(),
205 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus."
206 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
__anon2f2fc9af0302(sdbusplus::message_t& m) 207 [](sdbusplus::message_t& m) {
208 boost::container::flat_map<std::string, std::variant<bool, double>>
209 values;
210 m.read(std::string(), values);
211
212 auto findAssert =
213 std::find_if(values.begin(), values.end(), [](const auto& pair) {
214 return pair.first.find("Alarm") != std::string::npos;
215 });
216 if (findAssert != values.end())
217 {
218 auto ptr = std::get_if<bool>(&(findAssert->second));
219 if (ptr == nullptr)
220 {
221 lg2::error("thresholdChanged: Assert non bool");
222 return;
223 }
224 if (*ptr)
225 {
226 lg2::info(
227 "thresholdChanged: Assert, sensor path: {SENSOR_PATH}",
228 "SENSOR_PATH", m.get_path());
229 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr;
230 }
231 else
232 {
233 auto& value =
234 thresholdDeassertMap[m.get_path()][findAssert->first];
235 if (value)
236 {
237 lg2::info(
238 "thresholdChanged: deassert, sensor path: {SENSOR_PATH}",
239 "SENSOR_PATH", m.get_path());
240 value = *ptr;
241 }
242 }
243 }
244 });
245
246 namespace sensor
247 {
248 static constexpr const char* vrInterface =
249 "xyz.openbmc_project.Control.VoltageRegulatorMode";
250 static constexpr const char* sensorInterface =
251 "xyz.openbmc_project.Sensor.Value";
252 } // namespace sensor
253
getSensorMaxMin(const DbusInterfaceMap & sensorMap,double & max,double & min)254 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max,
255 double& min)
256 {
257 max = 127;
258 min = -128;
259
260 auto sensorObject = sensorMap.find(sensor::sensorInterface);
261 auto critical =
262 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
263 auto warning =
264 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
265
266 if (sensorObject != sensorMap.end())
267 {
268 auto maxMap = sensorObject->second.find("MaxValue");
269 auto minMap = sensorObject->second.find("MinValue");
270
271 if (maxMap != sensorObject->second.end())
272 {
273 max = std::visit(VariantToDoubleVisitor(), maxMap->second);
274 }
275 if (minMap != sensorObject->second.end())
276 {
277 min = std::visit(VariantToDoubleVisitor(), minMap->second);
278 }
279 }
280 if (critical != sensorMap.end())
281 {
282 auto lower = critical->second.find("CriticalLow");
283 auto upper = critical->second.find("CriticalHigh");
284 if (lower != critical->second.end())
285 {
286 double value = std::visit(VariantToDoubleVisitor(), lower->second);
287 if (std::isfinite(value))
288 {
289 min = std::fmin(value, min);
290 }
291 }
292 if (upper != critical->second.end())
293 {
294 double value = std::visit(VariantToDoubleVisitor(), upper->second);
295 if (std::isfinite(value))
296 {
297 max = std::fmax(value, max);
298 }
299 }
300 }
301 if (warning != sensorMap.end())
302 {
303 auto lower = warning->second.find("WarningLow");
304 auto upper = warning->second.find("WarningHigh");
305 if (lower != warning->second.end())
306 {
307 double value = std::visit(VariantToDoubleVisitor(), lower->second);
308 if (std::isfinite(value))
309 {
310 min = std::fmin(value, min);
311 }
312 }
313 if (upper != warning->second.end())
314 {
315 double value = std::visit(VariantToDoubleVisitor(), upper->second);
316 if (std::isfinite(value))
317 {
318 max = std::fmax(value, max);
319 }
320 }
321 }
322 }
323
getSensorMap(ipmi::Context::ptr ctx,std::string sensorConnection,std::string sensorPath,DbusInterfaceMap & sensorMap,int updatePeriod=sensorMapUpdatePeriod)324 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection,
325 std::string sensorPath, DbusInterfaceMap& sensorMap,
326 int updatePeriod = sensorMapUpdatePeriod)
327 {
328 #ifdef FEATURE_HYBRID_SENSORS
329 if (auto sensor = findStaticSensor(sensorPath);
330 sensor != ipmi::sensor::sensors.end() &&
331 getSensorEventTypeFromPath(sensorPath) !=
332 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
333 {
334 // If the incoming sensor is a discrete sensor, it might fail in
335 // getManagedObjects(), return true, and use its own getFunc to get
336 // value.
337 return true;
338 }
339 #endif
340
341 static boost::container::flat_map<
342 std::string, std::chrono::time_point<std::chrono::steady_clock>>
343 updateTimeMap;
344
345 auto updateFind = updateTimeMap.find(sensorConnection);
346 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
347 if (updateFind != updateTimeMap.end())
348 {
349 lastUpdate = updateFind->second;
350 }
351
352 auto now = std::chrono::steady_clock::now();
353
354 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
355 .count() > updatePeriod)
356 {
357 bool found = false;
358
359 // Object managers for different kinds of OpenBMC DBus interfaces.
360 // Documented in the phosphor-dbus-interfaces repository.
361 const char* paths[] = {
362 "/xyz/openbmc_project/sensors",
363 "/xyz/openbmc_project/vr",
364 };
365 constexpr size_t numPaths = sizeof(paths) / sizeof(paths[0]);
366 ObjectValueTree allManagedObjects;
367
368 for (size_t i = 0; i < numPaths; i++)
369 {
370 ObjectValueTree managedObjects;
371 boost::system::error_code ec = getManagedObjects(
372 ctx, sensorConnection.c_str(), paths[i], managedObjects);
373 if (ec)
374 {
375 continue;
376 }
377 allManagedObjects.merge(managedObjects);
378 found = true;
379 }
380
381 if (!found)
382 {
383 lg2::error("GetMangagedObjects for getSensorMap failed, "
384 "service: {SERVICE}",
385 "SERVICE", sensorConnection);
386
387 return false;
388 }
389
390 SensorCache[sensorConnection] = allManagedObjects;
391 // Update time after finish building the map which allow the
392 // data to be cached for updatePeriod plus the build time.
393 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now();
394 }
395 auto connection = SensorCache.find(sensorConnection);
396 if (connection == SensorCache.end())
397 {
398 return false;
399 }
400 auto path = connection->second.find(sensorPath);
401 if (path == connection->second.end())
402 {
403 return false;
404 }
405 sensorMap = path->second;
406
407 return true;
408 }
409
410 namespace sensor
411 {
412 // Read VR profiles from sensor(daemon) interface
getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type & object)413 static std::optional<std::vector<std::string>> getSupportedVrProfiles(
414 const ipmi::DbusInterfaceMap::mapped_type& object)
415 {
416 // get VR mode profiles from Supported Interface
417 auto supportedProperty = object.find("Supported");
418 if (supportedProperty == object.end() ||
419 object.find("Selected") == object.end())
420 {
421 lg2::error("Missing the required Supported and Selected properties");
422 return std::nullopt;
423 }
424
425 const auto profilesPtr =
426 std::get_if<std::vector<std::string>>(&supportedProperty->second);
427
428 if (profilesPtr == nullptr)
429 {
430 lg2::error("property is not array of string");
431 return std::nullopt;
432 }
433 return *profilesPtr;
434 }
435
436 // Calculate VR Mode from input IPMI discrete event bytes
calculateVRMode(uint15_t assertOffset,const ipmi::DbusInterfaceMap::mapped_type & VRObject)437 static std::optional<std::string> calculateVRMode(
438 uint15_t assertOffset, const ipmi::DbusInterfaceMap::mapped_type& VRObject)
439 {
440 // get VR mode profiles from Supported Interface
441 auto profiles = getSupportedVrProfiles(VRObject);
442 if (!profiles)
443 {
444 return std::nullopt;
445 }
446
447 // interpret IPMI cmd bits into profiles' index
448 long unsigned int index = 0;
449 // only one bit should be set and the highest bit should not be used.
450 if (assertOffset == 0 || assertOffset == (1u << 15) ||
451 (assertOffset & (assertOffset - 1)))
452 {
453 lg2::error("IPMI cmd format incorrect, bytes: {BYTES}", "BYTES",
454 lg2::hex, static_cast<uint16_t>(assertOffset));
455 return std::nullopt;
456 }
457
458 while (assertOffset != 1)
459 {
460 assertOffset >>= 1;
461 index++;
462 }
463
464 if (index >= profiles->size())
465 {
466 lg2::error("profile index out of boundary");
467 return std::nullopt;
468 }
469
470 return profiles->at(index);
471 }
472
473 // Calculate sensor value from IPMI reading byte
calculateValue(uint8_t reading,const ipmi::DbusInterfaceMap & sensorMap,const ipmi::DbusInterfaceMap::mapped_type & valueObject)474 static std::optional<double> calculateValue(
475 uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap,
476 const ipmi::DbusInterfaceMap::mapped_type& valueObject)
477 {
478 if (valueObject.find("Value") == valueObject.end())
479 {
480 lg2::error("Missing the required Value property");
481 return std::nullopt;
482 }
483
484 double max = 0;
485 double min = 0;
486 getSensorMaxMin(sensorMap, max, min);
487
488 int16_t mValue = 0;
489 int16_t bValue = 0;
490 int8_t rExp = 0;
491 int8_t bExp = 0;
492 bool bSigned = false;
493
494 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
495 {
496 return std::nullopt;
497 }
498
499 double value = bSigned ? ((int8_t)reading) : reading;
500
501 value *= ((double)mValue);
502 value += ((double)bValue) * std::pow(10.0, bExp);
503 value *= std::pow(10.0, rExp);
504
505 return value;
506 }
507
508 // Extract file name from sensor path as the sensors SDR ID. Simplify the name
509 // if it is too long.
parseSdrIdFromPath(const std::string & path)510 std::string parseSdrIdFromPath(const std::string& path)
511 {
512 std::string name;
513 size_t nameStart = path.rfind("/");
514 if (nameStart != std::string::npos)
515 {
516 name = path.substr(nameStart + 1, std::string::npos - nameStart);
517 }
518
519 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
520 {
521 #ifdef SHORTNAME_REMOVE_SUFFIX
522 for (const auto& suffix : suffixes)
523 {
524 if (boost::ends_with(name, suffix))
525 {
526 boost::replace_all(name, suffix, "");
527 break;
528 }
529 }
530 #endif
531 #ifdef SHORTNAME_REPLACE_WORDS
532 constexpr std::array<std::pair<const char*, const char*>, 2>
533 replaceWords = {std::make_pair("Output", "Out"),
534 std::make_pair("Input", "In")};
535 for (const auto& [find, replace] : replaceWords)
536 {
537 boost::replace_all(name, find, replace);
538 }
539 #endif
540
541 // as a backup and if nothing else is configured
542 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
543 }
544 return name;
545 }
546
getVrEventStatus(ipmi::Context::ptr ctx,const std::string & connection,const std::string & path,const ipmi::DbusInterfaceMap::mapped_type & object,std::bitset<16> & assertions)547 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
548 const std::string& path,
549 const ipmi::DbusInterfaceMap::mapped_type& object,
550 std::bitset<16>& assertions)
551 {
552 auto profiles = sensor::getSupportedVrProfiles(object);
553 if (!profiles)
554 {
555 return false;
556 }
557 std::string mode;
558
559 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
560 "Selected", mode);
561 if (ec)
562 {
563 lg2::error("Failed to get Selected, path: {PATH}, "
564 "interface: {INTERFACE}, error: {ERROR}",
565 "PATH", path, "INTERFACE", sensor::sensorInterface, "ERROR",
566 ec.message());
567 return false;
568 }
569
570 auto itr = std::find(profiles->begin(), profiles->end(), mode);
571 if (itr == profiles->end())
572 {
573 lg2::error("VR mode doesn't match any of its profiles, path: {PATH}",
574 "PATH", path);
575 return false;
576 }
577 std::size_t index =
578 static_cast<std::size_t>(std::distance(profiles->begin(), itr));
579
580 // map index to response event assertion bit.
581 if (index < 16)
582 {
583 assertions.set(index);
584 }
585 else
586 {
587 lg2::error("VR profile index reaches max assertion bit, "
588 "path: {PATH}, index: {INDEX}",
589 "PATH", path, "INDEX", index);
590 return false;
591 }
592 if constexpr (debug)
593 {
594 lg2::error("VR sensor {PATH} mode is: [{INDEX}] {MODE}", "PATH",
595 sensor::parseSdrIdFromPath(path), "INDEX", index, "MODE",
596 mode);
597 }
598 return true;
599 }
600
601 /*
602 * Handle every Sensor Data Record besides Type 01
603 *
604 * The D-Bus sensors work well for generating Type 01 SDRs.
605 * After the Type 01 sensors are processed the remaining sensor types require
606 * special handling. Each BMC vendor is going to have their own requirements for
607 * insertion of non-Type 01 records.
608 * Manage non-Type 01 records:
609 *
610 * Create a new file: dbus-sdr/sensorcommands_oem.cpp
611 * Populate it with the two weakly linked functions below, without adding the
612 * 'weak' attribute definition prior to the function definition.
613 * getOtherSensorsCount(...)
614 * getOtherSensorsDataRecord(...)
615 * Example contents are provided in the weak definitions below
616 * Enable 'sensors-oem' in your phosphor-ipmi-host bbappend file
617 * 'EXTRA_OEMESON:append = " -Dsensors-oem=enabled"'
618 * The contents of the sensorcommands_oem.cpp file will then override the code
619 * provided below.
620 */
621
622 size_t getOtherSensorsCount(ipmi::Context::ptr ctx) __attribute__((weak));
getOtherSensorsCount(ipmi::Context::ptr ctx)623 size_t getOtherSensorsCount(ipmi::Context::ptr ctx)
624 {
625 size_t fruCount = 0;
626
627 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount);
628 if (ret != ipmi::ccSuccess)
629 {
630 lg2::error("getOtherSensorsCount: getFruSdrCount error");
631 return std::numeric_limits<size_t>::max();
632 }
633
634 const auto& entityRecords =
635 ipmi::sensor::EntityInfoMapContainer::getContainer()
636 ->getIpmiEntityRecords();
637 size_t entityCount = entityRecords.size();
638
639 return fruCount + ipmi::storage::type12Count + entityCount;
640 }
641
642 int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
643 std::vector<uint8_t>& recordData)
644 __attribute__((weak));
getOtherSensorsDataRecord(ipmi::Context::ptr ctx,uint16_t recordID,std::vector<uint8_t> & recordData)645 int getOtherSensorsDataRecord(ipmi::Context::ptr ctx, uint16_t recordID,
646 std::vector<uint8_t>& recordData)
647 {
648 size_t otherCount{ipmi::sensor::getOtherSensorsCount(ctx)};
649 if (otherCount == std::numeric_limits<size_t>::max())
650 {
651 return GENERAL_ERROR;
652 }
653 const auto& entityRecords =
654 ipmi::sensor::EntityInfoMapContainer::getContainer()
655 ->getIpmiEntityRecords();
656
657 size_t sdrIndex(recordID - ipmi::getNumberOfSensors());
658 size_t entityCount{entityRecords.size()};
659 size_t fruCount{otherCount - ipmi::storage::type12Count - entityCount};
660
661 if (sdrIndex > otherCount)
662 {
663 return std::numeric_limits<int>::min();
664 }
665 else if (sdrIndex >= fruCount + ipmi::storage::type12Count)
666 {
667 // handle type 8 entity map records
668 ipmi::sensor::EntityInfoMap::const_iterator entity =
669 entityRecords.find(static_cast<uint8_t>(
670 sdrIndex - fruCount - ipmi::storage::type12Count));
671
672 if (entity == entityRecords.end())
673 {
674 return GENERAL_ERROR;
675 }
676 recordData = ipmi::storage::getType8SDRs(entity, recordID);
677 }
678 else if (sdrIndex >= fruCount)
679 {
680 // handle type 12 hardcoded records
681 size_t type12Index = sdrIndex - fruCount;
682 if (type12Index >= ipmi::storage::type12Count)
683 {
684 lg2::error("getSensorDataRecord: type12Index error");
685 return GENERAL_ERROR;
686 }
687 recordData = ipmi::storage::getType12SDRs(type12Index, recordID);
688 }
689 else
690 {
691 // handle fru records
692 get_sdr::SensorDataFruRecord data;
693 if (ipmi::Cc ret = ipmi::storage::getFruSdrs(ctx, sdrIndex, data);
694 ret != ipmi::ccSuccess)
695 {
696 return GENERAL_ERROR;
697 }
698 data.header.record_id_msb = recordID >> 8;
699 data.header.record_id_lsb = recordID & 0xFF;
700 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data),
701 reinterpret_cast<uint8_t*>(&data) + sizeof(data));
702 }
703
704 return 0;
705 }
706
707 } // namespace sensor
708
ipmiSenPlatformEvent(ipmi::Context::ptr ctx,ipmi::message::Payload & p)709 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx,
710 ipmi::message::Payload& p)
711 {
712 constexpr const uint8_t validEnvmRev = 0x04;
713 constexpr const uint8_t lastSensorType = 0x2C;
714 constexpr const uint8_t oemReserved = 0xC0;
715
716 uint8_t sysgeneratorID = 0;
717 uint8_t evmRev = 0;
718 uint8_t sensorType = 0;
719 uint8_t sensorNum = 0;
720 uint8_t eventType = 0;
721 uint8_t eventData1 = 0;
722 std::optional<uint8_t> eventData2 = 0;
723 std::optional<uint8_t> eventData3 = 0;
724 [[maybe_unused]] uint16_t generatorID = 0;
725 ipmi::ChannelInfo chInfo;
726
727 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess)
728 {
729 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL",
730 ctx->channel);
731 return ipmi::responseUnspecifiedError();
732 }
733
734 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
735 ipmi::EChannelMediumType::systemInterface)
736 {
737 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType,
738 eventData1, eventData2, eventData3);
739 constexpr const uint8_t isSoftwareID = 0x01;
740 if (!(sysgeneratorID & isSoftwareID))
741 {
742 return ipmi::responseInvalidFieldRequest();
743 }
744 // Refer to IPMI Spec Table 32: SEL Event Records
745 generatorID = (ctx->channel << 12) // Channel
746 | (0x0 << 10) // Reserved
747 | (0x0 << 8) // 0x0 for sys-soft ID
748 | sysgeneratorID;
749 }
750 else
751 {
752 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1,
753 eventData2, eventData3);
754 // Refer to IPMI Spec Table 32: SEL Event Records
755 generatorID = (ctx->channel << 12) // Channel
756 | (0x0 << 10) // Reserved
757 | ((ctx->lun & 0x3) << 8) // Lun
758 | (ctx->rqSA << 1);
759 }
760
761 if (!p.fullyUnpacked())
762 {
763 return ipmi::responseReqDataLenInvalid();
764 }
765
766 // Check for valid evmRev and Sensor Type(per Table 42 of spec)
767 if (evmRev != validEnvmRev)
768 {
769 return ipmi::responseInvalidFieldRequest();
770 }
771 if ((sensorType > lastSensorType) && (sensorType < oemReserved))
772 {
773 return ipmi::responseInvalidFieldRequest();
774 }
775
776 return ipmi::responseSuccess();
777 }
778
ipmiSetSensorReading(ipmi::Context::ptr ctx,uint8_t sensorNumber,uint8_t,uint8_t reading,uint15_t assertOffset,bool,uint15_t,bool,uint8_t,uint8_t,uint8_t)779 ipmi::RspType<> ipmiSetSensorReading(
780 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading,
781 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t)
782 {
783 std::string connection;
784 std::string path;
785 std::vector<std::string> interfaces;
786
787 ipmi::Cc status =
788 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces);
789 if (status)
790 {
791 return ipmi::response(status);
792 }
793
794 // we can tell the sensor type by its interface type
795 if (std::find(interfaces.begin(), interfaces.end(),
796 sensor::sensorInterface) != interfaces.end())
797 {
798 DbusInterfaceMap sensorMap;
799 if (!getSensorMap(ctx, connection, path, sensorMap))
800 {
801 return ipmi::responseResponseError();
802 }
803 auto sensorObject = sensorMap.find(sensor::sensorInterface);
804 if (sensorObject == sensorMap.end())
805 {
806 return ipmi::responseResponseError();
807 }
808
809 // Only allow external SetSensor if write permission granted
810 if (!details::sdrWriteTable.getWritePermission(
811 (ctx->lun << 8) | sensorNumber))
812 {
813 return ipmi::responseResponseError();
814 }
815
816 auto value =
817 sensor::calculateValue(reading, sensorMap, sensorObject->second);
818 if (!value)
819 {
820 return ipmi::responseResponseError();
821 }
822
823 if constexpr (debug)
824 {
825 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, "
826 "byte: {BYTE}, value: {VALUE}",
827 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading,
828 "VALUE", *value);
829 }
830
831 boost::system::error_code ec =
832 setDbusProperty(ctx, connection, path, sensor::sensorInterface,
833 "Value", ipmi::Value(*value));
834
835 // setDbusProperty intended to resolve dbus exception/rc within the
836 // function but failed to achieve that. Catch exception in the ipmi
837 // callback functions for now (e.g. ipmiSetSensorReading).
838 if (ec)
839 {
840 lg2::error("Failed to set Value, path: {PATH}, "
841 "interface: {INTERFACE}, ERROR: {ERROR}",
842 "PATH", path, "INTERFACE", sensor::sensorInterface,
843 "ERROR", ec.message());
844 return ipmi::responseResponseError();
845 }
846 return ipmi::responseSuccess();
847 }
848
849 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
850 interfaces.end())
851 {
852 DbusInterfaceMap sensorMap;
853 if (!getSensorMap(ctx, connection, path, sensorMap))
854 {
855 return ipmi::responseResponseError();
856 }
857 auto sensorObject = sensorMap.find(sensor::vrInterface);
858 if (sensorObject == sensorMap.end())
859 {
860 return ipmi::responseResponseError();
861 }
862
863 // VR sensors are treated as a special case and we will not check the
864 // write permission for VR sensors, since they always deemed writable
865 // and permission table are not applied to VR sensors.
866 auto vrMode =
867 sensor::calculateVRMode(assertOffset, sensorObject->second);
868 if (!vrMode)
869 {
870 return ipmi::responseResponseError();
871 }
872 boost::system::error_code ec = setDbusProperty(
873 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode);
874 // setDbusProperty intended to resolve dbus exception/rc within the
875 // function but failed to achieve that. Catch exception in the ipmi
876 // callback functions for now (e.g. ipmiSetSensorReading).
877 if (ec)
878 {
879 lg2::error("Failed to set Selected, path: {PATH}, "
880 "interface: {INTERFACE}, ERROR: {ERROR}",
881 "PATH", path, "INTERFACE", sensor::sensorInterface,
882 "ERROR", ec.message());
883 }
884 return ipmi::responseSuccess();
885 }
886
887 lg2::error("unknown sensor type, path: {PATH}", "PATH", path);
888 return ipmi::responseResponseError();
889 }
890
891 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>>
ipmiSenGetSensorReading(ipmi::Context::ptr ctx,uint8_t sensnum)892 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum)
893 {
894 std::string connection;
895 std::string path;
896
897 if (sensnum == reservedSensorNumber)
898 {
899 return ipmi::responseInvalidFieldRequest();
900 }
901
902 auto status = getSensorConnection(ctx, sensnum, connection, path);
903 if (status)
904 {
905 return ipmi::response(status);
906 }
907
908 #ifdef FEATURE_HYBRID_SENSORS
909 if (auto sensor = findStaticSensor(path);
910 sensor != ipmi::sensor::sensors.end() &&
911 getSensorEventTypeFromPath(path) !=
912 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
913 {
914 if (ipmi::sensor::Mutability::Read !=
915 (sensor->second.mutability & ipmi::sensor::Mutability::Read))
916 {
917 return ipmi::responseIllegalCommand();
918 }
919
920 uint8_t operation;
921 try
922 {
923 ipmi::sensor::GetSensorResponse getResponse =
924 sensor->second.getFunc(sensor->second);
925
926 if (getResponse.readingOrStateUnavailable)
927 {
928 operation |= static_cast<uint8_t>(
929 IPMISensorReadingByte2::readingStateUnavailable);
930 }
931 if (getResponse.scanningEnabled)
932 {
933 operation |= static_cast<uint8_t>(
934 IPMISensorReadingByte2::sensorScanningEnable);
935 }
936 if (getResponse.allEventMessagesEnabled)
937 {
938 operation |= static_cast<uint8_t>(
939 IPMISensorReadingByte2::eventMessagesEnable);
940 }
941 return ipmi::responseSuccess(
942 getResponse.reading, operation,
943 getResponse.thresholdLevelsStates,
944 getResponse.discreteReadingSensorStates);
945 }
946 catch (const std::exception& e)
947 {
948 operation |= static_cast<uint8_t>(
949 IPMISensorReadingByte2::readingStateUnavailable);
950 return ipmi::responseSuccess(0, operation, 0, std::nullopt);
951 }
952 }
953 #endif
954
955 DbusInterfaceMap sensorMap;
956 if (!getSensorMap(ctx, connection, path, sensorMap))
957 {
958 return ipmi::responseResponseError();
959 }
960 auto sensorObject = sensorMap.find(sensor::sensorInterface);
961
962 if (sensorObject == sensorMap.end() ||
963 sensorObject->second.find("Value") == sensorObject->second.end())
964 {
965 return ipmi::responseResponseError();
966 }
967 auto& valueVariant = sensorObject->second["Value"];
968 double reading = std::visit(VariantToDoubleVisitor(), valueVariant);
969
970 double max = 0;
971 double min = 0;
972 getSensorMaxMin(sensorMap, max, min);
973
974 int16_t mValue = 0;
975 int16_t bValue = 0;
976 int8_t rExp = 0;
977 int8_t bExp = 0;
978 bool bSigned = false;
979
980 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
981 {
982 return ipmi::responseResponseError();
983 }
984
985 uint8_t value =
986 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
987 uint8_t operation =
988 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
989 operation |=
990 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable);
991 bool notReading = std::isnan(reading);
992
993 if (!notReading)
994 {
995 auto availableObject =
996 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability");
997 if (availableObject != sensorMap.end())
998 {
999 auto findAvailable = availableObject->second.find("Available");
1000 if (findAvailable != availableObject->second.end())
1001 {
1002 bool* available = std::get_if<bool>(&(findAvailable->second));
1003 if (available && !(*available))
1004 {
1005 notReading = true;
1006 }
1007 }
1008 }
1009 }
1010
1011 if (notReading)
1012 {
1013 operation |= static_cast<uint8_t>(
1014 IPMISensorReadingByte2::readingStateUnavailable);
1015 }
1016
1017 if constexpr (details::enableInstrumentation)
1018 {
1019 int byteValue;
1020 if (bSigned)
1021 {
1022 byteValue = static_cast<int>(static_cast<int8_t>(value));
1023 }
1024 else
1025 {
1026 byteValue = static_cast<int>(static_cast<uint8_t>(value));
1027 }
1028
1029 // Keep stats on the reading just obtained, even if it is "NaN"
1030 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum,
1031 reading, byteValue))
1032 {
1033 // This is the first reading, show the coefficients
1034 double step = (max - min) / 255.0;
1035 lg2::error(
1036 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, "
1037 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} "
1038 "bExp={BEXP} bSigned={BSIGNED}",
1039 "NAME",
1040 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum),
1041 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP",
1042 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned);
1043 }
1044 }
1045
1046 uint8_t thresholds = 0;
1047
1048 auto warningObject =
1049 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1050 if (warningObject != sensorMap.end())
1051 {
1052 auto alarmHigh = warningObject->second.find("WarningAlarmHigh");
1053 auto alarmLow = warningObject->second.find("WarningAlarmLow");
1054 if (alarmHigh != warningObject->second.end())
1055 {
1056 if (std::get<bool>(alarmHigh->second))
1057 {
1058 thresholds |= static_cast<uint8_t>(
1059 IPMISensorReadingByte3::upperNonCritical);
1060 }
1061 }
1062 if (alarmLow != warningObject->second.end())
1063 {
1064 if (std::get<bool>(alarmLow->second))
1065 {
1066 thresholds |= static_cast<uint8_t>(
1067 IPMISensorReadingByte3::lowerNonCritical);
1068 }
1069 }
1070 }
1071
1072 auto criticalObject =
1073 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1074 if (criticalObject != sensorMap.end())
1075 {
1076 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh");
1077 auto alarmLow = criticalObject->second.find("CriticalAlarmLow");
1078 if (alarmHigh != criticalObject->second.end())
1079 {
1080 if (std::get<bool>(alarmHigh->second))
1081 {
1082 thresholds |=
1083 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1084 }
1085 }
1086 if (alarmLow != criticalObject->second.end())
1087 {
1088 if (std::get<bool>(alarmLow->second))
1089 {
1090 thresholds |=
1091 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1092 }
1093 }
1094 }
1095
1096 // no discrete as of today so optional byte is never returned
1097 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt);
1098 }
1099
1100 /** @brief implements the Set Sensor threshold command
1101 * @param sensorNumber - sensor number
1102 * @param lowerNonCriticalThreshMask
1103 * @param lowerCriticalThreshMask
1104 * @param lowerNonRecovThreshMask
1105 * @param upperNonCriticalThreshMask
1106 * @param upperCriticalThreshMask
1107 * @param upperNonRecovThreshMask
1108 * @param reserved
1109 * @param lowerNonCritical - lower non-critical threshold
1110 * @param lowerCritical - Lower critical threshold
1111 * @param lowerNonRecoverable - Lower non recovarable threshold
1112 * @param upperNonCritical - Upper non-critical threshold
1113 * @param upperCritical - Upper critical
1114 * @param upperNonRecoverable - Upper Non-recoverable
1115 *
1116 * @returns IPMI completion code
1117 */
ipmiSenSetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNum,bool lowerNonCriticalThreshMask,bool lowerCriticalThreshMask,bool lowerNonRecovThreshMask,bool upperNonCriticalThreshMask,bool upperCriticalThreshMask,bool upperNonRecovThreshMask,uint2_t reserved,uint8_t lowerNonCritical,uint8_t lowerCritical,uint8_t lowerNonRecoverable,uint8_t upperNonCritical,uint8_t upperCritical,uint8_t upperNonRecoverable)1118 ipmi::RspType<> ipmiSenSetSensorThresholds(
1119 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
1120 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
1121 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
1122 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
1123 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable,
1124 uint8_t upperNonCritical, uint8_t upperCritical,
1125 [[maybe_unused]] uint8_t upperNonRecoverable)
1126 {
1127 if (sensorNum == reservedSensorNumber || reserved)
1128 {
1129 return ipmi::responseInvalidFieldRequest();
1130 }
1131
1132 // lower nc and upper nc not suppported on any sensor
1133 if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
1134 {
1135 return ipmi::responseInvalidFieldRequest();
1136 }
1137
1138 // if none of the threshold mask are set, nothing to do
1139 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
1140 lowerNonRecovThreshMask | upperNonCriticalThreshMask |
1141 upperCriticalThreshMask | upperNonRecovThreshMask))
1142 {
1143 return ipmi::responseSuccess();
1144 }
1145
1146 std::string connection;
1147 std::string path;
1148
1149 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path);
1150 if (status)
1151 {
1152 return ipmi::response(status);
1153 }
1154 DbusInterfaceMap sensorMap;
1155 if (!getSensorMap(ctx, connection, path, sensorMap))
1156 {
1157 return ipmi::responseResponseError();
1158 }
1159
1160 double max = 0;
1161 double min = 0;
1162 getSensorMaxMin(sensorMap, max, min);
1163
1164 int16_t mValue = 0;
1165 int16_t bValue = 0;
1166 int8_t rExp = 0;
1167 int8_t bExp = 0;
1168 bool bSigned = false;
1169
1170 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1171 {
1172 return ipmi::responseResponseError();
1173 }
1174
1175 // store a vector of property name, value to set, and interface
1176 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
1177
1178 // define the indexes of the tuple
1179 constexpr uint8_t propertyName = 0;
1180 constexpr uint8_t thresholdValue = 1;
1181 constexpr uint8_t interface = 2;
1182 // verifiy all needed fields are present
1183 if (lowerCriticalThreshMask || upperCriticalThreshMask)
1184 {
1185 auto findThreshold =
1186 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1187 if (findThreshold == sensorMap.end())
1188 {
1189 return ipmi::responseInvalidFieldRequest();
1190 }
1191 if (lowerCriticalThreshMask)
1192 {
1193 auto findLower = findThreshold->second.find("CriticalLow");
1194 if (findLower == findThreshold->second.end())
1195 {
1196 return ipmi::responseInvalidFieldRequest();
1197 }
1198 thresholdsToSet.emplace_back("CriticalLow", lowerCritical,
1199 findThreshold->first);
1200 }
1201 if (upperCriticalThreshMask)
1202 {
1203 auto findUpper = findThreshold->second.find("CriticalHigh");
1204 if (findUpper == findThreshold->second.end())
1205 {
1206 return ipmi::responseInvalidFieldRequest();
1207 }
1208 thresholdsToSet.emplace_back("CriticalHigh", upperCritical,
1209 findThreshold->first);
1210 }
1211 }
1212 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
1213 {
1214 auto findThreshold =
1215 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1216 if (findThreshold == sensorMap.end())
1217 {
1218 return ipmi::responseInvalidFieldRequest();
1219 }
1220 if (lowerNonCriticalThreshMask)
1221 {
1222 auto findLower = findThreshold->second.find("WarningLow");
1223 if (findLower == findThreshold->second.end())
1224 {
1225 return ipmi::responseInvalidFieldRequest();
1226 }
1227 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical,
1228 findThreshold->first);
1229 }
1230 if (upperNonCriticalThreshMask)
1231 {
1232 auto findUpper = findThreshold->second.find("WarningHigh");
1233 if (findUpper == findThreshold->second.end())
1234 {
1235 return ipmi::responseInvalidFieldRequest();
1236 }
1237 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical,
1238 findThreshold->first);
1239 }
1240 }
1241 for (const auto& property : thresholdsToSet)
1242 {
1243 // from section 36.3 in the IPMI Spec, assume all linear
1244 double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
1245 (bValue * std::pow(10.0, bExp))) *
1246 std::pow(10.0, rExp);
1247 setDbusProperty(
1248 *getSdBus(), connection, path, std::get<interface>(property),
1249 std::get<propertyName>(property), ipmi::Value(valueToSet));
1250 }
1251 return ipmi::responseSuccess();
1252 }
1253
getIPMIThresholds(const DbusInterfaceMap & sensorMap)1254 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap)
1255 {
1256 IPMIThresholds resp;
1257 auto warningInterface =
1258 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1259 auto criticalInterface =
1260 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1261
1262 if ((warningInterface != sensorMap.end()) ||
1263 (criticalInterface != sensorMap.end()))
1264 {
1265 auto sensorPair = sensorMap.find(sensor::sensorInterface);
1266
1267 if (sensorPair == sensorMap.end())
1268 {
1269 // should not have been able to find a sensor not implementing
1270 // the sensor object
1271 throw std::runtime_error("Invalid sensor map");
1272 }
1273
1274 double max = 0;
1275 double min = 0;
1276 getSensorMaxMin(sensorMap, max, min);
1277
1278 int16_t mValue = 0;
1279 int16_t bValue = 0;
1280 int8_t rExp = 0;
1281 int8_t bExp = 0;
1282 bool bSigned = false;
1283
1284 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1285 {
1286 throw std::runtime_error("Invalid sensor atrributes");
1287 }
1288 if (warningInterface != sensorMap.end())
1289 {
1290 auto& warningMap = warningInterface->second;
1291
1292 auto warningHigh = warningMap.find("WarningHigh");
1293 auto warningLow = warningMap.find("WarningLow");
1294
1295 if (warningHigh != warningMap.end())
1296 {
1297 double value =
1298 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1299 if (std::isfinite(value))
1300 {
1301 resp.warningHigh = scaleIPMIValueFromDouble(
1302 value, mValue, rExp, bValue, bExp, bSigned);
1303 }
1304 }
1305 if (warningLow != warningMap.end())
1306 {
1307 double value =
1308 std::visit(VariantToDoubleVisitor(), warningLow->second);
1309 if (std::isfinite(value))
1310 {
1311 resp.warningLow = scaleIPMIValueFromDouble(
1312 value, mValue, rExp, bValue, bExp, bSigned);
1313 }
1314 }
1315 }
1316 if (criticalInterface != sensorMap.end())
1317 {
1318 auto& criticalMap = criticalInterface->second;
1319
1320 auto criticalHigh = criticalMap.find("CriticalHigh");
1321 auto criticalLow = criticalMap.find("CriticalLow");
1322
1323 if (criticalHigh != criticalMap.end())
1324 {
1325 double value =
1326 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1327 if (std::isfinite(value))
1328 {
1329 resp.criticalHigh = scaleIPMIValueFromDouble(
1330 value, mValue, rExp, bValue, bExp, bSigned);
1331 }
1332 }
1333 if (criticalLow != criticalMap.end())
1334 {
1335 double value =
1336 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1337 if (std::isfinite(value))
1338 {
1339 resp.criticalLow = scaleIPMIValueFromDouble(
1340 value, mValue, rExp, bValue, bExp, bSigned);
1341 }
1342 }
1343 }
1344 }
1345 return resp;
1346 }
1347
1348 ipmi::RspType<uint8_t, // readable
1349 uint8_t, // lowerNCrit
1350 uint8_t, // lowerCrit
1351 uint8_t, // lowerNrecoverable
1352 uint8_t, // upperNC
1353 uint8_t, // upperCrit
1354 uint8_t> // upperNRecoverable
ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx,uint8_t sensorNumber)1355 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber)
1356 {
1357 std::string connection;
1358 std::string path;
1359
1360 if (sensorNumber == reservedSensorNumber)
1361 {
1362 return ipmi::responseInvalidFieldRequest();
1363 }
1364
1365 auto status = getSensorConnection(ctx, sensorNumber, connection, path);
1366 if (status)
1367 {
1368 return ipmi::response(status);
1369 }
1370
1371 DbusInterfaceMap sensorMap;
1372 if (!getSensorMap(ctx, connection, path, sensorMap))
1373 {
1374 return ipmi::responseResponseError();
1375 }
1376
1377 IPMIThresholds thresholdData;
1378 try
1379 {
1380 thresholdData = getIPMIThresholds(sensorMap);
1381 }
1382 catch (const std::exception&)
1383 {
1384 return ipmi::responseResponseError();
1385 }
1386
1387 uint8_t readable = 0;
1388 uint8_t lowerNC = 0;
1389 uint8_t lowerCritical = 0;
1390 uint8_t lowerNonRecoverable = 0;
1391 uint8_t upperNC = 0;
1392 uint8_t upperCritical = 0;
1393 uint8_t upperNonRecoverable = 0;
1394
1395 if (thresholdData.warningHigh)
1396 {
1397 readable |=
1398 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical);
1399 upperNC = *thresholdData.warningHigh;
1400 }
1401 if (thresholdData.warningLow)
1402 {
1403 readable |=
1404 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical);
1405 lowerNC = *thresholdData.warningLow;
1406 }
1407
1408 if (thresholdData.criticalHigh)
1409 {
1410 readable |=
1411 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical);
1412 upperCritical = *thresholdData.criticalHigh;
1413 }
1414 if (thresholdData.criticalLow)
1415 {
1416 readable |=
1417 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical);
1418 lowerCritical = *thresholdData.criticalLow;
1419 }
1420
1421 return ipmi::responseSuccess(readable, lowerNC, lowerCritical,
1422 lowerNonRecoverable, upperNC, upperCritical,
1423 upperNonRecoverable);
1424 }
1425
1426 /** @brief implements the get Sensor event enable command
1427 * @param sensorNumber - sensor number
1428 *
1429 * @returns IPMI completion code plus response data
1430 * - enabled - Sensor Event messages
1431 * - assertionEnabledLsb - Assertion event messages
1432 * - assertionEnabledMsb - Assertion event messages
1433 * - deassertionEnabledLsb - Deassertion event messages
1434 * - deassertionEnabledMsb - Deassertion event messages
1435 */
1436
1437 ipmi::RspType<uint8_t, // enabled
1438 uint8_t, // assertionEnabledLsb
1439 uint8_t, // assertionEnabledMsb
1440 uint8_t, // deassertionEnabledLsb
1441 uint8_t> // deassertionEnabledMsb
ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx,uint8_t sensorNum)1442 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum)
1443 {
1444 std::string connection;
1445 std::string path;
1446
1447 uint8_t enabled = 0;
1448 uint8_t assertionEnabledLsb = 0;
1449 uint8_t assertionEnabledMsb = 0;
1450 uint8_t deassertionEnabledLsb = 0;
1451 uint8_t deassertionEnabledMsb = 0;
1452
1453 if (sensorNum == reservedSensorNumber)
1454 {
1455 return ipmi::responseInvalidFieldRequest();
1456 }
1457
1458 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1459 if (status)
1460 {
1461 return ipmi::response(status);
1462 }
1463
1464 #ifdef FEATURE_HYBRID_SENSORS
1465 if (auto sensor = findStaticSensor(path);
1466 sensor != ipmi::sensor::sensors.end() &&
1467 getSensorEventTypeFromPath(path) !=
1468 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1469 {
1470 enabled = static_cast<uint8_t>(
1471 IPMISensorEventEnableByte2::sensorScanningEnable);
1472 uint16_t assertionEnabled = 0;
1473 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin()
1474 ->second.begin()
1475 ->second.second)
1476 {
1477 assertionEnabled |= (1 << offsetValMap.first);
1478 }
1479 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF));
1480 assertionEnabledMsb =
1481 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF));
1482
1483 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1484 assertionEnabledMsb, deassertionEnabledLsb,
1485 deassertionEnabledMsb);
1486 }
1487 #endif
1488
1489 DbusInterfaceMap sensorMap;
1490 if (!getSensorMap(ctx, connection, path, sensorMap))
1491 {
1492 return ipmi::responseResponseError();
1493 }
1494
1495 auto warningInterface =
1496 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1497 auto criticalInterface =
1498 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1499 if ((warningInterface != sensorMap.end()) ||
1500 (criticalInterface != sensorMap.end()))
1501 {
1502 enabled = static_cast<uint8_t>(
1503 IPMISensorEventEnableByte2::sensorScanningEnable);
1504 if (warningInterface != sensorMap.end())
1505 {
1506 auto& warningMap = warningInterface->second;
1507
1508 auto warningHigh = warningMap.find("WarningHigh");
1509 auto warningLow = warningMap.find("WarningLow");
1510 if (warningHigh != warningMap.end())
1511 {
1512 double value =
1513 std::visit(VariantToDoubleVisitor(), warningHigh->second);
1514 if (std::isfinite(value))
1515 {
1516 assertionEnabledLsb |= static_cast<uint8_t>(
1517 IPMISensorEventEnableThresholds::
1518 upperNonCriticalGoingHigh);
1519 deassertionEnabledLsb |= static_cast<uint8_t>(
1520 IPMISensorEventEnableThresholds::
1521 upperNonCriticalGoingLow);
1522 }
1523 }
1524 if (warningLow != warningMap.end())
1525 {
1526 double value =
1527 std::visit(VariantToDoubleVisitor(), warningLow->second);
1528 if (std::isfinite(value))
1529 {
1530 assertionEnabledLsb |= static_cast<uint8_t>(
1531 IPMISensorEventEnableThresholds::
1532 lowerNonCriticalGoingLow);
1533 deassertionEnabledLsb |= static_cast<uint8_t>(
1534 IPMISensorEventEnableThresholds::
1535 lowerNonCriticalGoingHigh);
1536 }
1537 }
1538 }
1539 if (criticalInterface != sensorMap.end())
1540 {
1541 auto& criticalMap = criticalInterface->second;
1542
1543 auto criticalHigh = criticalMap.find("CriticalHigh");
1544 auto criticalLow = criticalMap.find("CriticalLow");
1545
1546 if (criticalHigh != criticalMap.end())
1547 {
1548 double value =
1549 std::visit(VariantToDoubleVisitor(), criticalHigh->second);
1550 if (std::isfinite(value))
1551 {
1552 assertionEnabledMsb |= static_cast<uint8_t>(
1553 IPMISensorEventEnableThresholds::
1554 upperCriticalGoingHigh);
1555 deassertionEnabledMsb |= static_cast<uint8_t>(
1556 IPMISensorEventEnableThresholds::upperCriticalGoingLow);
1557 }
1558 }
1559 if (criticalLow != criticalMap.end())
1560 {
1561 double value =
1562 std::visit(VariantToDoubleVisitor(), criticalLow->second);
1563 if (std::isfinite(value))
1564 {
1565 assertionEnabledLsb |= static_cast<uint8_t>(
1566 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1567 deassertionEnabledLsb |= static_cast<uint8_t>(
1568 IPMISensorEventEnableThresholds::
1569 lowerCriticalGoingHigh);
1570 }
1571 }
1572 }
1573 }
1574
1575 return ipmi::responseSuccess(enabled, assertionEnabledLsb,
1576 assertionEnabledMsb, deassertionEnabledLsb,
1577 deassertionEnabledMsb);
1578 }
1579
1580 /** @brief implements the get Sensor event status command
1581 * @param sensorNumber - sensor number, FFh = reserved
1582 *
1583 * @returns IPMI completion code plus response data
1584 * - sensorEventStatus - Sensor Event messages state
1585 * - assertions - Assertion event messages
1586 * - deassertions - Deassertion event messages
1587 */
1588 ipmi::RspType<uint8_t, // sensorEventStatus
1589 std::bitset<16>, // assertions
1590 std::bitset<16> // deassertion
1591 >
ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx,uint8_t sensorNum)1592 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum)
1593 {
1594 if (sensorNum == reservedSensorNumber)
1595 {
1596 return ipmi::responseInvalidFieldRequest();
1597 }
1598
1599 std::string connection;
1600 std::string path;
1601 auto status = getSensorConnection(ctx, sensorNum, connection, path);
1602 if (status)
1603 {
1604 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, "
1605 "sensor number: {SENSOR_NUM}",
1606 "SENSOR_NUM", sensorNum);
1607 return ipmi::response(status);
1608 }
1609
1610 #ifdef FEATURE_HYBRID_SENSORS
1611 if (auto sensor = findStaticSensor(path);
1612 sensor != ipmi::sensor::sensors.end() &&
1613 getSensorEventTypeFromPath(path) !=
1614 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
1615 {
1616 auto response = ipmi::sensor::get::mapDbusToAssertion(
1617 sensor->second, path, sensor->second.sensorInterface);
1618 std::bitset<16> assertions;
1619 // deassertions are not used.
1620 std::bitset<16> deassertions = 0;
1621 uint8_t sensorEventStatus;
1622 if (response.readingOrStateUnavailable)
1623 {
1624 sensorEventStatus |= static_cast<uint8_t>(
1625 IPMISensorReadingByte2::readingStateUnavailable);
1626 }
1627 if (response.scanningEnabled)
1628 {
1629 sensorEventStatus |= static_cast<uint8_t>(
1630 IPMISensorReadingByte2::sensorScanningEnable);
1631 }
1632 if (response.allEventMessagesEnabled)
1633 {
1634 sensorEventStatus |= static_cast<uint8_t>(
1635 IPMISensorReadingByte2::eventMessagesEnable);
1636 }
1637 assertions |= response.discreteReadingSensorStates << 8;
1638 assertions |= response.thresholdLevelsStates;
1639 return ipmi::responseSuccess(sensorEventStatus, assertions,
1640 deassertions);
1641 }
1642 #endif
1643
1644 DbusInterfaceMap sensorMap;
1645 if (!getSensorMap(ctx, connection, path, sensorMap))
1646 {
1647 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, "
1648 "sensor path: {SENSOR_PATH}",
1649 "SENSOR_PATH", path);
1650 return ipmi::responseResponseError();
1651 }
1652
1653 uint8_t sensorEventStatus =
1654 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
1655 std::bitset<16> assertions = 0;
1656 std::bitset<16> deassertions = 0;
1657
1658 // handle VR typed sensor
1659 auto vrInterface = sensorMap.find(sensor::vrInterface);
1660 if (vrInterface != sensorMap.end())
1661 {
1662 if (!sensor::getVrEventStatus(ctx, connection, path,
1663 vrInterface->second, assertions))
1664 {
1665 return ipmi::responseResponseError();
1666 }
1667
1668 // both Event Message and Sensor Scanning are disable for VR.
1669 sensorEventStatus = 0;
1670 return ipmi::responseSuccess(sensorEventStatus, assertions,
1671 deassertions);
1672 }
1673
1674 auto warningInterface =
1675 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
1676 auto criticalInterface =
1677 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
1678
1679 std::optional<bool> criticalDeassertHigh =
1680 thresholdDeassertMap[path]["CriticalAlarmHigh"];
1681 std::optional<bool> criticalDeassertLow =
1682 thresholdDeassertMap[path]["CriticalAlarmLow"];
1683 std::optional<bool> warningDeassertHigh =
1684 thresholdDeassertMap[path]["WarningAlarmHigh"];
1685 std::optional<bool> warningDeassertLow =
1686 thresholdDeassertMap[path]["WarningAlarmLow"];
1687
1688 if (criticalDeassertHigh && !*criticalDeassertHigh)
1689 {
1690 deassertions.set(static_cast<size_t>(
1691 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh));
1692 }
1693 if (criticalDeassertLow && !*criticalDeassertLow)
1694 {
1695 deassertions.set(static_cast<size_t>(
1696 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow));
1697 }
1698 if (warningDeassertHigh && !*warningDeassertHigh)
1699 {
1700 deassertions.set(static_cast<size_t>(
1701 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh));
1702 }
1703 if (warningDeassertLow && !*warningDeassertLow)
1704 {
1705 deassertions.set(static_cast<size_t>(
1706 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh));
1707 }
1708 if ((warningInterface != sensorMap.end()) ||
1709 (criticalInterface != sensorMap.end()))
1710 {
1711 sensorEventStatus = static_cast<size_t>(
1712 IPMISensorEventEnableByte2::eventMessagesEnable);
1713 if (warningInterface != sensorMap.end())
1714 {
1715 auto& warningMap = warningInterface->second;
1716
1717 auto warningHigh = warningMap.find("WarningAlarmHigh");
1718 auto warningLow = warningMap.find("WarningAlarmLow");
1719 auto warningHighAlarm = false;
1720 auto warningLowAlarm = false;
1721
1722 if (warningHigh != warningMap.end())
1723 {
1724 warningHighAlarm = std::get<bool>(warningHigh->second);
1725 }
1726 if (warningLow != warningMap.end())
1727 {
1728 warningLowAlarm = std::get<bool>(warningLow->second);
1729 }
1730 if (warningHighAlarm)
1731 {
1732 assertions.set(static_cast<size_t>(
1733 IPMIGetSensorEventEnableThresholds::
1734 upperNonCriticalGoingHigh));
1735 }
1736 if (warningLowAlarm)
1737 {
1738 assertions.set(static_cast<size_t>(
1739 IPMIGetSensorEventEnableThresholds::
1740 lowerNonCriticalGoingLow));
1741 }
1742 }
1743 if (criticalInterface != sensorMap.end())
1744 {
1745 auto& criticalMap = criticalInterface->second;
1746
1747 auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
1748 auto criticalLow = criticalMap.find("CriticalAlarmLow");
1749 auto criticalHighAlarm = false;
1750 auto criticalLowAlarm = false;
1751
1752 if (criticalHigh != criticalMap.end())
1753 {
1754 criticalHighAlarm = std::get<bool>(criticalHigh->second);
1755 }
1756 if (criticalLow != criticalMap.end())
1757 {
1758 criticalLowAlarm = std::get<bool>(criticalLow->second);
1759 }
1760 if (criticalHighAlarm)
1761 {
1762 assertions.set(static_cast<size_t>(
1763 IPMIGetSensorEventEnableThresholds::
1764 upperCriticalGoingHigh));
1765 }
1766 if (criticalLowAlarm)
1767 {
1768 assertions.set(static_cast<size_t>(
1769 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow));
1770 }
1771 }
1772 }
1773
1774 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions);
1775 }
1776
1777 // Construct a type 1 SDR for threshold sensor.
constructSensorSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataFullRecord & record)1778 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
1779 get_sdr::SensorDataFullRecord& record)
1780 {
1781 get_sdr::header::set_record_id(
1782 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
1783
1784 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
1785 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
1786
1787 record.header.sdr_version = ipmiSdrVersion;
1788 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
1789 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
1790 sizeof(get_sdr::SensorDataRecordHeader);
1791 record.key.owner_id = bmcI2CAddr;
1792 record.key.owner_lun = lun;
1793 record.key.sensor_number = sensornumber;
1794 }
constructSensorSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataFullRecord & record)1795 bool constructSensorSdr(
1796 ipmi::Context::ptr ctx,
1797 const std::unordered_set<std::string>& ipmiDecoratorPaths,
1798 uint16_t sensorNum, uint16_t recordID, const std::string& service,
1799 const std::string& path, get_sdr::SensorDataFullRecord& record)
1800 {
1801 constructSensorSdrHeaderKey(sensorNum, recordID, record);
1802
1803 DbusInterfaceMap sensorMap;
1804 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
1805 {
1806 lg2::error("Failed to update sensor map for threshold sensor, "
1807 "service: {SERVICE}, path: {PATH}",
1808 "SERVICE", service, "PATH", path);
1809 return false;
1810 }
1811
1812 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis
1813 record.body.sensor_type = getSensorTypeFromPath(path);
1814 std::string type = getSensorTypeStringFromPath(path);
1815 auto typeCstr = type.c_str();
1816 auto findUnits = sensorUnits.find(typeCstr);
1817 if (findUnits != sensorUnits.end())
1818 {
1819 record.body.sensor_units_2_base =
1820 static_cast<uint8_t>(findUnits->second);
1821 } // else default 0x0 unspecified
1822
1823 record.body.event_reading_type = getSensorEventTypeFromPath(path);
1824
1825 auto sensorObject = sensorMap.find(sensor::sensorInterface);
1826 if (sensorObject == sensorMap.end())
1827 {
1828 lg2::error("constructSensorSdr: sensorObject error");
1829 return false;
1830 }
1831
1832 uint8_t entityId = 0;
1833 uint8_t entityInstance = 0x01;
1834
1835 // follow the association chain to get the parent board's entityid and
1836 // entityInstance
1837 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId,
1838 entityInstance);
1839
1840 record.body.entity_id = entityId;
1841 record.body.entity_instance = entityInstance;
1842
1843 double max = 0;
1844 double min = 0;
1845 getSensorMaxMin(sensorMap, max, min);
1846
1847 int16_t mValue = 0;
1848 int8_t rExp = 0;
1849 int16_t bValue = 0;
1850 int8_t bExp = 0;
1851 bool bSigned = false;
1852
1853 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
1854 {
1855 lg2::error("constructSensorSdr: getSensorAttributes error");
1856 return false;
1857 }
1858
1859 // The record.body is a struct SensorDataFullRecordBody
1860 // from sensorhandler.hpp in phosphor-ipmi-host.
1861 // The meaning of these bits appears to come from
1862 // table 43.1 of the IPMI spec.
1863 // The above 5 sensor attributes are stuffed in as follows:
1864 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned
1865 // Byte 22-24 are for other purposes
1866 // Byte 25 = MMMMMMMM = LSB of M
1867 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance
1868 // Byte 27 = BBBBBBBB = LSB of B
1869 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy
1870 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy
1871 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed)
1872
1873 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
1874 record.body.m_lsb = mValue & 0xFF;
1875
1876 uint8_t mBitSign = (mValue < 0) ? 1 : 0;
1877 uint8_t mBitNine = (mValue & 0x0100) >> 8;
1878
1879 // move the smallest bit of the MSB into place (bit 9)
1880 // the MSbs are bits 7:8 in m_msb_and_tolerance
1881 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6);
1882
1883 record.body.b_lsb = bValue & 0xFF;
1884
1885 uint8_t bBitSign = (bValue < 0) ? 1 : 0;
1886 uint8_t bBitNine = (bValue & 0x0100) >> 8;
1887
1888 // move the smallest bit of the MSB into place (bit 9)
1889 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
1890 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6);
1891
1892 uint8_t rExpSign = (rExp < 0) ? 1 : 0;
1893 uint8_t rExpBits = rExp & 0x07;
1894
1895 uint8_t bExpSign = (bExp < 0) ? 1 : 0;
1896 uint8_t bExpBits = bExp & 0x07;
1897
1898 // move rExp and bExp into place
1899 record.body.r_b_exponents =
1900 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits;
1901
1902 // Set the analog reading byte interpretation accordingly
1903 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7;
1904
1905 // TODO(): Perhaps care about Tolerance, Accuracy, and so on
1906 // These seem redundant, but derivable from the above 5 attributes
1907 // Original comment said "todo fill out rest of units"
1908
1909 // populate sensor name from path
1910 auto name = sensor::parseSdrIdFromPath(path);
1911 get_sdr::body::set_id_strlen(name.size(), &record.body);
1912 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
1913 std::memcpy(record.body.id_string, name.c_str(),
1914 std::min(name.length() + 1, sizeof(record.body.id_string)));
1915
1916 // Remember the sensor name, as determined for this sensor number
1917 details::sdrStatsTable.updateName(sensorNum, name);
1918
1919 bool sensorSettable = false;
1920 auto mutability =
1921 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability");
1922 if (mutability != sensorMap.end())
1923 {
1924 sensorSettable =
1925 mappedVariant<bool>(mutability->second, "Mutable", false);
1926 }
1927 get_sdr::body::init_settable_state(sensorSettable, &record.body);
1928
1929 // Grant write permission to sensors deemed externally settable
1930 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable);
1931
1932 IPMIThresholds thresholdData;
1933 try
1934 {
1935 thresholdData = getIPMIThresholds(sensorMap);
1936 }
1937 catch (const std::exception&)
1938 {
1939 lg2::error("constructSensorSdr: getIPMIThresholds error");
1940 return false;
1941 }
1942
1943 if (thresholdData.criticalHigh)
1944 {
1945 record.body.upper_critical_threshold = *thresholdData.criticalHigh;
1946 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1947 IPMISensorEventEnableThresholds::criticalThreshold);
1948 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1949 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1950 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1951 IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
1952 record.body.discrete_reading_setting_mask[0] |=
1953 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical);
1954 }
1955 if (thresholdData.warningHigh)
1956 {
1957 record.body.upper_noncritical_threshold = *thresholdData.warningHigh;
1958 record.body.supported_deassertions[1] |= static_cast<uint8_t>(
1959 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1960 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1961 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1962 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1963 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
1964 record.body.discrete_reading_setting_mask[0] |=
1965 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical);
1966 }
1967 if (thresholdData.criticalLow)
1968 {
1969 record.body.lower_critical_threshold = *thresholdData.criticalLow;
1970 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1971 IPMISensorEventEnableThresholds::criticalThreshold);
1972 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1973 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1974 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1975 IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
1976 record.body.discrete_reading_setting_mask[0] |=
1977 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical);
1978 }
1979 if (thresholdData.warningLow)
1980 {
1981 record.body.lower_noncritical_threshold = *thresholdData.warningLow;
1982 record.body.supported_assertions[1] |= static_cast<uint8_t>(
1983 IPMISensorEventEnableThresholds::nonCriticalThreshold);
1984 record.body.supported_deassertions[0] |= static_cast<uint8_t>(
1985 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1986 record.body.supported_assertions[0] |= static_cast<uint8_t>(
1987 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
1988 record.body.discrete_reading_setting_mask[0] |=
1989 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical);
1990 }
1991
1992 // everything that is readable is setable
1993 record.body.discrete_reading_setting_mask[1] =
1994 record.body.discrete_reading_setting_mask[0];
1995 return true;
1996 }
1997
1998 #ifdef FEATURE_HYBRID_SENSORS
1999 // Construct a type 1 SDR for discrete Sensor typed sensor.
constructStaticSensorSdr(ipmi::Context::ptr,uint16_t sensorNum,uint16_t recordID,ipmi::sensor::IdInfoMap::const_iterator sensor,get_sdr::SensorDataFullRecord & record)2000 void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum,
2001 uint16_t recordID,
2002 ipmi::sensor::IdInfoMap::const_iterator sensor,
2003 get_sdr::SensorDataFullRecord& record)
2004 {
2005 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2006
2007 record.body.entity_id = sensor->second.entityType;
2008 record.body.sensor_type = sensor->second.sensorType;
2009 record.body.event_reading_type = sensor->second.sensorReadingType;
2010 record.body.entity_instance = sensor->second.instance;
2011 if (ipmi::sensor::Mutability::Write ==
2012 (sensor->second.mutability & ipmi::sensor::Mutability::Write))
2013 {
2014 get_sdr::body::init_settable_state(true, &(record.body));
2015 }
2016
2017 auto id_string = sensor->second.sensorName;
2018
2019 if (id_string.empty())
2020 {
2021 id_string = sensor->second.sensorNameFunc(sensor->second);
2022 }
2023
2024 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
2025 {
2026 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH,
2027 &(record.body));
2028 }
2029 else
2030 {
2031 get_sdr::body::set_id_strlen(id_string.length(), &(record.body));
2032 }
2033 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
2034 std::strncpy(record.body.id_string, id_string.c_str(),
2035 get_sdr::body::get_id_strlen(&(record.body)));
2036 }
2037 #endif
2038
2039 // Construct type 3 SDR header and key (for VR and other discrete sensors)
constructEventSdrHeaderKey(uint16_t sensorNum,uint16_t recordID,get_sdr::SensorDataEventRecord & record)2040 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID,
2041 get_sdr::SensorDataEventRecord& record)
2042 {
2043 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2044 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2045
2046 get_sdr::header::set_record_id(
2047 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record));
2048
2049 record.header.sdr_version = ipmiSdrVersion;
2050 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD;
2051 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) -
2052 sizeof(get_sdr::SensorDataRecordHeader);
2053 record.key.owner_id = bmcI2CAddr;
2054 record.key.owner_lun = lun;
2055 record.key.sensor_number = sensornumber;
2056
2057 record.body.entity_id = 0x00;
2058 record.body.entity_instance = 0x01;
2059 }
2060
2061 // Construct a type 3 SDR for VR typed sensor(daemon).
constructVrSdr(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,uint16_t sensorNum,uint16_t recordID,const std::string & service,const std::string & path,get_sdr::SensorDataEventRecord & record)2062 bool constructVrSdr(ipmi::Context::ptr ctx,
2063 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2064 uint16_t sensorNum, uint16_t recordID,
2065 const std::string& service, const std::string& path,
2066 get_sdr::SensorDataEventRecord& record)
2067 {
2068 constructEventSdrHeaderKey(sensorNum, recordID, record);
2069
2070 DbusInterfaceMap sensorMap;
2071 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod))
2072 {
2073 lg2::error("Failed to update sensor map for VR sensor, "
2074 "service: {SERVICE}, path: {PATH}",
2075 "SERVICE", service, "PATH", path);
2076 return false;
2077 }
2078 // follow the association chain to get the parent board's entityid and
2079 // entityInstance
2080 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap,
2081 record.body.entity_id,
2082 record.body.entity_instance);
2083
2084 // Sensor type is hardcoded as a module/board type instead of parsing from
2085 // sensor path. This is because VR control is allocated in an independent
2086 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by
2087 // types.
2088 static constexpr const uint8_t moduleBoardType = 0x15;
2089 record.body.sensor_type = moduleBoardType;
2090 record.body.event_reading_type = 0x00;
2091
2092 record.body.sensor_record_sharing_1 = 0x00;
2093 record.body.sensor_record_sharing_2 = 0x00;
2094
2095 // populate sensor name from path
2096 auto name = sensor::parseSdrIdFromPath(path);
2097 int nameSize = std::min(name.size(), sizeof(record.body.id_string));
2098 get_sdr::body::set_id_strlen(nameSize, &record.body);
2099 get_sdr::body::set_id_type(3, &record.body); // "8-bit ASCII + Latin 1"
2100 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string));
2101 std::memcpy(record.body.id_string, name.c_str(), nameSize);
2102
2103 // Remember the sensor name, as determined for this sensor number
2104 details::sdrStatsTable.updateName(sensorNum, name);
2105
2106 return true;
2107 }
2108
getNumberOfSensors()2109 uint16_t getNumberOfSensors()
2110 {
2111 return std::min(getSensorTree().size(), maxIPMISensors);
2112 }
2113
getSensorDataRecord(ipmi::Context::ptr ctx,const std::unordered_set<std::string> & ipmiDecoratorPaths,std::vector<uint8_t> & recordData,uint16_t recordID,uint8_t readBytes=std::numeric_limits<uint8_t>::max ())2114 static int getSensorDataRecord(
2115 ipmi::Context::ptr ctx,
2116 const std::unordered_set<std::string>& ipmiDecoratorPaths,
2117 std::vector<uint8_t>& recordData, uint16_t recordID,
2118 uint8_t readBytes = std::numeric_limits<uint8_t>::max())
2119 {
2120 recordData.clear();
2121 size_t lastRecord = ipmi::getNumberOfSensors() +
2122 ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2123 uint16_t nextRecord(recordID + 1);
2124
2125 if (recordID == lastRecordIndex)
2126 {
2127 recordID = lastRecord;
2128 }
2129 if (recordID == lastRecord)
2130 {
2131 nextRecord = lastRecordIndex;
2132 }
2133 if (recordID > lastRecord)
2134 {
2135 lg2::error("getSensorDataRecord: recordID > lastRecord error");
2136 return GENERAL_ERROR;
2137 }
2138 if (recordID >= ipmi::getNumberOfSensors())
2139 {
2140 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID,
2141 recordData);
2142 err < 0)
2143 {
2144 return lastRecordIndex;
2145 }
2146 return nextRecord;
2147 }
2148
2149 // Perform a incremental scan of the SDR Record ID's and translate the
2150 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor
2151 // Numbers. The IPMI sensor numbers are not linear, and have a reserved
2152 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2
2153 // which has special meaning.
2154 std::string connection;
2155 std::string path;
2156 std::vector<std::string> interfaces;
2157 uint16_t sensNumFromRecID{recordID};
2158 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum))
2159 {
2160 // LUN 0 has one reserved sensor number. Compensate here by adding one
2161 // to the record ID
2162 sensNumFromRecID = recordID + 1;
2163 ctx->lun = lun1;
2164 }
2165 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors))
2166 {
2167 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2
2168 // to the record ID. Skip all 256 sensors in LUN 2, as it has special
2169 // rules governing its use.
2170 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2;
2171 ctx->lun = lun3;
2172 }
2173
2174 auto status =
2175 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID),
2176 connection, path, &interfaces);
2177 if (status)
2178 {
2179 lg2::error("getSensorDataRecord: getSensorConnection error");
2180 return GENERAL_ERROR;
2181 }
2182 uint16_t sensorNum = getSensorNumberFromPath(path);
2183 // Return an error on LUN 2 assingments, and any sensor number beyond the
2184 // range of LUN 3
2185 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) ||
2186 (sensorNum > lun3MaxSensorNum))
2187 {
2188 lg2::error("getSensorDataRecord: invalidSensorNumber");
2189 return GENERAL_ERROR;
2190 }
2191 uint8_t sensornumber = static_cast<uint8_t>(sensorNum);
2192 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8);
2193
2194 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) &&
2195 (lun != ctx->lun))
2196 {
2197 lg2::error("getSensorDataRecord: sensor record mismatch");
2198 return GENERAL_ERROR;
2199 }
2200
2201 // Construct full record (SDR type 1) for the threshold sensors
2202 if (std::find(interfaces.begin(), interfaces.end(),
2203 sensor::sensorInterface) != interfaces.end())
2204 {
2205 get_sdr::SensorDataFullRecord record = {};
2206
2207 // If the request doesn't read SDR body, construct only header and key
2208 // part to avoid additional DBus transaction.
2209 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2210 {
2211 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2212 }
2213 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum,
2214 recordID, connection, path, record))
2215 {
2216 return GENERAL_ERROR;
2217 }
2218
2219 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2220 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2221
2222 return nextRecord;
2223 }
2224
2225 #ifdef FEATURE_HYBRID_SENSORS
2226 if (auto sensor = findStaticSensor(path);
2227 sensor != ipmi::sensor::sensors.end() &&
2228 getSensorEventTypeFromPath(path) !=
2229 static_cast<uint8_t>(SensorEventTypeCodes::threshold))
2230 {
2231 get_sdr::SensorDataFullRecord record = {};
2232
2233 // If the request doesn't read SDR body, construct only header and key
2234 // part to avoid additional DBus transaction.
2235 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2236 {
2237 constructSensorSdrHeaderKey(sensorNum, recordID, record);
2238 }
2239 else
2240 {
2241 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record);
2242 }
2243
2244 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2245 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2246
2247 return nextRecord;
2248 }
2249 #endif
2250
2251 // Contruct SDR type 3 record for VR sensor (daemon)
2252 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) !=
2253 interfaces.end())
2254 {
2255 get_sdr::SensorDataEventRecord record = {};
2256
2257 // If the request doesn't read SDR body, construct only header and key
2258 // part to avoid additional DBus transaction.
2259 if (readBytes <= sizeof(record.header) + sizeof(record.key))
2260 {
2261 constructEventSdrHeaderKey(sensorNum, recordID, record);
2262 }
2263 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID,
2264 connection, path, record))
2265 {
2266 return GENERAL_ERROR;
2267 }
2268 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record),
2269 reinterpret_cast<uint8_t*>(&record) + sizeof(record));
2270 }
2271
2272 return nextRecord;
2273 }
2274
2275 /** @brief implements the get SDR Info command
2276 * @param operation : 0 or not supplied returns sensor count
2277 * 1 return SDR count
2278 *
2279 * @returns IPMI completion code plus response data
2280 * - sdrCount - sensor/SDR count
2281 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag
2282 */
2283 static ipmi::RspType<uint8_t, // respcount
2284 uint8_t, // dynamic population flags
2285 uint32_t // last time a sensor was added
2286 >
ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,std::optional<uint8_t> operation)2287 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx,
2288 std::optional<uint8_t> operation)
2289 {
2290 auto& sensorTree{getSensorTree()};
2291 uint8_t sdrCount{};
2292 // Sensors are dynamically allocated
2293 uint8_t lunsAndDynamicPopulation{0x80};
2294 constexpr uint8_t getSdrCount{1};
2295 constexpr uint8_t getSensorCount{0};
2296
2297 if (!getSensorSubtree(sensorTree) || sensorTree.empty())
2298 {
2299 return ipmi::responseResponseError();
2300 }
2301 uint16_t numSensors{ipmi::getNumberOfSensors()};
2302 if (operation.value_or(0) == getSdrCount)
2303 {
2304 sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1;
2305 }
2306 else if (operation.value_or(0) == getSensorCount)
2307 {
2308 // Return the number of sensors attached to the LUN
2309 if ((ctx->lun == lun0) && (numSensors > 0))
2310 {
2311 sdrCount =
2312 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors;
2313 }
2314 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN))
2315 {
2316 sdrCount = (numSensors > (2 * maxSensorsPerLUN))
2317 ? maxSensorsPerLUN
2318 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN;
2319 }
2320 else if (ctx->lun == lun3)
2321 {
2322 if (numSensors <= maxIPMISensors)
2323 {
2324 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) &
2325 maxSensorsPerLUN;
2326 }
2327 else
2328 {
2329 throw std::out_of_range(
2330 "Maximum number of IPMI sensors exceeded.");
2331 }
2332 }
2333 }
2334 else
2335 {
2336 return ipmi::responseInvalidFieldRequest();
2337 }
2338
2339 // Flag which LUNs have sensors associated
2340 if (numSensors > 0)
2341 {
2342 lunsAndDynamicPopulation |= 1;
2343 }
2344 if (numSensors > maxSensorsPerLUN)
2345 {
2346 lunsAndDynamicPopulation |= 2;
2347 }
2348 if (numSensors >= (maxSensorsPerLUN * 2))
2349 {
2350 lunsAndDynamicPopulation |= 8;
2351 }
2352 if (numSensors > maxIPMISensors)
2353 {
2354 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
2355 }
2356
2357 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation,
2358 sdrLastAdd);
2359 }
2360
2361 /* end sensor commands */
2362
2363 /* storage commands */
2364
2365 ipmi::RspType<uint8_t, // sdr version
2366 uint16_t, // record count
2367 uint16_t, // free space
2368 uint32_t, // most recent addition
2369 uint32_t, // most recent erase
2370 uint8_t // operationSupport
2371 >
ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)2372 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx)
2373 {
2374 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF;
2375 uint16_t recordCount =
2376 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx);
2377
2378 uint8_t operationSupport = static_cast<uint8_t>(
2379 SdrRepositoryInfoOps::overflow); // write not supported
2380
2381 operationSupport |=
2382 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
2383 operationSupport |= static_cast<uint8_t>(
2384 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
2385 return ipmi::responseSuccess(ipmiSdrVersion, recordCount,
2386 unspecifiedFreeSpace, sdrLastAdd,
2387 sdrLastRemove, operationSupport);
2388 }
2389
2390 /** @brief implements the get SDR allocation info command
2391 *
2392 * @returns IPMI completion code plus response data
2393 * - allocUnits - Number of possible allocation units
2394 * - allocUnitSize - Allocation unit size in bytes.
2395 * - allocUnitFree - Number of free allocation units
2396 * - allocUnitLargestFree - Largest free block in allocation units
2397 * - maxRecordSize - Maximum record size in allocation units.
2398 */
2399 ipmi::RspType<uint16_t, // allocUnits
2400 uint16_t, // allocUnitSize
2401 uint16_t, // allocUnitFree
2402 uint16_t, // allocUnitLargestFree
2403 uint8_t // maxRecordSize
2404 >
ipmiStorageGetSDRAllocationInfo()2405 ipmiStorageGetSDRAllocationInfo()
2406 {
2407 // 0000h unspecified number of alloc units
2408 constexpr uint16_t allocUnits = 0;
2409
2410 constexpr uint16_t allocUnitFree = 0;
2411 constexpr uint16_t allocUnitLargestFree = 0;
2412 // only allow one block at a time
2413 constexpr uint8_t maxRecordSize = 1;
2414
2415 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree,
2416 allocUnitLargestFree, maxRecordSize);
2417 }
2418
2419 /** @brief implements the reserve SDR command
2420 * @returns IPMI completion code plus response data
2421 * - sdrReservationID
2422 */
ipmiStorageReserveSDR()2423 ipmi::RspType<uint16_t> ipmiStorageReserveSDR()
2424 {
2425 sdrReservationID++;
2426 if (sdrReservationID == 0)
2427 {
2428 sdrReservationID++;
2429 }
2430
2431 return ipmi::responseSuccess(sdrReservationID);
2432 }
2433
2434 ipmi::RspType<uint16_t, // next record ID
2435 std::vector<uint8_t> // payload
2436 >
ipmiStorageGetSDR(ipmi::Context::ptr ctx,uint16_t reservationID,uint16_t recordID,uint8_t offset,uint8_t bytesToRead)2437 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID,
2438 uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
2439 {
2440 // reservation required for partial reads with non zero offset into
2441 // record
2442 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset)
2443 {
2444 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId");
2445 return ipmi::responseInvalidReservationId();
2446 }
2447
2448 auto& sensorTree = getSensorTree();
2449 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2450 {
2451 lg2::error("ipmiStorageGetSDR: getSensorSubtree error");
2452 return ipmi::responseResponseError();
2453 }
2454
2455 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2456
2457 std::vector<uint8_t> record;
2458 int nextRecordId = getSensorDataRecord(
2459 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2460 record, recordID, offset + bytesToRead);
2461
2462 if (nextRecordId < 0)
2463 {
2464 lg2::error("ipmiStorageGetSDR: fail to get SDR");
2465 return ipmi::responseInvalidFieldRequest();
2466 }
2467 get_sdr::SensorDataRecordHeader* hdr =
2468 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data());
2469 if (!hdr)
2470 {
2471 lg2::error("ipmiStorageGetSDR: record header is null");
2472 return ipmi::responseSuccess(nextRecordId, record);
2473 }
2474
2475 size_t sdrLength =
2476 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length;
2477 if (offset >= sdrLength)
2478 {
2479 lg2::error("ipmiStorageGetSDR: offset is outside the record");
2480 return ipmi::responseParmOutOfRange();
2481 }
2482 if (sdrLength < (offset + bytesToRead))
2483 {
2484 bytesToRead = sdrLength - offset;
2485 }
2486
2487 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset;
2488 if (!respStart)
2489 {
2490 lg2::error("ipmiStorageGetSDR: record is null");
2491 return ipmi::responseSuccess(nextRecordId, record);
2492 }
2493
2494 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead);
2495
2496 return ipmi::responseSuccess(nextRecordId, recordData);
2497 }
2498 namespace dcmi
2499 {
2500
2501 std::tuple<uint8_t, // Total of instance sensors
2502 std::vector<sensorInfo> // The list of sensors
2503 >
getSensorsByEntityId(ipmi::Context::ptr ctx,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2504 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId,
2505 uint8_t entityInstance, uint8_t instanceStart)
2506 {
2507 std::vector<sensorInfo> sensorList;
2508 uint8_t totalInstSensor = 0;
2509 auto match = ipmi::dcmi::validEntityId.find(entityId);
2510
2511 if (match == ipmi::dcmi::validEntityId.end())
2512 {
2513 return std::make_tuple(totalInstSensor, sensorList);
2514 }
2515
2516 auto& sensorTree = getSensorTree();
2517 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
2518 {
2519 return std::make_tuple(totalInstSensor, sensorList);
2520 }
2521
2522 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx);
2523
2524 size_t invalidSensorNumberErrCount = 0;
2525 for (const auto& sensor : sensorTree)
2526 {
2527 const std::string& sensorObjPath = sensor.first;
2528 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath);
2529
2530 /*
2531 * In the DCMI specification, it only supports the sensor type is 0x01
2532 * (temperature type) for both Get Sensor Info and Get Temperature
2533 * Readings commands.
2534 */
2535 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType)
2536 {
2537 continue;
2538 }
2539
2540 const auto& connection = sensor.second.begin()->first;
2541 DbusInterfaceMap sensorMap;
2542
2543 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap,
2544 sensorMapSdrUpdatePeriod))
2545 {
2546 lg2::error("Failed to update sensor map for threshold sensor, "
2547 "service: {SERVICE}, path: {PATH}",
2548 "SERVICE", connection, "PATH", sensorObjPath);
2549 continue;
2550 }
2551
2552 uint8_t entityIdValue = 0;
2553 uint8_t entityInstanceValue = 0;
2554
2555 /*
2556 * Get the Entity ID, Entity Instance information which are configured
2557 * in the Entity-Manger.
2558 */
2559 updateIpmiFromAssociation(
2560 sensorObjPath,
2561 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()),
2562 sensorMap, entityIdValue, entityInstanceValue);
2563
2564 if (entityIdValue == match->first || entityIdValue == match->second)
2565 {
2566 totalInstSensor++;
2567
2568 /*
2569 * When Entity Instance parameter is not 0, we only get the first
2570 * sensor whose Entity Instance number is equal input Entity
2571 * Instance parameter.
2572 */
2573 if (entityInstance)
2574 {
2575 if (!sensorList.empty())
2576 {
2577 continue;
2578 }
2579
2580 if (entityInstanceValue == entityInstance)
2581 {
2582 auto recordId = getSensorNumberFromPath(sensorObjPath);
2583 if (recordId == invalidSensorNumber)
2584 {
2585 ++invalidSensorNumberErrCount;
2586 continue;
2587 }
2588 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2589 recordId, entityIdValue,
2590 entityInstanceValue);
2591 }
2592 }
2593 else if (entityInstanceValue >= instanceStart)
2594 {
2595 auto recordId = getSensorNumberFromPath(sensorObjPath);
2596 if (recordId == invalidSensorNumber)
2597 {
2598 ++invalidSensorNumberErrCount;
2599 continue;
2600 }
2601 sensorList.emplace_back(sensorObjPath, sensorTypeValue,
2602 recordId, entityIdValue,
2603 entityInstanceValue);
2604 }
2605 }
2606 }
2607 if (invalidSensorNumberErrCount != 0)
2608 {
2609 lg2::error("getSensorNumberFromPath returned invalidSensorNumber "
2610 "{ERR_COUNT} times",
2611 "ERR_COUNT", invalidSensorNumberErrCount);
2612 }
2613
2614 auto cmpFunc = [](sensorInfo first, sensorInfo second) {
2615 return first.entityInstance <= second.entityInstance;
2616 };
2617
2618 sort(sensorList.begin(), sensorList.end(), cmpFunc);
2619
2620 return std::make_tuple(totalInstSensor, sensorList);
2621 }
2622
2623 std::tuple<bool, // Reading result
2624 uint7_t, // Temp value
2625 bool> // Sign bit
readTemp(ipmi::Context::ptr ctx,const std::string & objectPath)2626 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath)
2627 {
2628 std::string service{};
2629 boost::system::error_code ec =
2630 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service);
2631 if (ec.value())
2632 {
2633 return std::make_tuple(false, 0, false);
2634 }
2635
2636 ipmi::PropertyMap properties{};
2637 ec = ipmi::getAllDbusProperties(ctx, service, objectPath,
2638 sensor::sensorInterface, properties);
2639 if (ec.value())
2640 {
2641 return std::make_tuple(false, 0, false);
2642 }
2643
2644 auto scaleIt = properties.find("Scale");
2645 double scaleVal = 0.0;
2646 if (scaleIt != properties.end())
2647 {
2648 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second);
2649 }
2650
2651 auto tempValIt = properties.find("Value");
2652 double tempVal = 0.0;
2653 if (tempValIt == properties.end())
2654 {
2655 return std::make_tuple(false, 0, false);
2656 }
2657
2658 const double maxTemp = 127;
2659 double absTempVal = 0.0;
2660 bool signBit = false;
2661
2662 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second);
2663 tempVal = std::pow(10, scaleVal) * tempVal;
2664 absTempVal = std::abs(tempVal);
2665 absTempVal = std::min(absTempVal, maxTemp);
2666 signBit = (tempVal < 0) ? true : false;
2667
2668 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit);
2669 }
2670
2671 ipmi::RspType<uint8_t, // No of instances for requested id
2672 uint8_t, // No of record ids in the response
2673 std::vector<uint16_t> // SDR Record ID corresponding to the Entity
2674 // IDs
2675 >
getSensorInfo(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2676 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId,
2677 uint8_t entityInstance, uint8_t instanceStart)
2678 {
2679 auto match = ipmi::dcmi::validEntityId.find(entityId);
2680 if (match == ipmi::dcmi::validEntityId.end())
2681 {
2682 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2683
2684 return ipmi::responseInvalidFieldRequest();
2685 }
2686
2687 if (sensorType != ipmi::dcmi::temperatureSensorType)
2688 {
2689 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2690 sensorType);
2691
2692 return ipmi::responseInvalidFieldRequest();
2693 }
2694
2695 std::vector<uint16_t> sensorRec{};
2696 const auto& [totalSensorInst, sensorList] =
2697 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2698
2699 if (sensorList.empty())
2700 {
2701 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec);
2702 }
2703
2704 /*
2705 * As DCMI specification, the maximum number of Record Ids of response data
2706 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2707 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2708 * in response data.
2709 */
2710 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2711
2712 for (const auto& sensor : sensorList)
2713 {
2714 sensorRec.emplace_back(sensor.recordId);
2715 if (sensorRec.size() >= numOfRec)
2716 {
2717 break;
2718 }
2719 }
2720
2721 return ipmi::responseSuccess(
2722 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec);
2723 }
2724
2725 ipmi::RspType<uint8_t, // No of instances for requested id
2726 uint8_t, // No of record ids in the response
2727 std::vector< // Temperature Data
2728 std::tuple<uint7_t, // Temperature value
2729 bool, // Sign bit
2730 uint8_t // Entity Instance of sensor
2731 >>>
getTempReadings(ipmi::Context::ptr ctx,uint8_t sensorType,uint8_t entityId,uint8_t entityInstance,uint8_t instanceStart)2732 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType,
2733 uint8_t entityId, uint8_t entityInstance,
2734 uint8_t instanceStart)
2735 {
2736 auto match = ipmi::dcmi::validEntityId.find(entityId);
2737 if (match == ipmi::dcmi::validEntityId.end())
2738 {
2739 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
2740
2741 return ipmi::responseInvalidFieldRequest();
2742 }
2743
2744 if (sensorType != ipmi::dcmi::temperatureSensorType)
2745 {
2746 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
2747 sensorType);
2748
2749 return ipmi::responseInvalidFieldRequest();
2750 }
2751
2752 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{};
2753 const auto& [totalSensorInst, sensorList] =
2754 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart);
2755
2756 if (sensorList.empty())
2757 {
2758 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal);
2759 }
2760
2761 /*
2762 * As DCMI specification, the maximum number of Record Ids of response data
2763 * is 1 if Entity Instance paramter is not 0. Else the maximum number of
2764 * Record Ids of response data is 8. Therefore, not all of sensors are shown
2765 * in response data.
2766 */
2767 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords;
2768
2769 for (const auto& sensor : sensorList)
2770 {
2771 const auto& [readResult, tempVal, signBit] =
2772 readTemp(ctx, sensor.objectPath);
2773
2774 if (readResult)
2775 {
2776 tempReadingVal.emplace_back(
2777 std::make_tuple(tempVal, signBit, sensor.entityInstance));
2778
2779 if (tempReadingVal.size() >= numOfRec)
2780 {
2781 break;
2782 }
2783 }
2784 }
2785
2786 return ipmi::responseSuccess(totalSensorInst,
2787 static_cast<uint8_t>(tempReadingVal.size()),
2788 tempReadingVal);
2789 }
2790
2791 } // namespace dcmi
2792
2793 /* end storage commands */
2794
registerSensorFunctions()2795 void registerSensorFunctions()
2796 {
2797 // <Platform Event>
2798 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2799 ipmi::sensor_event::cmdPlatformEvent,
2800 ipmi::Privilege::Operator, ipmiSenPlatformEvent);
2801
2802 // <Set Sensor Reading and Event Status>
2803 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2804 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
2805 ipmi::Privilege::Operator, ipmiSetSensorReading);
2806
2807 // <Get Sensor Reading>
2808 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2809 ipmi::sensor_event::cmdGetSensorReading,
2810 ipmi::Privilege::User, ipmiSenGetSensorReading);
2811
2812 // <Get Sensor Threshold>
2813 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2814 ipmi::sensor_event::cmdGetSensorThreshold,
2815 ipmi::Privilege::User, ipmiSenGetSensorThresholds);
2816
2817 // <Set Sensor Threshold>
2818 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2819 ipmi::sensor_event::cmdSetSensorThreshold,
2820 ipmi::Privilege::Operator,
2821 ipmiSenSetSensorThresholds);
2822
2823 // <Get Sensor Event Enable>
2824 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2825 ipmi::sensor_event::cmdGetSensorEventEnable,
2826 ipmi::Privilege::User, ipmiSenGetSensorEventEnable);
2827
2828 // <Get Sensor Event Status>
2829 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2830 ipmi::sensor_event::cmdGetSensorEventStatus,
2831 ipmi::Privilege::User, ipmiSenGetSensorEventStatus);
2832
2833 // register all storage commands for both Sensor and Storage command
2834 // versions
2835
2836 // <Get SDR Repository Info>
2837 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2838 ipmi::storage::cmdGetSdrRepositoryInfo,
2839 ipmi::Privilege::User,
2840 ipmiStorageGetSDRRepositoryInfo);
2841
2842 // <Get Device SDR Info>
2843 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2844 ipmi::sensor_event::cmdGetDeviceSdrInfo,
2845 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
2846
2847 // <Get SDR Allocation Info>
2848 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2849 ipmi::storage::cmdGetSdrRepositoryAllocInfo,
2850 ipmi::Privilege::User,
2851 ipmiStorageGetSDRAllocationInfo);
2852
2853 // <Reserve SDR Repo>
2854 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2855 ipmi::sensor_event::cmdReserveDeviceSdrRepository,
2856 ipmi::Privilege::User, ipmiStorageReserveSDR);
2857
2858 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2859 ipmi::storage::cmdReserveSdrRepository,
2860 ipmi::Privilege::User, ipmiStorageReserveSDR);
2861
2862 // <Get Sdr>
2863 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
2864 ipmi::sensor_event::cmdGetDeviceSdr,
2865 ipmi::Privilege::User, ipmiStorageGetSDR);
2866
2867 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
2868 ipmi::storage::cmdGetSdr, ipmi::Privilege::User,
2869 ipmiStorageGetSDR);
2870 // <Get DCMI Sensor Info>
2871 ipmi::registerGroupHandler(
2872 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2873 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator,
2874 ipmi::dcmi::getSensorInfo);
2875 // <Get Temperature Readings>
2876 ipmi::registerGroupHandler(
2877 ipmi::prioOpenBmcBase, ipmi::groupDCMI,
2878 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User,
2879 ipmi::dcmi::getTempReadings);
2880 }
2881 } // namespace ipmi
2882