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 { 117 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/'", 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/'", 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 156 ipmi::Cc 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::ccResponseError; 164 } 165 166 if (ctx == nullptr) 167 { 168 return ipmi::ccResponseError; 169 } 170 171 path = getPathFromSensorNumber((ctx->lun << 8) | sensnum); 172 if (path.empty()) 173 { 174 return ipmi::ccInvalidFieldRequest; 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 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'", 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 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 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 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 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 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. 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 (name.ends_with(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 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)); 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)); 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.recordId = recordID; 699 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&data), 700 reinterpret_cast<uint8_t*>(&data) + sizeof(data)); 701 } 702 703 return 0; 704 } 705 706 } // namespace sensor 707 708 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::Context::ptr ctx, 709 ipmi::message::Payload& p) 710 { 711 constexpr const uint8_t validEnvmRev = 0x04; 712 constexpr const uint8_t lastSensorType = 0x2C; 713 constexpr const uint8_t oemReserved = 0xC0; 714 715 uint8_t sysgeneratorID = 0; 716 uint8_t evmRev = 0; 717 uint8_t sensorType = 0; 718 uint8_t sensorNum = 0; 719 uint8_t eventType = 0; 720 uint8_t eventData1 = 0; 721 std::optional<uint8_t> eventData2 = 0; 722 std::optional<uint8_t> eventData3 = 0; 723 [[maybe_unused]] uint16_t generatorID = 0; 724 ipmi::ChannelInfo chInfo; 725 726 if (ipmi::getChannelInfo(ctx->channel, chInfo) != ipmi::ccSuccess) 727 { 728 lg2::error("Failed to get Channel Info, channel: {CHANNEL}", "CHANNEL", 729 ctx->channel); 730 return ipmi::responseUnspecifiedError(); 731 } 732 733 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) == 734 ipmi::EChannelMediumType::systemInterface) 735 { 736 p.unpack(sysgeneratorID, evmRev, sensorType, sensorNum, eventType, 737 eventData1, eventData2, eventData3); 738 constexpr const uint8_t isSoftwareID = 0x01; 739 if (!(sysgeneratorID & isSoftwareID)) 740 { 741 return ipmi::responseInvalidFieldRequest(); 742 } 743 // Refer to IPMI Spec Table 32: SEL Event Records 744 generatorID = (ctx->channel << 12) // Channel 745 | (0x0 << 10) // Reserved 746 | (0x0 << 8) // 0x0 for sys-soft ID 747 | sysgeneratorID; 748 } 749 else 750 { 751 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1, 752 eventData2, eventData3); 753 // Refer to IPMI Spec Table 32: SEL Event Records 754 generatorID = (ctx->channel << 12) // Channel 755 | (0x0 << 10) // Reserved 756 | ((ctx->lun & 0x3) << 8) // Lun 757 | (ctx->rqSA << 1); 758 } 759 760 if (!p.fullyUnpacked()) 761 { 762 return ipmi::responseReqDataLenInvalid(); 763 } 764 765 // Check for valid evmRev and Sensor Type(per Table 42 of spec) 766 if (evmRev != validEnvmRev) 767 { 768 return ipmi::responseInvalidFieldRequest(); 769 } 770 if ((sensorType > lastSensorType) && (sensorType < oemReserved)) 771 { 772 return ipmi::responseInvalidFieldRequest(); 773 } 774 775 return ipmi::responseSuccess(); 776 } 777 778 ipmi::RspType<> ipmiSetSensorReading( 779 ipmi::Context::ptr ctx, uint8_t sensorNumber, uint8_t, uint8_t reading, 780 uint15_t assertOffset, bool, uint15_t, bool, uint8_t, uint8_t, uint8_t) 781 { 782 std::string connection; 783 std::string path; 784 std::vector<std::string> interfaces; 785 786 ipmi::Cc status = 787 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces); 788 if (status) 789 { 790 return ipmi::response(status); 791 } 792 793 // we can tell the sensor type by its interface type 794 if (std::find(interfaces.begin(), interfaces.end(), 795 sensor::sensorInterface) != interfaces.end()) 796 { 797 DbusInterfaceMap sensorMap; 798 if (!getSensorMap(ctx, connection, path, sensorMap)) 799 { 800 return ipmi::responseResponseError(); 801 } 802 auto sensorObject = sensorMap.find(sensor::sensorInterface); 803 if (sensorObject == sensorMap.end()) 804 { 805 return ipmi::responseResponseError(); 806 } 807 808 // Only allow external SetSensor if write permission granted 809 if (!details::sdrWriteTable.getWritePermission( 810 (ctx->lun << 8) | sensorNumber)) 811 { 812 return ipmi::responseResponseError(); 813 } 814 815 auto value = 816 sensor::calculateValue(reading, sensorMap, sensorObject->second); 817 if (!value) 818 { 819 return ipmi::responseResponseError(); 820 } 821 822 if constexpr (debug) 823 { 824 lg2::info("IPMI SET_SENSOR, sensor number: {SENSOR_NUM}, " 825 "byte: {BYTE}, value: {VALUE}", 826 "SENSOR_NUM", sensorNumber, "BYTE", (unsigned int)reading, 827 "VALUE", *value); 828 } 829 830 boost::system::error_code ec = 831 setDbusProperty(ctx, connection, path, sensor::sensorInterface, 832 "Value", ipmi::Value(*value)); 833 834 // setDbusProperty intended to resolve dbus exception/rc within the 835 // function but failed to achieve that. Catch exception in the ipmi 836 // callback functions for now (e.g. ipmiSetSensorReading). 837 if (ec) 838 { 839 lg2::error("Failed to set Value, path: {PATH}, " 840 "interface: {INTERFACE}, ERROR: {ERROR}", 841 "PATH", path, "INTERFACE", sensor::sensorInterface, 842 "ERROR", ec.message()); 843 return ipmi::responseResponseError(); 844 } 845 return ipmi::responseSuccess(); 846 } 847 848 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) != 849 interfaces.end()) 850 { 851 DbusInterfaceMap sensorMap; 852 if (!getSensorMap(ctx, connection, path, sensorMap)) 853 { 854 return ipmi::responseResponseError(); 855 } 856 auto sensorObject = sensorMap.find(sensor::vrInterface); 857 if (sensorObject == sensorMap.end()) 858 { 859 return ipmi::responseResponseError(); 860 } 861 862 // VR sensors are treated as a special case and we will not check the 863 // write permission for VR sensors, since they always deemed writable 864 // and permission table are not applied to VR sensors. 865 auto vrMode = 866 sensor::calculateVRMode(assertOffset, sensorObject->second); 867 if (!vrMode) 868 { 869 return ipmi::responseResponseError(); 870 } 871 boost::system::error_code ec = setDbusProperty( 872 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode); 873 // setDbusProperty intended to resolve dbus exception/rc within the 874 // function but failed to achieve that. Catch exception in the ipmi 875 // callback functions for now (e.g. ipmiSetSensorReading). 876 if (ec) 877 { 878 lg2::error("Failed to set Selected, path: {PATH}, " 879 "interface: {INTERFACE}, ERROR: {ERROR}", 880 "PATH", path, "INTERFACE", sensor::sensorInterface, 881 "ERROR", ec.message()); 882 } 883 return ipmi::responseSuccess(); 884 } 885 886 lg2::error("unknown sensor type, path: {PATH}", "PATH", path); 887 return ipmi::responseResponseError(); 888 } 889 890 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>> 891 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum) 892 { 893 std::string connection; 894 std::string path; 895 896 if (sensnum == reservedSensorNumber) 897 { 898 return ipmi::responseInvalidFieldRequest(); 899 } 900 901 auto status = getSensorConnection(ctx, sensnum, connection, path); 902 if (status) 903 { 904 return ipmi::response(status); 905 } 906 907 #ifdef FEATURE_HYBRID_SENSORS 908 if (auto sensor = findStaticSensor(path); 909 sensor != ipmi::sensor::sensors.end() && 910 getSensorEventTypeFromPath(path) != 911 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 912 { 913 if (ipmi::sensor::Mutability::Read != 914 (sensor->second.mutability & ipmi::sensor::Mutability::Read)) 915 { 916 return ipmi::responseIllegalCommand(); 917 } 918 919 uint8_t operation; 920 try 921 { 922 ipmi::sensor::GetSensorResponse getResponse = 923 sensor->second.getFunc(sensor->second); 924 925 if (getResponse.readingOrStateUnavailable) 926 { 927 operation |= static_cast<uint8_t>( 928 IPMISensorReadingByte2::readingStateUnavailable); 929 } 930 if (getResponse.scanningEnabled) 931 { 932 operation |= static_cast<uint8_t>( 933 IPMISensorReadingByte2::sensorScanningEnable); 934 } 935 if (getResponse.allEventMessagesEnabled) 936 { 937 operation |= static_cast<uint8_t>( 938 IPMISensorReadingByte2::eventMessagesEnable); 939 } 940 return ipmi::responseSuccess( 941 getResponse.reading, operation, 942 getResponse.thresholdLevelsStates, 943 getResponse.discreteReadingSensorStates); 944 } 945 catch (const std::exception& e) 946 { 947 operation |= static_cast<uint8_t>( 948 IPMISensorReadingByte2::readingStateUnavailable); 949 return ipmi::responseSuccess(0, operation, 0, std::nullopt); 950 } 951 } 952 #endif 953 954 DbusInterfaceMap sensorMap; 955 if (!getSensorMap(ctx, connection, path, sensorMap)) 956 { 957 return ipmi::responseResponseError(); 958 } 959 auto sensorObject = sensorMap.find(sensor::sensorInterface); 960 961 if (sensorObject == sensorMap.end() || 962 sensorObject->second.find("Value") == sensorObject->second.end()) 963 { 964 return ipmi::responseResponseError(); 965 } 966 auto& valueVariant = sensorObject->second["Value"]; 967 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 968 969 double max = 0; 970 double min = 0; 971 getSensorMaxMin(sensorMap, max, min); 972 973 int16_t mValue = 0; 974 int16_t bValue = 0; 975 int8_t rExp = 0; 976 int8_t bExp = 0; 977 bool bSigned = false; 978 979 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 980 { 981 return ipmi::responseResponseError(); 982 } 983 984 uint8_t value = 985 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 986 uint8_t operation = 987 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 988 operation |= 989 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 990 bool notReading = std::isnan(reading); 991 992 if (!notReading) 993 { 994 auto availableObject = 995 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability"); 996 if (availableObject != sensorMap.end()) 997 { 998 auto findAvailable = availableObject->second.find("Available"); 999 if (findAvailable != availableObject->second.end()) 1000 { 1001 bool* available = std::get_if<bool>(&(findAvailable->second)); 1002 if (available && !(*available)) 1003 { 1004 notReading = true; 1005 } 1006 } 1007 } 1008 } 1009 1010 if (notReading) 1011 { 1012 operation |= static_cast<uint8_t>( 1013 IPMISensorReadingByte2::readingStateUnavailable); 1014 } 1015 1016 if constexpr (details::enableInstrumentation) 1017 { 1018 int byteValue; 1019 if (bSigned) 1020 { 1021 byteValue = static_cast<int>(static_cast<int8_t>(value)); 1022 } 1023 else 1024 { 1025 byteValue = static_cast<int>(static_cast<uint8_t>(value)); 1026 } 1027 1028 // Keep stats on the reading just obtained, even if it is "NaN" 1029 if (details::sdrStatsTable.updateReading((ctx->lun << 8) | sensnum, 1030 reading, byteValue)) 1031 { 1032 // This is the first reading, show the coefficients 1033 double step = (max - min) / 255.0; 1034 lg2::error( 1035 "IPMI sensor {NAME}: Range min={MIN} max={MAX}, step={STEP}, " 1036 "Coefficients mValue={MVALUE} rExp={REXP} bValue={BVALUE} " 1037 "bExp={BEXP} bSigned={BSIGNED}", 1038 "NAME", 1039 details::sdrStatsTable.getName((ctx->lun << 8) | sensnum), 1040 "MIN", min, "MAX", max, "STEP", step, "MVALUE", mValue, "REXP", 1041 rExp, "BVALUE", bValue, "BEXP", bExp, "BSIGNED", bSigned); 1042 } 1043 } 1044 1045 uint8_t thresholds = 0; 1046 1047 auto warningObject = 1048 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1049 if (warningObject != sensorMap.end()) 1050 { 1051 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 1052 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 1053 if (alarmHigh != warningObject->second.end()) 1054 { 1055 if (std::get<bool>(alarmHigh->second)) 1056 { 1057 thresholds |= static_cast<uint8_t>( 1058 IPMISensorReadingByte3::upperNonCritical); 1059 } 1060 } 1061 if (alarmLow != warningObject->second.end()) 1062 { 1063 if (std::get<bool>(alarmLow->second)) 1064 { 1065 thresholds |= static_cast<uint8_t>( 1066 IPMISensorReadingByte3::lowerNonCritical); 1067 } 1068 } 1069 } 1070 1071 auto criticalObject = 1072 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1073 if (criticalObject != sensorMap.end()) 1074 { 1075 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 1076 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 1077 if (alarmHigh != criticalObject->second.end()) 1078 { 1079 if (std::get<bool>(alarmHigh->second)) 1080 { 1081 thresholds |= 1082 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1083 } 1084 } 1085 if (alarmLow != criticalObject->second.end()) 1086 { 1087 if (std::get<bool>(alarmLow->second)) 1088 { 1089 thresholds |= 1090 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1091 } 1092 } 1093 } 1094 1095 // no discrete as of today so optional byte is never returned 1096 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 1097 } 1098 1099 /** @brief implements the Set Sensor threshold command 1100 * @param sensorNumber - sensor number 1101 * @param lowerNonCriticalThreshMask 1102 * @param lowerCriticalThreshMask 1103 * @param lowerNonRecovThreshMask 1104 * @param upperNonCriticalThreshMask 1105 * @param upperCriticalThreshMask 1106 * @param upperNonRecovThreshMask 1107 * @param reserved 1108 * @param lowerNonCritical - lower non-critical threshold 1109 * @param lowerCritical - Lower critical threshold 1110 * @param lowerNonRecoverable - Lower non recovarable threshold 1111 * @param upperNonCritical - Upper non-critical threshold 1112 * @param upperCritical - Upper critical 1113 * @param upperNonRecoverable - Upper Non-recoverable 1114 * 1115 * @returns IPMI completion code 1116 */ 1117 ipmi::RspType<> ipmiSenSetSensorThresholds( 1118 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask, 1119 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 1120 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 1121 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 1122 uint8_t lowerCritical, [[maybe_unused]] uint8_t lowerNonRecoverable, 1123 uint8_t upperNonCritical, uint8_t upperCritical, 1124 [[maybe_unused]] uint8_t upperNonRecoverable) 1125 { 1126 if (sensorNum == reservedSensorNumber || reserved) 1127 { 1128 return ipmi::responseInvalidFieldRequest(); 1129 } 1130 1131 // lower nc and upper nc not suppported on any sensor 1132 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 1133 { 1134 return ipmi::responseInvalidFieldRequest(); 1135 } 1136 1137 // if none of the threshold mask are set, nothing to do 1138 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 1139 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 1140 upperCriticalThreshMask | upperNonRecovThreshMask)) 1141 { 1142 return ipmi::responseSuccess(); 1143 } 1144 1145 std::string connection; 1146 std::string path; 1147 1148 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path); 1149 if (status) 1150 { 1151 return ipmi::response(status); 1152 } 1153 DbusInterfaceMap sensorMap; 1154 if (!getSensorMap(ctx, connection, path, sensorMap)) 1155 { 1156 return ipmi::responseResponseError(); 1157 } 1158 1159 double max = 0; 1160 double min = 0; 1161 getSensorMaxMin(sensorMap, max, min); 1162 1163 int16_t mValue = 0; 1164 int16_t bValue = 0; 1165 int8_t rExp = 0; 1166 int8_t bExp = 0; 1167 bool bSigned = false; 1168 1169 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1170 { 1171 return ipmi::responseResponseError(); 1172 } 1173 1174 // store a vector of property name, value to set, and interface 1175 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 1176 1177 // define the indexes of the tuple 1178 constexpr uint8_t propertyName = 0; 1179 constexpr uint8_t thresholdValue = 1; 1180 constexpr uint8_t interface = 2; 1181 // verifiy all needed fields are present 1182 if (lowerCriticalThreshMask || upperCriticalThreshMask) 1183 { 1184 auto findThreshold = 1185 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1186 if (findThreshold == sensorMap.end()) 1187 { 1188 return ipmi::responseInvalidFieldRequest(); 1189 } 1190 if (lowerCriticalThreshMask) 1191 { 1192 auto findLower = findThreshold->second.find("CriticalLow"); 1193 if (findLower == findThreshold->second.end()) 1194 { 1195 return ipmi::responseInvalidFieldRequest(); 1196 } 1197 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 1198 findThreshold->first); 1199 } 1200 if (upperCriticalThreshMask) 1201 { 1202 auto findUpper = findThreshold->second.find("CriticalHigh"); 1203 if (findUpper == findThreshold->second.end()) 1204 { 1205 return ipmi::responseInvalidFieldRequest(); 1206 } 1207 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 1208 findThreshold->first); 1209 } 1210 } 1211 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 1212 { 1213 auto findThreshold = 1214 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1215 if (findThreshold == sensorMap.end()) 1216 { 1217 return ipmi::responseInvalidFieldRequest(); 1218 } 1219 if (lowerNonCriticalThreshMask) 1220 { 1221 auto findLower = findThreshold->second.find("WarningLow"); 1222 if (findLower == findThreshold->second.end()) 1223 { 1224 return ipmi::responseInvalidFieldRequest(); 1225 } 1226 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 1227 findThreshold->first); 1228 } 1229 if (upperNonCriticalThreshMask) 1230 { 1231 auto findUpper = findThreshold->second.find("WarningHigh"); 1232 if (findUpper == findThreshold->second.end()) 1233 { 1234 return ipmi::responseInvalidFieldRequest(); 1235 } 1236 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 1237 findThreshold->first); 1238 } 1239 } 1240 for (const auto& property : thresholdsToSet) 1241 { 1242 // from section 36.3 in the IPMI Spec, assume all linear 1243 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 1244 (bValue * std::pow(10.0, bExp))) * 1245 std::pow(10.0, rExp); 1246 setDbusProperty( 1247 *getSdBus(), connection, path, std::get<interface>(property), 1248 std::get<propertyName>(property), ipmi::Value(valueToSet)); 1249 } 1250 return ipmi::responseSuccess(); 1251 } 1252 1253 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap) 1254 { 1255 IPMIThresholds resp; 1256 auto warningInterface = 1257 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1258 auto criticalInterface = 1259 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1260 1261 if ((warningInterface != sensorMap.end()) || 1262 (criticalInterface != sensorMap.end())) 1263 { 1264 auto sensorPair = sensorMap.find(sensor::sensorInterface); 1265 1266 if (sensorPair == sensorMap.end()) 1267 { 1268 // should not have been able to find a sensor not implementing 1269 // the sensor object 1270 throw std::runtime_error("Invalid sensor map"); 1271 } 1272 1273 double max = 0; 1274 double min = 0; 1275 getSensorMaxMin(sensorMap, max, min); 1276 1277 int16_t mValue = 0; 1278 int16_t bValue = 0; 1279 int8_t rExp = 0; 1280 int8_t bExp = 0; 1281 bool bSigned = false; 1282 1283 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1284 { 1285 throw std::runtime_error("Invalid sensor atrributes"); 1286 } 1287 if (warningInterface != sensorMap.end()) 1288 { 1289 auto& warningMap = warningInterface->second; 1290 1291 auto warningHigh = warningMap.find("WarningHigh"); 1292 auto warningLow = warningMap.find("WarningLow"); 1293 1294 if (warningHigh != warningMap.end()) 1295 { 1296 double value = 1297 std::visit(VariantToDoubleVisitor(), warningHigh->second); 1298 if (std::isfinite(value)) 1299 { 1300 resp.warningHigh = scaleIPMIValueFromDouble( 1301 value, mValue, rExp, bValue, bExp, bSigned); 1302 } 1303 } 1304 if (warningLow != warningMap.end()) 1305 { 1306 double value = 1307 std::visit(VariantToDoubleVisitor(), warningLow->second); 1308 if (std::isfinite(value)) 1309 { 1310 resp.warningLow = scaleIPMIValueFromDouble( 1311 value, mValue, rExp, bValue, bExp, bSigned); 1312 } 1313 } 1314 } 1315 if (criticalInterface != sensorMap.end()) 1316 { 1317 auto& criticalMap = criticalInterface->second; 1318 1319 auto criticalHigh = criticalMap.find("CriticalHigh"); 1320 auto criticalLow = criticalMap.find("CriticalLow"); 1321 1322 if (criticalHigh != criticalMap.end()) 1323 { 1324 double value = 1325 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 1326 if (std::isfinite(value)) 1327 { 1328 resp.criticalHigh = scaleIPMIValueFromDouble( 1329 value, mValue, rExp, bValue, bExp, bSigned); 1330 } 1331 } 1332 if (criticalLow != criticalMap.end()) 1333 { 1334 double value = 1335 std::visit(VariantToDoubleVisitor(), criticalLow->second); 1336 if (std::isfinite(value)) 1337 { 1338 resp.criticalLow = scaleIPMIValueFromDouble( 1339 value, mValue, rExp, bValue, bExp, bSigned); 1340 } 1341 } 1342 } 1343 } 1344 return resp; 1345 } 1346 1347 ipmi::RspType<uint8_t, // readable 1348 uint8_t, // lowerNCrit 1349 uint8_t, // lowerCrit 1350 uint8_t, // lowerNrecoverable 1351 uint8_t, // upperNC 1352 uint8_t, // upperCrit 1353 uint8_t> // upperNRecoverable 1354 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber) 1355 { 1356 std::string connection; 1357 std::string path; 1358 1359 if (sensorNumber == reservedSensorNumber) 1360 { 1361 return ipmi::responseInvalidFieldRequest(); 1362 } 1363 1364 auto status = getSensorConnection(ctx, sensorNumber, connection, path); 1365 if (status) 1366 { 1367 return ipmi::response(status); 1368 } 1369 1370 DbusInterfaceMap sensorMap; 1371 if (!getSensorMap(ctx, connection, path, sensorMap)) 1372 { 1373 return ipmi::responseResponseError(); 1374 } 1375 1376 IPMIThresholds thresholdData; 1377 try 1378 { 1379 thresholdData = getIPMIThresholds(sensorMap); 1380 } 1381 catch (const std::exception&) 1382 { 1383 return ipmi::responseResponseError(); 1384 } 1385 1386 uint8_t readable = 0; 1387 uint8_t lowerNC = 0; 1388 uint8_t lowerCritical = 0; 1389 uint8_t lowerNonRecoverable = 0; 1390 uint8_t upperNC = 0; 1391 uint8_t upperCritical = 0; 1392 uint8_t upperNonRecoverable = 0; 1393 1394 if (thresholdData.warningHigh) 1395 { 1396 readable |= 1397 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 1398 upperNC = *thresholdData.warningHigh; 1399 } 1400 if (thresholdData.warningLow) 1401 { 1402 readable |= 1403 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 1404 lowerNC = *thresholdData.warningLow; 1405 } 1406 1407 if (thresholdData.criticalHigh) 1408 { 1409 readable |= 1410 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 1411 upperCritical = *thresholdData.criticalHigh; 1412 } 1413 if (thresholdData.criticalLow) 1414 { 1415 readable |= 1416 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 1417 lowerCritical = *thresholdData.criticalLow; 1418 } 1419 1420 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 1421 lowerNonRecoverable, upperNC, upperCritical, 1422 upperNonRecoverable); 1423 } 1424 1425 /** @brief implements the get Sensor event enable command 1426 * @param sensorNumber - sensor number 1427 * 1428 * @returns IPMI completion code plus response data 1429 * - enabled - Sensor Event messages 1430 * - assertionEnabledLsb - Assertion event messages 1431 * - assertionEnabledMsb - Assertion event messages 1432 * - deassertionEnabledLsb - Deassertion event messages 1433 * - deassertionEnabledMsb - Deassertion event messages 1434 */ 1435 1436 ipmi::RspType<uint8_t, // enabled 1437 uint8_t, // assertionEnabledLsb 1438 uint8_t, // assertionEnabledMsb 1439 uint8_t, // deassertionEnabledLsb 1440 uint8_t> // deassertionEnabledMsb 1441 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum) 1442 { 1443 std::string connection; 1444 std::string path; 1445 1446 uint8_t enabled = 0; 1447 uint8_t assertionEnabledLsb = 0; 1448 uint8_t assertionEnabledMsb = 0; 1449 uint8_t deassertionEnabledLsb = 0; 1450 uint8_t deassertionEnabledMsb = 0; 1451 1452 if (sensorNum == reservedSensorNumber) 1453 { 1454 return ipmi::responseInvalidFieldRequest(); 1455 } 1456 1457 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1458 if (status) 1459 { 1460 return ipmi::response(status); 1461 } 1462 1463 #ifdef FEATURE_HYBRID_SENSORS 1464 if (auto sensor = findStaticSensor(path); 1465 sensor != ipmi::sensor::sensors.end() && 1466 getSensorEventTypeFromPath(path) != 1467 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1468 { 1469 enabled = static_cast<uint8_t>( 1470 IPMISensorEventEnableByte2::sensorScanningEnable); 1471 uint16_t assertionEnabled = 0; 1472 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin() 1473 ->second.begin() 1474 ->second.second) 1475 { 1476 assertionEnabled |= (1 << offsetValMap.first); 1477 } 1478 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF)); 1479 assertionEnabledMsb = 1480 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF)); 1481 1482 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1483 assertionEnabledMsb, deassertionEnabledLsb, 1484 deassertionEnabledMsb); 1485 } 1486 #endif 1487 1488 DbusInterfaceMap sensorMap; 1489 if (!getSensorMap(ctx, connection, path, sensorMap)) 1490 { 1491 return ipmi::responseResponseError(); 1492 } 1493 1494 auto warningInterface = 1495 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1496 auto criticalInterface = 1497 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1498 if ((warningInterface != sensorMap.end()) || 1499 (criticalInterface != sensorMap.end())) 1500 { 1501 enabled = static_cast<uint8_t>( 1502 IPMISensorEventEnableByte2::sensorScanningEnable); 1503 if (warningInterface != sensorMap.end()) 1504 { 1505 auto& warningMap = warningInterface->second; 1506 1507 auto warningHigh = warningMap.find("WarningHigh"); 1508 auto warningLow = warningMap.find("WarningLow"); 1509 if (warningHigh != warningMap.end()) 1510 { 1511 double value = 1512 std::visit(VariantToDoubleVisitor(), warningHigh->second); 1513 if (std::isfinite(value)) 1514 { 1515 assertionEnabledLsb |= static_cast<uint8_t>( 1516 IPMISensorEventEnableThresholds:: 1517 upperNonCriticalGoingHigh); 1518 deassertionEnabledLsb |= static_cast<uint8_t>( 1519 IPMISensorEventEnableThresholds:: 1520 upperNonCriticalGoingLow); 1521 } 1522 } 1523 if (warningLow != warningMap.end()) 1524 { 1525 double value = 1526 std::visit(VariantToDoubleVisitor(), warningLow->second); 1527 if (std::isfinite(value)) 1528 { 1529 assertionEnabledLsb |= static_cast<uint8_t>( 1530 IPMISensorEventEnableThresholds:: 1531 lowerNonCriticalGoingLow); 1532 deassertionEnabledLsb |= static_cast<uint8_t>( 1533 IPMISensorEventEnableThresholds:: 1534 lowerNonCriticalGoingHigh); 1535 } 1536 } 1537 } 1538 if (criticalInterface != sensorMap.end()) 1539 { 1540 auto& criticalMap = criticalInterface->second; 1541 1542 auto criticalHigh = criticalMap.find("CriticalHigh"); 1543 auto criticalLow = criticalMap.find("CriticalLow"); 1544 1545 if (criticalHigh != criticalMap.end()) 1546 { 1547 double value = 1548 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 1549 if (std::isfinite(value)) 1550 { 1551 assertionEnabledMsb |= static_cast<uint8_t>( 1552 IPMISensorEventEnableThresholds:: 1553 upperCriticalGoingHigh); 1554 deassertionEnabledMsb |= static_cast<uint8_t>( 1555 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 1556 } 1557 } 1558 if (criticalLow != criticalMap.end()) 1559 { 1560 double value = 1561 std::visit(VariantToDoubleVisitor(), criticalLow->second); 1562 if (std::isfinite(value)) 1563 { 1564 assertionEnabledLsb |= static_cast<uint8_t>( 1565 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1566 deassertionEnabledLsb |= static_cast<uint8_t>( 1567 IPMISensorEventEnableThresholds:: 1568 lowerCriticalGoingHigh); 1569 } 1570 } 1571 } 1572 } 1573 1574 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1575 assertionEnabledMsb, deassertionEnabledLsb, 1576 deassertionEnabledMsb); 1577 } 1578 1579 /** @brief implements the get Sensor event status command 1580 * @param sensorNumber - sensor number, FFh = reserved 1581 * 1582 * @returns IPMI completion code plus response data 1583 * - sensorEventStatus - Sensor Event messages state 1584 * - assertions - Assertion event messages 1585 * - deassertions - Deassertion event messages 1586 */ 1587 ipmi::RspType<uint8_t, // sensorEventStatus 1588 std::bitset<16>, // assertions 1589 std::bitset<16> // deassertion 1590 > 1591 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum) 1592 { 1593 if (sensorNum == reservedSensorNumber) 1594 { 1595 return ipmi::responseInvalidFieldRequest(); 1596 } 1597 1598 std::string connection; 1599 std::string path; 1600 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1601 if (status) 1602 { 1603 lg2::error("ipmiSenGetSensorEventStatus: Sensor connection Error, " 1604 "sensor number: {SENSOR_NUM}", 1605 "SENSOR_NUM", sensorNum); 1606 return ipmi::response(status); 1607 } 1608 1609 #ifdef FEATURE_HYBRID_SENSORS 1610 if (auto sensor = findStaticSensor(path); 1611 sensor != ipmi::sensor::sensors.end() && 1612 getSensorEventTypeFromPath(path) != 1613 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1614 { 1615 auto response = ipmi::sensor::get::mapDbusToAssertion( 1616 sensor->second, path, sensor->second.sensorInterface); 1617 std::bitset<16> assertions; 1618 // deassertions are not used. 1619 std::bitset<16> deassertions = 0; 1620 uint8_t sensorEventStatus; 1621 if (response.readingOrStateUnavailable) 1622 { 1623 sensorEventStatus |= static_cast<uint8_t>( 1624 IPMISensorReadingByte2::readingStateUnavailable); 1625 } 1626 if (response.scanningEnabled) 1627 { 1628 sensorEventStatus |= static_cast<uint8_t>( 1629 IPMISensorReadingByte2::sensorScanningEnable); 1630 } 1631 if (response.allEventMessagesEnabled) 1632 { 1633 sensorEventStatus |= static_cast<uint8_t>( 1634 IPMISensorReadingByte2::eventMessagesEnable); 1635 } 1636 assertions |= response.discreteReadingSensorStates << 8; 1637 assertions |= response.thresholdLevelsStates; 1638 return ipmi::responseSuccess(sensorEventStatus, assertions, 1639 deassertions); 1640 } 1641 #endif 1642 1643 DbusInterfaceMap sensorMap; 1644 if (!getSensorMap(ctx, connection, path, sensorMap)) 1645 { 1646 lg2::error("ipmiSenGetSensorEventStatus: Sensor Mapping Error, " 1647 "sensor path: {SENSOR_PATH}", 1648 "SENSOR_PATH", path); 1649 return ipmi::responseResponseError(); 1650 } 1651 1652 uint8_t sensorEventStatus = 1653 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 1654 std::bitset<16> assertions = 0; 1655 std::bitset<16> deassertions = 0; 1656 1657 // handle VR typed sensor 1658 auto vrInterface = sensorMap.find(sensor::vrInterface); 1659 if (vrInterface != sensorMap.end()) 1660 { 1661 if (!sensor::getVrEventStatus(ctx, connection, path, 1662 vrInterface->second, assertions)) 1663 { 1664 return ipmi::responseResponseError(); 1665 } 1666 1667 // both Event Message and Sensor Scanning are disable for VR. 1668 sensorEventStatus = 0; 1669 return ipmi::responseSuccess(sensorEventStatus, assertions, 1670 deassertions); 1671 } 1672 1673 auto warningInterface = 1674 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1675 auto criticalInterface = 1676 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1677 1678 std::optional<bool> criticalDeassertHigh = 1679 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 1680 std::optional<bool> criticalDeassertLow = 1681 thresholdDeassertMap[path]["CriticalAlarmLow"]; 1682 std::optional<bool> warningDeassertHigh = 1683 thresholdDeassertMap[path]["WarningAlarmHigh"]; 1684 std::optional<bool> warningDeassertLow = 1685 thresholdDeassertMap[path]["WarningAlarmLow"]; 1686 1687 if (criticalDeassertHigh && !*criticalDeassertHigh) 1688 { 1689 deassertions.set(static_cast<size_t>( 1690 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh)); 1691 } 1692 if (criticalDeassertLow && !*criticalDeassertLow) 1693 { 1694 deassertions.set(static_cast<size_t>( 1695 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow)); 1696 } 1697 if (warningDeassertHigh && !*warningDeassertHigh) 1698 { 1699 deassertions.set(static_cast<size_t>( 1700 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh)); 1701 } 1702 if (warningDeassertLow && !*warningDeassertLow) 1703 { 1704 deassertions.set(static_cast<size_t>( 1705 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh)); 1706 } 1707 if ((warningInterface != sensorMap.end()) || 1708 (criticalInterface != sensorMap.end())) 1709 { 1710 sensorEventStatus = static_cast<size_t>( 1711 IPMISensorEventEnableByte2::eventMessagesEnable); 1712 if (warningInterface != sensorMap.end()) 1713 { 1714 auto& warningMap = warningInterface->second; 1715 1716 auto warningHigh = warningMap.find("WarningAlarmHigh"); 1717 auto warningLow = warningMap.find("WarningAlarmLow"); 1718 auto warningHighAlarm = false; 1719 auto warningLowAlarm = false; 1720 1721 if (warningHigh != warningMap.end()) 1722 { 1723 warningHighAlarm = std::get<bool>(warningHigh->second); 1724 } 1725 if (warningLow != warningMap.end()) 1726 { 1727 warningLowAlarm = std::get<bool>(warningLow->second); 1728 } 1729 if (warningHighAlarm) 1730 { 1731 assertions.set(static_cast<size_t>( 1732 IPMIGetSensorEventEnableThresholds:: 1733 upperNonCriticalGoingHigh)); 1734 } 1735 if (warningLowAlarm) 1736 { 1737 assertions.set(static_cast<size_t>( 1738 IPMIGetSensorEventEnableThresholds:: 1739 lowerNonCriticalGoingLow)); 1740 } 1741 } 1742 if (criticalInterface != sensorMap.end()) 1743 { 1744 auto& criticalMap = criticalInterface->second; 1745 1746 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1747 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1748 auto criticalHighAlarm = false; 1749 auto criticalLowAlarm = false; 1750 1751 if (criticalHigh != criticalMap.end()) 1752 { 1753 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1754 } 1755 if (criticalLow != criticalMap.end()) 1756 { 1757 criticalLowAlarm = std::get<bool>(criticalLow->second); 1758 } 1759 if (criticalHighAlarm) 1760 { 1761 assertions.set(static_cast<size_t>( 1762 IPMIGetSensorEventEnableThresholds:: 1763 upperCriticalGoingHigh)); 1764 } 1765 if (criticalLowAlarm) 1766 { 1767 assertions.set(static_cast<size_t>( 1768 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow)); 1769 } 1770 } 1771 } 1772 1773 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions); 1774 } 1775 1776 // Construct a type 1 SDR for threshold sensor. 1777 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 1778 get_sdr::SensorDataFullRecord& record) 1779 { 1780 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1781 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1782 1783 record.header.recordId = recordID; 1784 record.header.sdrVersion = ipmiSdrVersion; 1785 record.header.recordType = get_sdr::SENSOR_DATA_FULL_RECORD; 1786 record.header.recordLength = sizeof(get_sdr::SensorDataFullRecord) - 1787 sizeof(get_sdr::SensorDataRecordHeader); 1788 record.key.ownerId = bmcI2CAddr; 1789 record.key.ownerLun = lun; 1790 record.key.sensorNumber = sensornumber; 1791 } 1792 bool constructSensorSdr( 1793 ipmi::Context::ptr ctx, 1794 const std::unordered_set<std::string>& ipmiDecoratorPaths, 1795 uint16_t sensorNum, uint16_t recordID, const std::string& service, 1796 const std::string& path, get_sdr::SensorDataFullRecord& record) 1797 { 1798 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1799 1800 DbusInterfaceMap sensorMap; 1801 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 1802 { 1803 lg2::error("Failed to update sensor map for threshold sensor, " 1804 "service: {SERVICE}, path: {PATH}", 1805 "SERVICE", service, "PATH", path); 1806 return false; 1807 } 1808 1809 record.body.sensorCapabilities = 0x68; // auto rearm - todo hysteresis 1810 record.body.sensorType = getSensorTypeFromPath(path); 1811 std::string type = getSensorTypeStringFromPath(path); 1812 auto typeCstr = type.c_str(); 1813 auto findUnits = sensorUnits.find(typeCstr); 1814 if (findUnits != sensorUnits.end()) 1815 { 1816 record.body.sensorUnits2Base = static_cast<uint8_t>(findUnits->second); 1817 } // else default 0x0 unspecified 1818 1819 record.body.eventReadingType = getSensorEventTypeFromPath(path); 1820 1821 auto sensorObject = sensorMap.find(sensor::sensorInterface); 1822 if (sensorObject == sensorMap.end()) 1823 { 1824 lg2::error("constructSensorSdr: sensorObject error"); 1825 return false; 1826 } 1827 1828 uint8_t entityId = 0; 1829 uint8_t entityInstance = 0x01; 1830 1831 // follow the association chain to get the parent board's entityid and 1832 // entityInstance 1833 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, entityId, 1834 entityInstance); 1835 1836 record.body.entityId = entityId; 1837 record.body.entityInstance = entityInstance; 1838 1839 double max = 0; 1840 double min = 0; 1841 getSensorMaxMin(sensorMap, max, min); 1842 1843 int16_t mValue = 0; 1844 int8_t rExp = 0; 1845 int16_t bValue = 0; 1846 int8_t bExp = 0; 1847 bool bSigned = false; 1848 1849 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1850 { 1851 lg2::error("constructSensorSdr: getSensorAttributes error"); 1852 return false; 1853 } 1854 1855 // The record.body is a struct SensorDataFullRecordBody 1856 // from sensorhandler.hpp in phosphor-ipmi-host. 1857 // The meaning of these bits appears to come from 1858 // table 43.1 of the IPMI spec. 1859 // The above 5 sensor attributes are stuffed in as follows: 1860 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned 1861 // Byte 22-24 are for other purposes 1862 // Byte 25 = MMMMMMMM = LSB of M 1863 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance 1864 // Byte 27 = BBBBBBBB = LSB of B 1865 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy 1866 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy 1867 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed) 1868 1869 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1870 record.body.mLsb = mValue & 0xFF; 1871 1872 uint8_t mBitSign = (mValue < 0) ? 1 : 0; 1873 uint8_t mBitNine = (mValue & 0x0100) >> 8; 1874 1875 // move the smallest bit of the MSB into place (bit 9) 1876 // the MSbs are bits 7:8 in mMsbAndToLerance 1877 record.body.mMsbAndTolerance = (mBitSign << 7) | (mBitNine << 6); 1878 1879 record.body.bLsb = bValue & 0xFF; 1880 1881 uint8_t bBitSign = (bValue < 0) ? 1 : 0; 1882 uint8_t bBitNine = (bValue & 0x0100) >> 8; 1883 1884 // move the smallest bit of the MSB into place (bit 9) 1885 // the MSbs are bits 7:8 in bMsbAndAccuracyLsb 1886 record.body.bMsbAndAccuracyLsb = (bBitSign << 7) | (bBitNine << 6); 1887 1888 uint8_t rExpSign = (rExp < 0) ? 1 : 0; 1889 uint8_t rExpBits = rExp & 0x07; 1890 1891 uint8_t bExpSign = (bExp < 0) ? 1 : 0; 1892 uint8_t bExpBits = bExp & 0x07; 1893 1894 // move rExp and bExp into place 1895 record.body.rbExponents = 1896 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits; 1897 1898 // Set the analog reading byte interpretation accordingly 1899 record.body.sensorUnits1 = (bSigned ? 1 : 0) << 7; 1900 1901 // TODO(): Perhaps care about Tolerance, Accuracy, and so on 1902 // These seem redundant, but derivable from the above 5 attributes 1903 // Original comment said "todo fill out rest of units" 1904 1905 // populate sensor name from path 1906 auto name = sensor::parseSdrIdFromPath(path); 1907 get_sdr::body::setIdStrLen(name.size(), record.body); 1908 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1" 1909 std::memcpy(record.body.idString, name.c_str(), 1910 std::min(name.length() + 1, sizeof(record.body.idString))); 1911 1912 // Remember the sensor name, as determined for this sensor number 1913 details::sdrStatsTable.updateName(sensorNum, name); 1914 1915 bool sensorSettable = false; 1916 auto mutability = 1917 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability"); 1918 if (mutability != sensorMap.end()) 1919 { 1920 sensorSettable = 1921 mappedVariant<bool>(mutability->second, "Mutable", false); 1922 } 1923 get_sdr::body::initSettableState(sensorSettable, record.body); 1924 1925 // Grant write permission to sensors deemed externally settable 1926 details::sdrWriteTable.setWritePermission(sensorNum, sensorSettable); 1927 1928 IPMIThresholds thresholdData; 1929 try 1930 { 1931 thresholdData = getIPMIThresholds(sensorMap); 1932 } 1933 catch (const std::exception&) 1934 { 1935 lg2::error("constructSensorSdr: getIPMIThresholds error"); 1936 return false; 1937 } 1938 1939 if (thresholdData.criticalHigh) 1940 { 1941 record.body.upperCriticalThreshold = *thresholdData.criticalHigh; 1942 record.body.supportedDeassertions[1] |= static_cast<uint8_t>( 1943 IPMISensorEventEnableThresholds::criticalThreshold); 1944 record.body.supportedDeassertions[1] |= static_cast<uint8_t>( 1945 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1946 record.body.supportedAssertions[1] |= static_cast<uint8_t>( 1947 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1948 record.body.discreteReadingSettingMask[0] |= 1949 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1950 } 1951 if (thresholdData.warningHigh) 1952 { 1953 record.body.upperNoncriticalThreshold = *thresholdData.warningHigh; 1954 record.body.supportedDeassertions[1] |= static_cast<uint8_t>( 1955 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1956 record.body.supportedDeassertions[0] |= static_cast<uint8_t>( 1957 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1958 record.body.supportedAssertions[0] |= static_cast<uint8_t>( 1959 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1960 record.body.discreteReadingSettingMask[0] |= 1961 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1962 } 1963 if (thresholdData.criticalLow) 1964 { 1965 record.body.lowerCriticalThreshold = *thresholdData.criticalLow; 1966 record.body.supportedAssertions[1] |= static_cast<uint8_t>( 1967 IPMISensorEventEnableThresholds::criticalThreshold); 1968 record.body.supportedDeassertions[0] |= static_cast<uint8_t>( 1969 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1970 record.body.supportedAssertions[0] |= static_cast<uint8_t>( 1971 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1972 record.body.discreteReadingSettingMask[0] |= 1973 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1974 } 1975 if (thresholdData.warningLow) 1976 { 1977 record.body.lowerNoncriticalThreshold = *thresholdData.warningLow; 1978 record.body.supportedAssertions[1] |= static_cast<uint8_t>( 1979 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1980 record.body.supportedDeassertions[0] |= static_cast<uint8_t>( 1981 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1982 record.body.supportedAssertions[0] |= static_cast<uint8_t>( 1983 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1984 record.body.discreteReadingSettingMask[0] |= 1985 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1986 } 1987 1988 // everything that is readable is setable 1989 record.body.discreteReadingSettingMask[1] = 1990 record.body.discreteReadingSettingMask[0]; 1991 return true; 1992 } 1993 1994 #ifdef FEATURE_HYBRID_SENSORS 1995 // Construct a type 1 SDR for discrete Sensor typed sensor. 1996 void constructStaticSensorSdr(ipmi::Context::ptr, uint16_t sensorNum, 1997 uint16_t recordID, 1998 ipmi::sensor::IdInfoMap::const_iterator sensor, 1999 get_sdr::SensorDataFullRecord& record) 2000 { 2001 constructSensorSdrHeaderKey(sensorNum, recordID, record); 2002 2003 record.body.entityId = sensor->second.entityType; 2004 record.body.sensorType = sensor->second.sensorType; 2005 record.body.eventReadingType = sensor->second.sensorReadingType; 2006 record.body.entityInstance = sensor->second.instance; 2007 if (ipmi::sensor::Mutability::Write == 2008 (sensor->second.mutability & ipmi::sensor::Mutability::Write)) 2009 { 2010 get_sdr::body::initSettableState(true, record.body); 2011 } 2012 2013 auto idString = sensor->second.sensorName; 2014 2015 if (idString.empty()) 2016 { 2017 idString = sensor->second.sensorNameFunc(sensor->second); 2018 } 2019 2020 if (idString.length() > FULL_RECORD_ID_STR_MAX_LENGTH) 2021 { 2022 get_sdr::body::setIdStrLen(FULL_RECORD_ID_STR_MAX_LENGTH, record.body); 2023 } 2024 else 2025 { 2026 get_sdr::body::setIdStrLen(idString.length(), record.body); 2027 } 2028 get_sdr::body::setIdType(3, &record.body); // "8-bit ASCII + Latin 1" 2029 std::strncpy(record.body.idString, idString.c_str(), 2030 get_sdr::body::setIdStrLen(record.body)); 2031 } 2032 #endif 2033 2034 // Construct type 3 SDR header and key (for VR and other discrete sensors) 2035 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 2036 get_sdr::SensorDataEventRecord& record) 2037 { 2038 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 2039 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 2040 2041 record.header.recordId = recordID; 2042 record.header.sdrVersion = ipmiSdrVersion; 2043 record.header.recordType = get_sdr::SENSOR_DATA_EVENT_RECORD; 2044 record.header.recordLength = sizeof(get_sdr::SensorDataEventRecord) - 2045 sizeof(get_sdr::SensorDataRecordHeader); 2046 record.key.ownerId = bmcI2CAddr; 2047 record.key.ownerLun = lun; 2048 record.key.sensorNumber = sensornumber; 2049 2050 record.body.entityId = 0x00; 2051 record.body.entityInstance = 0x01; 2052 } 2053 2054 // Construct a type 3 SDR for VR typed sensor(daemon). 2055 bool constructVrSdr(ipmi::Context::ptr ctx, 2056 const std::unordered_set<std::string>& ipmiDecoratorPaths, 2057 uint16_t sensorNum, uint16_t recordID, 2058 const std::string& service, const std::string& path, 2059 get_sdr::SensorDataEventRecord& record) 2060 { 2061 constructEventSdrHeaderKey(sensorNum, recordID, record); 2062 2063 DbusInterfaceMap sensorMap; 2064 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 2065 { 2066 lg2::error("Failed to update sensor map for VR sensor, " 2067 "service: {SERVICE}, path: {PATH}", 2068 "SERVICE", service, "PATH", path); 2069 return false; 2070 } 2071 // follow the association chain to get the parent board's entityid and 2072 // entityInstance 2073 updateIpmiFromAssociation(path, ipmiDecoratorPaths, sensorMap, 2074 record.body.entityId, record.body.entityInstance); 2075 2076 // Sensor type is hardcoded as a module/board type instead of parsing from 2077 // sensor path. This is because VR control is allocated in an independent 2078 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by 2079 // types. 2080 static constexpr const uint8_t moduleBoardType = 0x15; 2081 record.body.sensorType = moduleBoardType; 2082 record.body.eventReadingType = 0x00; 2083 2084 record.body.sensorRecordSharing1 = 0x00; 2085 record.body.sensorRecordSharing2 = 0x00; 2086 2087 // populate sensor name from path 2088 auto name = sensor::parseSdrIdFromPath(path); 2089 int nameSize = std::min(name.size(), sizeof(record.body.idString)); 2090 get_sdr::body::setIdStrLen(nameSize, record.body); 2091 get_sdr::body::setIdType(3, record.body); // "8-bit ASCII + Latin 1" 2092 std::memset(record.body.idString, 0x00, sizeof(record.body.idString)); 2093 std::memcpy(record.body.idString, name.c_str(), nameSize); 2094 2095 // Remember the sensor name, as determined for this sensor number 2096 details::sdrStatsTable.updateName(sensorNum, name); 2097 2098 return true; 2099 } 2100 2101 uint16_t getNumberOfSensors() 2102 { 2103 return std::min(getSensorTree().size(), maxIPMISensors); 2104 } 2105 2106 static int getSensorDataRecord( 2107 ipmi::Context::ptr ctx, 2108 const std::unordered_set<std::string>& ipmiDecoratorPaths, 2109 std::vector<uint8_t>& recordData, uint16_t recordID, 2110 uint8_t readBytes = std::numeric_limits<uint8_t>::max()) 2111 { 2112 recordData.clear(); 2113 size_t lastRecord = ipmi::getNumberOfSensors() + 2114 ipmi::sensor::getOtherSensorsCount(ctx) - 1; 2115 uint16_t nextRecord(recordID + 1); 2116 2117 if (recordID == lastRecordIndex) 2118 { 2119 recordID = lastRecord; 2120 } 2121 if (recordID == lastRecord) 2122 { 2123 nextRecord = lastRecordIndex; 2124 } 2125 if (recordID > lastRecord) 2126 { 2127 lg2::error("getSensorDataRecord: recordID > lastRecord error"); 2128 return GENERAL_ERROR; 2129 } 2130 if (recordID >= ipmi::getNumberOfSensors()) 2131 { 2132 if (auto err = ipmi::sensor::getOtherSensorsDataRecord(ctx, recordID, 2133 recordData); 2134 err < 0) 2135 { 2136 return lastRecordIndex; 2137 } 2138 return nextRecord; 2139 } 2140 2141 // Perform a incremental scan of the SDR Record ID's and translate the 2142 // first 765 SDR records (i.e. maxIPMISensors) into IPMI Sensor 2143 // Numbers. The IPMI sensor numbers are not linear, and have a reserved 2144 // gap at 0xff. This code creates 254 sensors per LUN, excepting LUN 2 2145 // which has special meaning. 2146 std::string connection; 2147 std::string path; 2148 std::vector<std::string> interfaces; 2149 uint16_t sensNumFromRecID{recordID}; 2150 if ((recordID > lun0MaxSensorNum) && (recordID < lun1MaxSensorNum)) 2151 { 2152 // LUN 0 has one reserved sensor number. Compensate here by adding one 2153 // to the record ID 2154 sensNumFromRecID = recordID + 1; 2155 ctx->lun = lun1; 2156 } 2157 else if ((recordID >= lun1MaxSensorNum) && (recordID < maxIPMISensors)) 2158 { 2159 // LUN 0, 1 have a reserved sensor number. Compensate here by adding 2 2160 // to the record ID. Skip all 256 sensors in LUN 2, as it has special 2161 // rules governing its use. 2162 sensNumFromRecID = recordID + (maxSensorsPerLUN + 1) + 2; 2163 ctx->lun = lun3; 2164 } 2165 2166 auto status = 2167 getSensorConnection(ctx, static_cast<uint8_t>(sensNumFromRecID), 2168 connection, path, &interfaces); 2169 if (status) 2170 { 2171 lg2::error("getSensorDataRecord: getSensorConnection error"); 2172 return GENERAL_ERROR; 2173 } 2174 uint16_t sensorNum = getSensorNumberFromPath(path); 2175 // Return an error on LUN 2 assingments, and any sensor number beyond the 2176 // range of LUN 3 2177 if (((sensorNum > lun1MaxSensorNum) && (sensorNum <= maxIPMISensors)) || 2178 (sensorNum > lun3MaxSensorNum)) 2179 { 2180 lg2::error("getSensorDataRecord: invalidSensorNumber"); 2181 return GENERAL_ERROR; 2182 } 2183 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 2184 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 2185 2186 if ((sensornumber != static_cast<uint8_t>(sensNumFromRecID)) && 2187 (lun != ctx->lun)) 2188 { 2189 lg2::error("getSensorDataRecord: sensor record mismatch"); 2190 return GENERAL_ERROR; 2191 } 2192 2193 // Construct full record (SDR type 1) for the threshold sensors 2194 if (std::find(interfaces.begin(), interfaces.end(), 2195 sensor::sensorInterface) != interfaces.end()) 2196 { 2197 get_sdr::SensorDataFullRecord record = {}; 2198 2199 // If the request doesn't read SDR body, construct only header and key 2200 // part to avoid additional DBus transaction. 2201 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 2202 { 2203 constructSensorSdrHeaderKey(sensorNum, recordID, record); 2204 } 2205 else if (!constructSensorSdr(ctx, ipmiDecoratorPaths, sensorNum, 2206 recordID, connection, path, record)) 2207 { 2208 return GENERAL_ERROR; 2209 } 2210 2211 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record), 2212 reinterpret_cast<uint8_t*>(&record) + sizeof(record)); 2213 2214 return nextRecord; 2215 } 2216 2217 #ifdef FEATURE_HYBRID_SENSORS 2218 if (auto sensor = findStaticSensor(path); 2219 sensor != ipmi::sensor::sensors.end() && 2220 getSensorEventTypeFromPath(path) != 2221 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 2222 { 2223 get_sdr::SensorDataFullRecord record = {}; 2224 2225 // If the request doesn't read SDR body, construct only header and key 2226 // part to avoid additional DBus transaction. 2227 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 2228 { 2229 constructSensorSdrHeaderKey(sensorNum, recordID, record); 2230 } 2231 else 2232 { 2233 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record); 2234 } 2235 2236 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record), 2237 reinterpret_cast<uint8_t*>(&record) + sizeof(record)); 2238 2239 return nextRecord; 2240 } 2241 #endif 2242 2243 // Contruct SDR type 3 record for VR sensor (daemon) 2244 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) != 2245 interfaces.end()) 2246 { 2247 get_sdr::SensorDataEventRecord record = {}; 2248 2249 // If the request doesn't read SDR body, construct only header and key 2250 // part to avoid additional DBus transaction. 2251 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 2252 { 2253 constructEventSdrHeaderKey(sensorNum, recordID, record); 2254 } 2255 else if (!constructVrSdr(ctx, ipmiDecoratorPaths, sensorNum, recordID, 2256 connection, path, record)) 2257 { 2258 return GENERAL_ERROR; 2259 } 2260 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record), 2261 reinterpret_cast<uint8_t*>(&record) + sizeof(record)); 2262 } 2263 2264 return nextRecord; 2265 } 2266 2267 /** @brief implements the get SDR Info command 2268 * @param operation : 0 or not supplied returns sensor count 2269 * 1 return SDR count 2270 * 2271 * @returns IPMI completion code plus response data 2272 * - sdrCount - sensor/SDR count 2273 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag 2274 */ 2275 static ipmi::RspType<uint8_t, // respcount 2276 uint8_t, // dynamic population flags 2277 uint32_t // last time a sensor was added 2278 > 2279 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx, 2280 std::optional<uint8_t> operation) 2281 { 2282 auto& sensorTree{getSensorTree()}; 2283 uint8_t sdrCount{}; 2284 // Sensors are dynamically allocated 2285 uint8_t lunsAndDynamicPopulation{0x80}; 2286 constexpr uint8_t getSdrCount{1}; 2287 constexpr uint8_t getSensorCount{0}; 2288 2289 if (!getSensorSubtree(sensorTree) || sensorTree.empty()) 2290 { 2291 return ipmi::responseResponseError(); 2292 } 2293 uint16_t numSensors{ipmi::getNumberOfSensors()}; 2294 if (operation.value_or(0) == getSdrCount) 2295 { 2296 sdrCount = numSensors + ipmi::sensor::getOtherSensorsCount(ctx) - 1; 2297 } 2298 else if (operation.value_or(0) == getSensorCount) 2299 { 2300 // Return the number of sensors attached to the LUN 2301 if ((ctx->lun == lun0) && (numSensors > 0)) 2302 { 2303 sdrCount = 2304 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors; 2305 } 2306 else if ((ctx->lun == lun1) && (numSensors > maxSensorsPerLUN)) 2307 { 2308 sdrCount = (numSensors > (2 * maxSensorsPerLUN)) 2309 ? maxSensorsPerLUN 2310 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN; 2311 } 2312 else if (ctx->lun == lun3) 2313 { 2314 if (numSensors <= maxIPMISensors) 2315 { 2316 sdrCount = (numSensors - (2 * maxSensorsPerLUN)) & 2317 maxSensorsPerLUN; 2318 } 2319 else 2320 { 2321 throw std::out_of_range( 2322 "Maximum number of IPMI sensors exceeded."); 2323 } 2324 } 2325 } 2326 else 2327 { 2328 return ipmi::responseInvalidFieldRequest(); 2329 } 2330 2331 // Flag which LUNs have sensors associated 2332 if (numSensors > 0) 2333 { 2334 lunsAndDynamicPopulation |= 1; 2335 } 2336 if (numSensors > maxSensorsPerLUN) 2337 { 2338 lunsAndDynamicPopulation |= 2; 2339 } 2340 if (numSensors >= (maxSensorsPerLUN * 2)) 2341 { 2342 lunsAndDynamicPopulation |= 8; 2343 } 2344 if (numSensors > maxIPMISensors) 2345 { 2346 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 2347 } 2348 2349 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation, 2350 sdrLastAdd); 2351 } 2352 2353 /* end sensor commands */ 2354 2355 /* storage commands */ 2356 2357 ipmi::RspType<uint8_t, // sdr version 2358 uint16_t, // record count 2359 uint16_t, // free space 2360 uint32_t, // most recent addition 2361 uint32_t, // most recent erase 2362 uint8_t // operationSupport 2363 > 2364 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx) 2365 { 2366 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 2367 uint16_t recordCount = 2368 ipmi::getNumberOfSensors() + ipmi::sensor::getOtherSensorsCount(ctx); 2369 2370 uint8_t operationSupport = static_cast<uint8_t>( 2371 SdrRepositoryInfoOps::overflow); // write not supported 2372 2373 operationSupport |= 2374 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 2375 operationSupport |= static_cast<uint8_t>( 2376 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 2377 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 2378 unspecifiedFreeSpace, sdrLastAdd, 2379 sdrLastRemove, operationSupport); 2380 } 2381 2382 /** @brief implements the get SDR allocation info command 2383 * 2384 * @returns IPMI completion code plus response data 2385 * - allocUnits - Number of possible allocation units 2386 * - allocUnitSize - Allocation unit size in bytes. 2387 * - allocUnitFree - Number of free allocation units 2388 * - allocUnitLargestFree - Largest free block in allocation units 2389 * - maxRecordSize - Maximum record size in allocation units. 2390 */ 2391 ipmi::RspType<uint16_t, // allocUnits 2392 uint16_t, // allocUnitSize 2393 uint16_t, // allocUnitFree 2394 uint16_t, // allocUnitLargestFree 2395 uint8_t // maxRecordSize 2396 > 2397 ipmiStorageGetSDRAllocationInfo() 2398 { 2399 // 0000h unspecified number of alloc units 2400 constexpr uint16_t allocUnits = 0; 2401 2402 constexpr uint16_t allocUnitFree = 0; 2403 constexpr uint16_t allocUnitLargestFree = 0; 2404 // only allow one block at a time 2405 constexpr uint8_t maxRecordSize = 1; 2406 2407 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 2408 allocUnitLargestFree, maxRecordSize); 2409 } 2410 2411 /** @brief implements the reserve SDR command 2412 * @returns IPMI completion code plus response data 2413 * - sdrReservationID 2414 */ 2415 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 2416 { 2417 sdrReservationID++; 2418 if (sdrReservationID == 0) 2419 { 2420 sdrReservationID++; 2421 } 2422 2423 return ipmi::responseSuccess(sdrReservationID); 2424 } 2425 2426 ipmi::RspType<uint16_t, // next record ID 2427 std::vector<uint8_t> // payload 2428 > 2429 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID, 2430 uint16_t recordID, uint8_t offset, uint8_t bytesToRead) 2431 { 2432 // reservation required for partial reads with non zero offset into 2433 // record 2434 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 2435 { 2436 lg2::error("ipmiStorageGetSDR: responseInvalidReservationId"); 2437 return ipmi::responseInvalidReservationId(); 2438 } 2439 2440 auto& sensorTree = getSensorTree(); 2441 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2442 { 2443 lg2::error("ipmiStorageGetSDR: getSensorSubtree error"); 2444 return ipmi::responseResponseError(); 2445 } 2446 2447 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx); 2448 2449 std::vector<uint8_t> record; 2450 int nextRecordId = getSensorDataRecord( 2451 ctx, ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()), 2452 record, recordID, offset + bytesToRead); 2453 2454 if (nextRecordId < 0) 2455 { 2456 lg2::error("ipmiStorageGetSDR: fail to get SDR"); 2457 return ipmi::responseInvalidFieldRequest(); 2458 } 2459 get_sdr::SensorDataRecordHeader* hdr = 2460 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data()); 2461 if (!hdr) 2462 { 2463 lg2::error("ipmiStorageGetSDR: record header is null"); 2464 return ipmi::responseSuccess(nextRecordId, record); 2465 } 2466 2467 size_t sdrLength = 2468 sizeof(get_sdr::SensorDataRecordHeader) + hdr->recordLength; 2469 if (offset >= sdrLength) 2470 { 2471 lg2::error("ipmiStorageGetSDR: offset is outside the record"); 2472 return ipmi::responseParmOutOfRange(); 2473 } 2474 if (sdrLength < (offset + bytesToRead)) 2475 { 2476 bytesToRead = sdrLength - offset; 2477 } 2478 2479 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset; 2480 if (!respStart) 2481 { 2482 lg2::error("ipmiStorageGetSDR: record is null"); 2483 return ipmi::responseSuccess(nextRecordId, record); 2484 } 2485 2486 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 2487 2488 return ipmi::responseSuccess(nextRecordId, recordData); 2489 } 2490 namespace dcmi 2491 { 2492 2493 std::tuple<uint8_t, // Total of instance sensors 2494 std::vector<sensorInfo> // The list of sensors 2495 > 2496 getSensorsByEntityId(ipmi::Context::ptr ctx, uint8_t entityId, 2497 uint8_t entityInstance, uint8_t instanceStart) 2498 { 2499 std::vector<sensorInfo> sensorList; 2500 uint8_t totalInstSensor = 0; 2501 auto match = ipmi::dcmi::validEntityId.find(entityId); 2502 2503 if (match == ipmi::dcmi::validEntityId.end()) 2504 { 2505 return std::make_tuple(totalInstSensor, sensorList); 2506 } 2507 2508 auto& sensorTree = getSensorTree(); 2509 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2510 { 2511 return std::make_tuple(totalInstSensor, sensorList); 2512 } 2513 2514 auto& ipmiDecoratorPaths = getIpmiDecoratorPaths(ctx); 2515 2516 size_t invalidSensorNumberErrCount = 0; 2517 for (const auto& sensor : sensorTree) 2518 { 2519 const std::string& sensorObjPath = sensor.first; 2520 const auto& sensorTypeValue = getSensorTypeFromPath(sensorObjPath); 2521 2522 /* 2523 * In the DCMI specification, it only supports the sensor type is 0x01 2524 * (temperature type) for both Get Sensor Info and Get Temperature 2525 * Readings commands. 2526 */ 2527 if (sensorTypeValue != ipmi::dcmi::temperatureSensorType) 2528 { 2529 continue; 2530 } 2531 2532 const auto& connection = sensor.second.begin()->first; 2533 DbusInterfaceMap sensorMap; 2534 2535 if (!getSensorMap(ctx, connection, sensorObjPath, sensorMap, 2536 sensorMapSdrUpdatePeriod)) 2537 { 2538 lg2::error("Failed to update sensor map for threshold sensor, " 2539 "service: {SERVICE}, path: {PATH}", 2540 "SERVICE", connection, "PATH", sensorObjPath); 2541 continue; 2542 } 2543 2544 uint8_t entityIdValue = 0; 2545 uint8_t entityInstanceValue = 0; 2546 2547 /* 2548 * Get the Entity ID, Entity Instance information which are configured 2549 * in the Entity-Manger. 2550 */ 2551 updateIpmiFromAssociation( 2552 sensorObjPath, 2553 ipmiDecoratorPaths.value_or(std::unordered_set<std::string>()), 2554 sensorMap, entityIdValue, entityInstanceValue); 2555 2556 if (entityIdValue == match->first || entityIdValue == match->second) 2557 { 2558 totalInstSensor++; 2559 2560 /* 2561 * When Entity Instance parameter is not 0, we only get the first 2562 * sensor whose Entity Instance number is equal input Entity 2563 * Instance parameter. 2564 */ 2565 if (entityInstance) 2566 { 2567 if (!sensorList.empty()) 2568 { 2569 continue; 2570 } 2571 2572 if (entityInstanceValue == entityInstance) 2573 { 2574 auto recordId = getSensorNumberFromPath(sensorObjPath); 2575 if (recordId == invalidSensorNumber) 2576 { 2577 ++invalidSensorNumberErrCount; 2578 continue; 2579 } 2580 sensorList.emplace_back(sensorObjPath, sensorTypeValue, 2581 recordId, entityIdValue, 2582 entityInstanceValue); 2583 } 2584 } 2585 else if (entityInstanceValue >= instanceStart) 2586 { 2587 auto recordId = getSensorNumberFromPath(sensorObjPath); 2588 if (recordId == invalidSensorNumber) 2589 { 2590 ++invalidSensorNumberErrCount; 2591 continue; 2592 } 2593 sensorList.emplace_back(sensorObjPath, sensorTypeValue, 2594 recordId, entityIdValue, 2595 entityInstanceValue); 2596 } 2597 } 2598 } 2599 if (invalidSensorNumberErrCount != 0) 2600 { 2601 lg2::error("getSensorNumberFromPath returned invalidSensorNumber " 2602 "{ERR_COUNT} times", 2603 "ERR_COUNT", invalidSensorNumberErrCount); 2604 } 2605 2606 auto cmpFunc = [](sensorInfo first, sensorInfo second) { 2607 return first.entityInstance <= second.entityInstance; 2608 }; 2609 2610 sort(sensorList.begin(), sensorList.end(), cmpFunc); 2611 2612 return std::make_tuple(totalInstSensor, sensorList); 2613 } 2614 2615 std::tuple<bool, // Reading result 2616 uint7_t, // Temp value 2617 bool> // Sign bit 2618 readTemp(ipmi::Context::ptr ctx, const std::string& objectPath) 2619 { 2620 std::string service{}; 2621 boost::system::error_code ec = 2622 ipmi::getService(ctx, sensor::sensorInterface, objectPath, service); 2623 if (ec.value()) 2624 { 2625 return std::make_tuple(false, 0, false); 2626 } 2627 2628 ipmi::PropertyMap properties{}; 2629 ec = ipmi::getAllDbusProperties(ctx, service, objectPath, 2630 sensor::sensorInterface, properties); 2631 if (ec.value()) 2632 { 2633 return std::make_tuple(false, 0, false); 2634 } 2635 2636 auto scaleIt = properties.find("Scale"); 2637 double scaleVal = 0.0; 2638 if (scaleIt != properties.end()) 2639 { 2640 scaleVal = std::visit(ipmi::VariantToDoubleVisitor(), scaleIt->second); 2641 } 2642 2643 auto tempValIt = properties.find("Value"); 2644 double tempVal = 0.0; 2645 if (tempValIt == properties.end()) 2646 { 2647 return std::make_tuple(false, 0, false); 2648 } 2649 2650 const double maxTemp = 127; 2651 double absTempVal = 0.0; 2652 bool signBit = false; 2653 2654 tempVal = std::visit(ipmi::VariantToDoubleVisitor(), tempValIt->second); 2655 tempVal = std::pow(10, scaleVal) * tempVal; 2656 absTempVal = std::abs(tempVal); 2657 absTempVal = std::min(absTempVal, maxTemp); 2658 signBit = (tempVal < 0) ? true : false; 2659 2660 return std::make_tuple(true, static_cast<uint7_t>(absTempVal), signBit); 2661 } 2662 2663 ipmi::RspType<uint8_t, // No of instances for requested id 2664 uint8_t, // No of record ids in the response 2665 std::vector<uint16_t> // SDR Record ID corresponding to the Entity 2666 // IDs 2667 > 2668 getSensorInfo(ipmi::Context::ptr ctx, uint8_t sensorType, uint8_t entityId, 2669 uint8_t entityInstance, uint8_t instanceStart) 2670 { 2671 auto match = ipmi::dcmi::validEntityId.find(entityId); 2672 if (match == ipmi::dcmi::validEntityId.end()) 2673 { 2674 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId); 2675 2676 return ipmi::responseInvalidFieldRequest(); 2677 } 2678 2679 if (sensorType != ipmi::dcmi::temperatureSensorType) 2680 { 2681 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE", 2682 sensorType); 2683 2684 return ipmi::responseInvalidFieldRequest(); 2685 } 2686 2687 std::vector<uint16_t> sensorRec{}; 2688 const auto& [totalSensorInst, sensorList] = 2689 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart); 2690 2691 if (sensorList.empty()) 2692 { 2693 return ipmi::responseSuccess(totalSensorInst, 0, sensorRec); 2694 } 2695 2696 /* 2697 * As DCMI specification, the maximum number of Record Ids of response data 2698 * is 1 if Entity Instance paramter is not 0. Else the maximum number of 2699 * Record Ids of response data is 8. Therefore, not all of sensors are shown 2700 * in response data. 2701 */ 2702 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords; 2703 2704 for (const auto& sensor : sensorList) 2705 { 2706 sensorRec.emplace_back(sensor.recordId); 2707 if (sensorRec.size() >= numOfRec) 2708 { 2709 break; 2710 } 2711 } 2712 2713 return ipmi::responseSuccess( 2714 totalSensorInst, static_cast<uint8_t>(sensorRec.size()), sensorRec); 2715 } 2716 2717 ipmi::RspType<uint8_t, // No of instances for requested id 2718 uint8_t, // No of record ids in the response 2719 std::vector< // Temperature Data 2720 std::tuple<uint7_t, // Temperature value 2721 bool, // Sign bit 2722 uint8_t // Entity Instance of sensor 2723 >>> 2724 getTempReadings(ipmi::Context::ptr ctx, uint8_t sensorType, 2725 uint8_t entityId, uint8_t entityInstance, 2726 uint8_t instanceStart) 2727 { 2728 auto match = ipmi::dcmi::validEntityId.find(entityId); 2729 if (match == ipmi::dcmi::validEntityId.end()) 2730 { 2731 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId); 2732 2733 return ipmi::responseInvalidFieldRequest(); 2734 } 2735 2736 if (sensorType != ipmi::dcmi::temperatureSensorType) 2737 { 2738 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE", 2739 sensorType); 2740 2741 return ipmi::responseInvalidFieldRequest(); 2742 } 2743 2744 std::vector<std::tuple<uint7_t, bool, uint8_t>> tempReadingVal{}; 2745 const auto& [totalSensorInst, sensorList] = 2746 getSensorsByEntityId(ctx, entityId, entityInstance, instanceStart); 2747 2748 if (sensorList.empty()) 2749 { 2750 return ipmi::responseSuccess(totalSensorInst, 0, tempReadingVal); 2751 } 2752 2753 /* 2754 * As DCMI specification, the maximum number of Record Ids of response data 2755 * is 1 if Entity Instance paramter is not 0. Else the maximum number of 2756 * Record Ids of response data is 8. Therefore, not all of sensors are shown 2757 * in response data. 2758 */ 2759 uint8_t numOfRec = (entityInstance != 0) ? 1 : ipmi::dcmi::maxRecords; 2760 2761 for (const auto& sensor : sensorList) 2762 { 2763 const auto& [readResult, tempVal, signBit] = 2764 readTemp(ctx, sensor.objectPath); 2765 2766 if (readResult) 2767 { 2768 tempReadingVal.emplace_back( 2769 std::make_tuple(tempVal, signBit, sensor.entityInstance)); 2770 2771 if (tempReadingVal.size() >= numOfRec) 2772 { 2773 break; 2774 } 2775 } 2776 } 2777 2778 return ipmi::responseSuccess(totalSensorInst, 2779 static_cast<uint8_t>(tempReadingVal.size()), 2780 tempReadingVal); 2781 } 2782 2783 } // namespace dcmi 2784 2785 /* end storage commands */ 2786 2787 void registerSensorFunctions() 2788 { 2789 // <Platform Event> 2790 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2791 ipmi::sensor_event::cmdPlatformEvent, 2792 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 2793 2794 // <Set Sensor Reading and Event Status> 2795 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2796 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts, 2797 ipmi::Privilege::Operator, ipmiSetSensorReading); 2798 2799 // <Get Sensor Reading> 2800 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2801 ipmi::sensor_event::cmdGetSensorReading, 2802 ipmi::Privilege::User, ipmiSenGetSensorReading); 2803 2804 // <Get Sensor Threshold> 2805 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2806 ipmi::sensor_event::cmdGetSensorThreshold, 2807 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 2808 2809 // <Set Sensor Threshold> 2810 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2811 ipmi::sensor_event::cmdSetSensorThreshold, 2812 ipmi::Privilege::Operator, 2813 ipmiSenSetSensorThresholds); 2814 2815 // <Get Sensor Event Enable> 2816 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2817 ipmi::sensor_event::cmdGetSensorEventEnable, 2818 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 2819 2820 // <Get Sensor Event Status> 2821 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2822 ipmi::sensor_event::cmdGetSensorEventStatus, 2823 ipmi::Privilege::User, ipmiSenGetSensorEventStatus); 2824 2825 // register all storage commands for both Sensor and Storage command 2826 // versions 2827 2828 // <Get SDR Repository Info> 2829 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2830 ipmi::storage::cmdGetSdrRepositoryInfo, 2831 ipmi::Privilege::User, 2832 ipmiStorageGetSDRRepositoryInfo); 2833 2834 // <Get Device SDR Info> 2835 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2836 ipmi::sensor_event::cmdGetDeviceSdrInfo, 2837 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo); 2838 2839 // <Get SDR Allocation Info> 2840 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2841 ipmi::storage::cmdGetSdrRepositoryAllocInfo, 2842 ipmi::Privilege::User, 2843 ipmiStorageGetSDRAllocationInfo); 2844 2845 // <Reserve SDR Repo> 2846 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2847 ipmi::sensor_event::cmdReserveDeviceSdrRepository, 2848 ipmi::Privilege::User, ipmiStorageReserveSDR); 2849 2850 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2851 ipmi::storage::cmdReserveSdrRepository, 2852 ipmi::Privilege::User, ipmiStorageReserveSDR); 2853 2854 // <Get Sdr> 2855 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2856 ipmi::sensor_event::cmdGetDeviceSdr, 2857 ipmi::Privilege::User, ipmiStorageGetSDR); 2858 2859 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2860 ipmi::storage::cmdGetSdr, ipmi::Privilege::User, 2861 ipmiStorageGetSDR); 2862 // <Get DCMI Sensor Info> 2863 ipmi::registerGroupHandler( 2864 ipmi::prioOpenBmcBase, ipmi::groupDCMI, 2865 ipmi::dcmi::cmdGetDcmiSensorInfo, ipmi::Privilege::Operator, 2866 ipmi::dcmi::getSensorInfo); 2867 // <Get Temperature Readings> 2868 ipmi::registerGroupHandler( 2869 ipmi::prioOpenBmcBase, ipmi::groupDCMI, 2870 ipmi::dcmi::cmdGetTemperatureReadings, ipmi::Privilege::User, 2871 ipmi::dcmi::getTempReadings); 2872 } 2873 } // namespace ipmi 2874