1 #include "config.h" 2 3 #include "chassis.hpp" 4 5 #include <iostream> 6 7 using namespace phosphor::logging; 8 using namespace phosphor::power::util; 9 namespace phosphor::power::chassis 10 { 11 12 constexpr auto powerSystemsInputsObjPath = 13 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus"; 14 constexpr auto IBMCFFPSInterface = 15 "xyz.openbmc_project.Configuration.IBMCFFPSConnector"; 16 constexpr auto chassisIdProp = "SlotNumber"; 17 constexpr auto i2cBusProp = "I2CBus"; 18 constexpr auto i2cAddressProp = "I2CAddress"; 19 constexpr auto psuNameProp = "Name"; 20 constexpr auto presLineName = "NamedPresenceGpio"; 21 constexpr auto supportedConfIntf = 22 "xyz.openbmc_project.Configuration.SupportedConfiguration"; 23 const auto deviceDirPath = "/sys/bus/i2c/devices/"; 24 const auto driverDirName = "/driver"; 25 26 const auto entityMgrService = "xyz.openbmc_project.EntityManager"; 27 const auto decoratorChassisId = "xyz.openbmc_project.Inventory.Decorator.Slot"; 28 29 constexpr auto INPUT_HISTORY_SYNC_DELAY = 5; 30 31 Chassis::Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath, 32 const sdeventplus::Event& e) : 33 bus(bus), chassisPath(chassisPath), 34 chassisPathUniqueId(getChassisPathUniqueId(chassisPath)), 35 powerSystemInputs( 36 bus, std::format(powerSystemsInputsObjPath, chassisPathUniqueId)), 37 eventLoop(e) 38 { 39 saveChassisName(); 40 getPSUConfiguration(); 41 getSupportedConfiguration(); 42 } 43 44 void Chassis::getPSUConfiguration() 45 { 46 auto depth = 0; 47 48 try 49 { 50 if (chassisPathUniqueId == invalidObjectPathUniqueId) 51 { 52 lg2::error("Chassis does not have chassis ID: {CHASSISPATH}", 53 "CHASSISPATH", chassisPath); 54 return; 55 } 56 auto connectorsSubTree = getSubTree(bus, "/", IBMCFFPSInterface, depth); 57 for (const auto& [path, services] : connectorsSubTree) 58 { 59 if (chassisPathUniqueId == getParentEMUniqueId(bus, path)) 60 { 61 // For each object in the array of objects, I want 62 // to get properties from the service, path, and 63 // interface. 64 auto properties = getAllProperties(bus, path, IBMCFFPSInterface, 65 entityMgrService); 66 getPSUProperties(properties); 67 } 68 } 69 } 70 catch (const sdbusplus::exception_t& e) 71 { 72 lg2::error("Failed while getting configuration - exception: {ERROR}", 73 "ERROR", e); 74 } 75 76 if (psus.empty()) 77 { 78 // Interface or properties not found. Let the Interfaces Added callback 79 // process the information once the interfaces are added to D-Bus. 80 lg2::info("No power supplies to monitor"); 81 } 82 } 83 84 void Chassis::getPSUProperties(util::DbusPropertyMap& properties) 85 { 86 std::string basePSUInvPath = chassisPath + "/motherboard/powersupply"; 87 88 // From passed in properties, I want to get: I2CBus, I2CAddress, 89 // and Name. Create a power supply object, using Name to build the inventory 90 // path. 91 92 uint64_t* i2cbus = nullptr; 93 uint64_t* i2caddr = nullptr; 94 std::string* psuname = nullptr; 95 std::string* preslineptr = nullptr; 96 97 for (const auto& property : properties) 98 { 99 try 100 { 101 if (property.first == i2cBusProp) 102 { 103 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]); 104 } 105 else if (property.first == i2cAddressProp) 106 { 107 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]); 108 } 109 else if (property.first == psuNameProp) 110 { 111 psuname = std::get_if<std::string>(&properties[psuNameProp]); 112 } 113 else if (property.first == presLineName) 114 { 115 preslineptr = 116 std::get_if<std::string>(&properties[presLineName]); 117 } 118 } 119 catch (const std::exception& e) 120 {} 121 } 122 123 if (i2cbus && i2caddr && psuname && !psuname->empty()) 124 { 125 std::string invpath = basePSUInvPath; 126 invpath.push_back(psuname->back()); 127 std::string presline = ""; 128 129 lg2::debug("Inventory Path: {INVPATH}", "INVPATH", invpath); 130 131 if (nullptr != preslineptr) 132 { 133 presline = *preslineptr; 134 } 135 136 auto invMatch = 137 std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) { 138 return psu->getInventoryPath() == invpath; 139 }); 140 if (invMatch != psus.end()) 141 { 142 // This power supply has the same inventory path as the one with 143 // information just added to D-Bus. 144 // Changes to GPIO line name unlikely, so skip checking. 145 // Changes to the I2C bus and address unlikely, as that would 146 // require corresponding device tree updates. 147 // Return out to avoid duplicate object creation. 148 return; 149 } 150 151 buildDriverName(*i2cbus, *i2caddr); 152 lg2::debug( 153 "make PowerSupply bus: {I2CBUS} addr: {I2CADDR} presline: {PRESLINE}", 154 "I2CBUS", *i2cbus, "I2CADDR", *i2caddr, "PRESLINE", presline); 155 156 auto psu = std::make_unique<PowerSupply>( 157 bus, invpath, *i2cbus, *i2caddr, driverName, presline, 158 std::bind(&Chassis::isPowerOn, this), chassisShortName); 159 psus.emplace_back(std::move(psu)); 160 161 // Subscribe to power supply presence changes 162 auto presenceMatch = std::make_unique<sdbusplus::bus::match_t>( 163 bus, 164 sdbusplus::bus::match::rules::propertiesChanged(invpath, 165 INVENTORY_IFACE), 166 [this](auto& msg) { this->psuPresenceChanged(msg); }); 167 presenceMatches.emplace_back(std::move(presenceMatch)); 168 } 169 if (psus.empty()) 170 { 171 lg2::info("No power supplies to monitor"); 172 } 173 else 174 { 175 populateDriverName(); 176 } 177 } 178 179 void Chassis::getSupportedConfiguration() 180 { 181 try 182 { 183 util::DbusSubtree subtree = 184 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0); 185 if (subtree.empty()) 186 { 187 throw std::runtime_error("Supported Configuration Not Found"); 188 } 189 190 for (const auto& [objPath, services] : subtree) 191 { 192 std::string service = services.begin()->first; 193 if (objPath.empty() || service.empty()) 194 { 195 continue; 196 } 197 198 if (chassisPathUniqueId == getParentEMUniqueId(bus, objPath)) 199 { 200 auto properties = util::getAllProperties( 201 bus, objPath, supportedConfIntf, service); 202 populateSupportedConfiguration(properties); 203 break; 204 } 205 } 206 } 207 catch (const std::exception& e) 208 { 209 // Interface or property not found. Let the Interfaces Added callback 210 // process the information once the interfaces are added to D-Bus. 211 lg2::info("Interface or Property not found, error {ERROR}", "ERROR", e); 212 } 213 } 214 215 void Chassis::populateSupportedConfiguration( 216 const util::DbusPropertyMap& properties) 217 { 218 try 219 { 220 auto propIt = properties.find("SupportedType"); 221 if (propIt == properties.end()) 222 { 223 return; 224 } 225 const std::string* type = std::get_if<std::string>(&(propIt->second)); 226 if ((type == nullptr) || (*type != "PowerSupply")) 227 { 228 return; 229 } 230 231 propIt = properties.find("SupportedModel"); 232 if (propIt == properties.end()) 233 { 234 return; 235 } 236 const std::string* model = std::get_if<std::string>(&(propIt->second)); 237 if (model == nullptr) 238 { 239 return; 240 } 241 242 SupportedPsuConfiguration supportedPsuConfig; 243 propIt = properties.find("RedundantCount"); 244 if (propIt != properties.end()) 245 { 246 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second)); 247 if (count != nullptr) 248 { 249 supportedPsuConfig.powerSupplyCount = *count; 250 } 251 } 252 propIt = properties.find("InputVoltage"); 253 if (propIt != properties.end()) 254 { 255 const std::vector<uint64_t>* voltage = 256 std::get_if<std::vector<uint64_t>>(&(propIt->second)); 257 if (voltage != nullptr) 258 { 259 supportedPsuConfig.inputVoltage = *voltage; 260 } 261 } 262 263 // The PowerConfigFullLoad is an optional property, default it to false 264 // since that's the default value of the power-config-full-load GPIO. 265 supportedPsuConfig.powerConfigFullLoad = false; 266 propIt = properties.find("PowerConfigFullLoad"); 267 if (propIt != properties.end()) 268 { 269 const bool* fullLoad = std::get_if<bool>(&(propIt->second)); 270 if (fullLoad != nullptr) 271 { 272 supportedPsuConfig.powerConfigFullLoad = *fullLoad; 273 } 274 } 275 276 supportedConfigs.emplace(*model, supportedPsuConfig); 277 } 278 catch (const std::exception& e) 279 { 280 lg2::info("populateSupportedConfiguration error {ERR}", "ERR", e); 281 } 282 } 283 284 void Chassis::psuPresenceChanged(sdbusplus::message_t& msg) 285 { 286 std::string msgSensor; 287 std::map<std::string, std::variant<uint32_t, bool>> msgData; 288 msg.read(msgSensor, msgData); 289 290 // Check if it was the Present property that changed. 291 auto valPropMap = msgData.find(PRESENT_PROP); 292 if (valPropMap != msgData.end()) 293 { 294 if (std::get<bool>(valPropMap->second)) 295 { 296 // A PSU became present, force the PSU validation to run. 297 runValidateConfig = true; 298 validationTimer->restartOnce(validationTimeout); 299 } 300 } 301 } 302 303 void Chassis::buildDriverName(uint64_t i2cbus, uint64_t i2caddr) 304 { 305 namespace fs = std::filesystem; 306 std::stringstream ss; 307 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr; 308 std::string symLinkPath = 309 deviceDirPath + std::to_string(i2cbus) + "-" + ss.str() + driverDirName; 310 try 311 { 312 fs::path linkStrPath = fs::read_symlink(symLinkPath); 313 driverName = linkStrPath.filename(); 314 } 315 catch (const std::exception& e) 316 { 317 lg2::error( 318 "Failed to find device driver {SYM_LINK_PATH}, error {ERROR_STR}", 319 "SYM_LINK_PATH", symLinkPath, "ERROR_STR", e); 320 } 321 } 322 323 void Chassis::populateDriverName() 324 { 325 std::string driverName; 326 // Search in PSUs for driver name 327 std::for_each(psus.begin(), psus.end(), [&driverName](auto& psu) { 328 if (!psu->getDriverName().empty()) 329 { 330 driverName = psu->getDriverName(); 331 } 332 }); 333 // Assign driver name to all PSUs 334 std::for_each(psus.begin(), psus.end(), 335 [&driverName](auto& psu) { psu->setDriverName(driverName); }); 336 } 337 338 uint64_t Chassis::getChassisPathUniqueId(const std::string& path) 339 { 340 try 341 { 342 return getChassisInventoryUniqueId(bus, path); 343 } 344 catch (const sdbusplus::exception_t& e) 345 { 346 lg2::error( 347 "Failed to find chassis path {CHASSIS_PATH} ID - exception: {ERROR}", 348 "CHASSIS_PATH", path, "ERROR", e); 349 } 350 return invalidObjectPathUniqueId; 351 } 352 353 void Chassis::initPowerMonitoring() 354 { 355 using namespace sdeventplus; 356 357 validationTimer = std::make_unique<utility::Timer<ClockId::Monotonic>>( 358 eventLoop, std::bind(&Chassis::validateConfig, this)); 359 attemptToCreatePowerConfigGPIO(); 360 361 // Subscribe to power state changes 362 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus); 363 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>( 364 bus, 365 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH, 366 POWER_IFACE), 367 [this](auto& msg) { this->powerStateChanged(msg); }); 368 // TODO initialize the chassis 369 } 370 371 void Chassis::validateConfig() 372 { 373 if (!runValidateConfig || supportedConfigs.empty() || psus.empty()) 374 { 375 return; 376 } 377 378 for (const auto& psu : psus) 379 { 380 if ((psu->hasInputFault() || psu->hasVINUVFault()) && psu->isPresent()) 381 { 382 // Do not try to validate if input voltage fault present. 383 validationTimer->restartOnce(validationTimeout); 384 return; 385 } 386 } 387 388 std::map<std::string, std::string> additionalData; 389 auto supported = hasRequiredPSUs(additionalData); 390 if (supported) 391 { 392 runValidateConfig = false; 393 double actualVoltage; 394 int inputVoltage; 395 int previousInputVoltage = 0; 396 bool voltageMismatch = false; 397 398 for (const auto& psu : psus) 399 { 400 if (!psu->isPresent()) 401 { 402 // Only present PSUs report a valid input voltage 403 continue; 404 } 405 psu->getInputVoltage(actualVoltage, inputVoltage); 406 if (previousInputVoltage && inputVoltage && 407 (previousInputVoltage != inputVoltage)) 408 { 409 additionalData["EXPECTED_VOLTAGE"] = 410 std::to_string(previousInputVoltage); 411 additionalData["ACTUAL_VOLTAGE"] = 412 std::to_string(actualVoltage); 413 voltageMismatch = true; 414 } 415 if (!previousInputVoltage && inputVoltage) 416 { 417 previousInputVoltage = inputVoltage; 418 } 419 } 420 if (!voltageMismatch) 421 { 422 return; 423 } 424 } 425 426 // Validation failed, create an error log. 427 // Return without setting the runValidateConfig flag to false because 428 // it may be that an additional supported configuration interface is 429 // added and we need to validate it to see if it matches this system. 430 createError("xyz.openbmc_project.Power.PowerSupply.Error.NotSupported", 431 additionalData); 432 } 433 434 void Chassis::syncHistory() 435 { 436 if (driverName != ACBEL_FSG032_DD_NAME) 437 { 438 if (!syncHistoryGPIO) 439 { 440 try 441 { 442 syncHistoryGPIO = createGPIO(INPUT_HISTORY_SYNC_GPIO); 443 } 444 catch (const std::exception& e) 445 { 446 // Not an error, system just hasn't implemented the synch gpio 447 lg2::info("No synchronization GPIO found"); 448 syncHistoryGPIO = nullptr; 449 } 450 } 451 if (syncHistoryGPIO) 452 { 453 const std::chrono::milliseconds delay{INPUT_HISTORY_SYNC_DELAY}; 454 lg2::info("Synchronize INPUT_HISTORY"); 455 syncHistoryGPIO->toggleLowHigh(delay); 456 lg2::info("Synchronize INPUT_HISTORY completed"); 457 } 458 } 459 460 // Always clear synch history required after calling this function 461 for (auto& psu : psus) 462 { 463 psu->clearSyncHistoryRequired(); 464 } 465 } 466 467 void Chassis::analyze() 468 { 469 auto syncHistoryRequired = 470 std::any_of(psus.begin(), psus.end(), [](const auto& psu) { 471 return psu->isSyncHistoryRequired(); 472 }); 473 if (syncHistoryRequired) 474 { 475 syncHistory(); 476 } 477 478 for (auto& psu : psus) 479 { 480 psu->analyze(); 481 } 482 483 analyzeBrownout(); 484 485 // Only perform individual PSU analysis if power is on and a brownout has 486 // not already been logged 487 // 488 // Note: TODO Check the chassis state when the power sequencer publishes 489 // chassis power on and system power on 490 if (powerOn && !brownoutLogged) 491 { 492 for (auto& psu : psus) 493 { 494 std::map<std::string, std::string> additionalData; 495 496 if (!psu->isFaultLogged() && !psu->isPresent() && 497 !validationTimer->isEnabled()) 498 { 499 std::map<std::string, std::string> requiredPSUsData; 500 auto requiredPSUsPresent = hasRequiredPSUs(requiredPSUsData); 501 // TODO check required PSU 502 503 if (!requiredPSUsPresent) 504 { 505 additionalData.merge(requiredPSUsData); 506 // Create error for power supply missing. 507 additionalData["CALLOUT_INVENTORY_PATH"] = 508 psu->getInventoryPath(); 509 additionalData["CALLOUT_PRIORITY"] = "H"; 510 createError( 511 "xyz.openbmc_project.Power.PowerSupply.Error.Missing", 512 additionalData); 513 } 514 psu->setFaultLogged(); 515 } 516 else if (!psu->isFaultLogged() && psu->isFaulted()) 517 { 518 // Add STATUS_WORD and STATUS_MFR last response, in padded 519 // hexadecimal format. 520 additionalData["STATUS_WORD"] = 521 std::format("{:#04x}", psu->getStatusWord()); 522 additionalData["STATUS_MFR"] = 523 std::format("{:#02x}", psu->getMFRFault()); 524 // If there are faults being reported, they possibly could be 525 // related to a bug in the firmware version running on the power 526 // supply. Capture that data into the error as well. 527 additionalData["FW_VERSION"] = psu->getFWVersion(); 528 529 if (psu->hasCommFault()) 530 { 531 additionalData["STATUS_CML"] = 532 std::format("{:#02x}", psu->getStatusCML()); 533 /* Attempts to communicate with the power supply have 534 * reached there limit. Create an error. */ 535 additionalData["CALLOUT_DEVICE_PATH"] = 536 psu->getDevicePath(); 537 538 createError( 539 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault", 540 additionalData); 541 542 psu->setFaultLogged(); 543 } 544 else if ((psu->hasInputFault() || psu->hasVINUVFault())) 545 { 546 // Include STATUS_INPUT for input faults. 547 additionalData["STATUS_INPUT"] = 548 std::format("{:#02x}", psu->getStatusInput()); 549 550 /* The power supply location might be needed if the input 551 * fault is due to a problem with the power supply itself. 552 * Include the inventory path with a call out priority of 553 * low. 554 */ 555 additionalData["CALLOUT_INVENTORY_PATH"] = 556 psu->getInventoryPath(); 557 additionalData["CALLOUT_PRIORITY"] = "L"; 558 createError("xyz.openbmc_project.Power.PowerSupply.Error." 559 "InputFault", 560 additionalData); 561 psu->setFaultLogged(); 562 } 563 else if (psu->hasPSKillFault()) 564 { 565 createError( 566 "xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault", 567 additionalData); 568 psu->setFaultLogged(); 569 } 570 else if (psu->hasVoutOVFault()) 571 { 572 // Include STATUS_VOUT for Vout faults. 573 additionalData["STATUS_VOUT"] = 574 std::format("{:#02x}", psu->getStatusVout()); 575 576 additionalData["CALLOUT_INVENTORY_PATH"] = 577 psu->getInventoryPath(); 578 579 createError( 580 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 581 additionalData); 582 583 psu->setFaultLogged(); 584 } 585 else if (psu->hasIoutOCFault()) 586 { 587 // Include STATUS_IOUT for Iout faults. 588 additionalData["STATUS_IOUT"] = 589 std::format("{:#02x}", psu->getStatusIout()); 590 591 createError( 592 "xyz.openbmc_project.Power.PowerSupply.Error.IoutOCFault", 593 additionalData); 594 595 psu->setFaultLogged(); 596 } 597 else if (psu->hasVoutUVFault() || psu->hasPS12VcsFault() || 598 psu->hasPSCS12VFault()) 599 { 600 // Include STATUS_VOUT for Vout faults. 601 additionalData["STATUS_VOUT"] = 602 std::format("{:#02x}", psu->getStatusVout()); 603 604 additionalData["CALLOUT_INVENTORY_PATH"] = 605 psu->getInventoryPath(); 606 607 createError( 608 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 609 additionalData); 610 611 psu->setFaultLogged(); 612 } 613 // A fan fault should have priority over a temperature fault, 614 // since a failed fan may lead to a temperature problem. 615 // Only process if not in power fault window. 616 else if (psu->hasFanFault() && !powerFaultOccurring) 617 { 618 // Include STATUS_TEMPERATURE and STATUS_FANS_1_2 619 additionalData["STATUS_TEMPERATURE"] = 620 std::format("{:#02x}", psu->getStatusTemperature()); 621 additionalData["STATUS_FANS_1_2"] = 622 std::format("{:#02x}", psu->getStatusFans12()); 623 624 additionalData["CALLOUT_INVENTORY_PATH"] = 625 psu->getInventoryPath(); 626 627 createError( 628 "xyz.openbmc_project.Power.PowerSupply.Error.FanFault", 629 additionalData); 630 631 psu->setFaultLogged(); 632 } 633 else if (psu->hasTempFault()) 634 { 635 // Include STATUS_TEMPERATURE for temperature faults. 636 additionalData["STATUS_TEMPERATURE"] = 637 std::format("{:#02x}", psu->getStatusTemperature()); 638 639 additionalData["CALLOUT_INVENTORY_PATH"] = 640 psu->getInventoryPath(); 641 642 createError( 643 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 644 additionalData); 645 646 psu->setFaultLogged(); 647 } 648 else if (psu->hasMFRFault()) 649 { 650 /* This can represent a variety of faults that result in 651 * calling out the power supply for replacement: Output 652 * OverCurrent, Output Under Voltage, and potentially other 653 * faults. 654 * 655 * Also plan on putting specific fault in AdditionalData, 656 * along with register names and register values 657 * (STATUS_WORD, STATUS_MFR, etc.).*/ 658 659 additionalData["CALLOUT_INVENTORY_PATH"] = 660 psu->getInventoryPath(); 661 662 createError( 663 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 664 additionalData); 665 666 psu->setFaultLogged(); 667 } 668 // Only process if not in power fault window. 669 else if (psu->hasPgoodFault() && !powerFaultOccurring) 670 { 671 /* POWER_GOOD# is not low, or OFF is on */ 672 additionalData["CALLOUT_INVENTORY_PATH"] = 673 psu->getInventoryPath(); 674 675 createError( 676 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 677 additionalData); 678 679 psu->setFaultLogged(); 680 } 681 } 682 } 683 } 684 } 685 686 void Chassis::analyzeBrownout() 687 { 688 // Count number of power supplies failing 689 size_t presentCount = 0; 690 size_t notPresentCount = 0; 691 size_t acFailedCount = 0; 692 size_t pgoodFailedCount = 0; 693 for (const auto& psu : psus) 694 { 695 if (psu->isPresent()) 696 { 697 ++presentCount; 698 if (psu->hasACFault()) 699 { 700 ++acFailedCount; 701 } 702 else if (psu->hasPgoodFault()) 703 { 704 ++pgoodFailedCount; 705 } 706 } 707 else 708 { 709 ++notPresentCount; 710 } 711 } 712 713 // Only issue brownout failure if chassis pgood has failed, it has not 714 // already been logged, at least one PSU has seen an AC fail, and all 715 // present PSUs have an AC or pgood failure. Note an AC fail is only set if 716 // at least one PSU is present. 717 if (powerFaultOccurring && !brownoutLogged && acFailedCount && 718 (presentCount == (acFailedCount + pgoodFailedCount))) 719 { 720 // Indicate that the system is in a brownout condition by creating an 721 // error log and setting the PowerSystemInputs status property to 722 // Fault. 723 powerSystemInputs.status( 724 sdbusplus::xyz::openbmc_project::State::Decorator::server:: 725 PowerSystemInputs::Status::Fault); 726 727 std::map<std::string, std::string> additionalData; 728 additionalData.emplace("NOT_PRESENT_COUNT", 729 std::to_string(notPresentCount)); 730 additionalData.emplace("VIN_FAULT_COUNT", 731 std::to_string(acFailedCount)); 732 additionalData.emplace("PGOOD_FAULT_COUNT", 733 std::to_string(pgoodFailedCount)); 734 lg2::info( 735 "Brownout detected, not present count: {NOT_PRESENT_COUNT}, AC fault count {AC_FAILED_COUNT}, pgood fault count: {PGOOD_FAILED_COUNT}", 736 "NOT_PRESENT_COUNT", notPresentCount, "AC_FAILED_COUNT", 737 acFailedCount, "PGOOD_FAILED_COUNT", pgoodFailedCount); 738 739 createError("xyz.openbmc_project.State.Shutdown.Power.Error.Blackout", 740 additionalData); 741 brownoutLogged = true; 742 } 743 else 744 { 745 // If a brownout was previously logged but at least one PSU is not 746 // currently in AC fault, determine if the brownout condition can be 747 // cleared 748 if (brownoutLogged && (acFailedCount < presentCount)) 749 { 750 // TODO Power State 751 } 752 } 753 } 754 755 void Chassis::createError(const std::string& faultName, 756 std::map<std::string, std::string>& additionalData) 757 { 758 using namespace sdbusplus::xyz::openbmc_project; 759 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging"; 760 constexpr auto loggingCreateInterface = 761 "xyz.openbmc_project.Logging.Create"; 762 763 try 764 { 765 additionalData["_PID"] = std::to_string(getpid()); 766 767 auto service = 768 util::getService(loggingObjectPath, loggingCreateInterface, bus); 769 770 if (service.empty()) 771 { 772 lg2::error("Unable to get logging manager service"); 773 return; 774 } 775 776 auto method = bus.new_method_call(service.c_str(), loggingObjectPath, 777 loggingCreateInterface, "Create"); 778 779 auto level = Logging::server::Entry::Level::Error; 780 method.append(faultName, level, additionalData); 781 782 auto reply = bus.call(method); 783 // TODO set power supply error 784 } 785 catch (const std::exception& e) 786 { 787 lg2::error( 788 "Failed creating event log for fault {FAULT_NAME} due to error {ERROR}", 789 "FAULT_NAME", faultName, "ERROR", e); 790 } 791 } 792 793 void Chassis::attemptToCreatePowerConfigGPIO() 794 { 795 try 796 { 797 powerConfigGPIO = createGPIO("power-config-full-load"); 798 } 799 catch (const std::exception& e) 800 { 801 powerConfigGPIO = nullptr; 802 lg2::info("GPIO not implemented in {CHASSIS}", "CHASSIS", 803 chassisShortName); 804 } 805 } 806 807 void Chassis::supportedConfigurationInterfaceAdded( 808 const util::DbusPropertyMap& properties) 809 { 810 populateSupportedConfiguration(properties); 811 updateMissingPSUs(); 812 } 813 814 void Chassis::psuInterfaceAdded(util::DbusPropertyMap& properties) 815 { 816 getPSUProperties(properties); 817 updateMissingPSUs(); 818 } 819 820 bool Chassis::hasRequiredPSUs( 821 std::map<std::string, std::string>& additionalData) 822 { 823 // ignore the following loop so code will compile 824 for (const auto& pair : additionalData) 825 { 826 std::cout << "Key = " << pair.first 827 << " additionalData value = " << pair.second << "\n"; 828 } 829 return true; 830 831 // TODO validate having the required PSUs 832 } 833 834 void Chassis::updateMissingPSUs() 835 { 836 if (supportedConfigs.empty() || psus.empty()) 837 { 838 return; 839 } 840 841 // Power supplies default to missing. If the power supply is present, 842 // the PowerSupply object will update the inventory Present property to 843 // true. If we have less than the required number of power supplies, and 844 // this power supply is missing, update the inventory Present property 845 // to false to indicate required power supply is missing. Avoid 846 // indicating power supply missing if not required. 847 848 auto presentCount = 849 std::count_if(psus.begin(), psus.end(), 850 [](const auto& psu) { return psu->isPresent(); }); 851 852 for (const auto& config : supportedConfigs) 853 { 854 for (const auto& psu : psus) 855 { 856 auto psuModel = psu->getModelName(); 857 auto psuShortName = psu->getShortName(); 858 auto psuInventoryPath = psu->getInventoryPath(); 859 auto relativeInvPath = 860 psuInventoryPath.substr(strlen(INVENTORY_OBJ_PATH)); 861 auto psuPresent = psu->isPresent(); 862 auto presProperty = false; 863 auto propReadFail = false; 864 865 try 866 { 867 presProperty = getPresence(bus, psuInventoryPath); 868 propReadFail = false; 869 } 870 catch (const sdbusplus::exception_t& e) 871 { 872 propReadFail = true; 873 // Relying on property change or interface added to retry. 874 // Log an informational trace to the journal. 875 lg2::info( 876 "D-Bus property {PSU_INVENTORY_PATH} access failure exception", 877 "PSU_INVENTORY_PATH", psuInventoryPath); 878 } 879 880 if (psuModel.empty()) 881 { 882 if (!propReadFail && (presProperty != psuPresent)) 883 { 884 // We already have this property, and it is not false 885 // set Present to false 886 setPresence(bus, relativeInvPath, psuPresent, psuShortName); 887 } 888 continue; 889 } 890 891 if (config.first != psuModel) 892 { 893 continue; 894 } 895 896 if ((presentCount < config.second.powerSupplyCount) && !psuPresent) 897 { 898 setPresence(bus, relativeInvPath, psuPresent, psuShortName); 899 } 900 } 901 } 902 } 903 904 void Chassis::powerStateChanged(sdbusplus::message_t& msg) 905 { 906 std::string msgSensor; 907 std::map<std::string, std::variant<int>> msgData; 908 msg.read(msgSensor, msgData); 909 910 // Check if it was the state property that changed. 911 auto valPropMap = msgData.find("state"); 912 if (valPropMap != msgData.end()) 913 { 914 int state = std::get<int>(valPropMap->second); 915 if (state) 916 { 917 // Power on requested 918 powerOn = true; 919 powerFaultOccurring = false; 920 validationTimer->restartOnce(validationTimeout); 921 // TODO clear faults 922 923 syncHistory(); 924 // TODO set power config 925 926 setInputVoltageRating(); 927 } 928 else 929 { 930 // Power off requested 931 powerOn = false; 932 powerFaultOccurring = false; 933 runValidateConfig = true; 934 } 935 } 936 937 // Check if it was the pgood property that changed. 938 valPropMap = msgData.find("pgood"); 939 if (valPropMap != msgData.end()) 940 { 941 int pgood = std::get<int>(valPropMap->second); 942 if (!pgood) 943 { 944 // Chassis power good has turned off 945 if (powerOn) 946 { 947 // pgood is off but state is on, in power fault window 948 powerFaultOccurring = true; 949 } 950 } 951 } 952 lg2::info( 953 "powerStateChanged: power on: {POWER_ON}, power fault occurring: {POWER_FAULT_OCCURRING}", 954 "POWER_ON", powerOn, "POWER_FAULT_OCCURRING", powerFaultOccurring); 955 } 956 957 } // namespace phosphor::power::chassis 958