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