1 #include "powermode.hpp" 2 3 #include <fcntl.h> 4 #include <fmt/core.h> 5 #include <sys/ioctl.h> 6 7 #ifdef POWERVM_CHECK 8 #include <com/ibm/Host/Target/server.hpp> 9 #endif 10 #include <org/open_power/OCC/Device/error.hpp> 11 #include <phosphor-logging/elog-errors.hpp> 12 #include <phosphor-logging/log.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp> 15 16 #include <cassert> 17 #include <fstream> 18 #include <regex> 19 20 namespace open_power 21 { 22 namespace occ 23 { 24 namespace powermode 25 { 26 27 using namespace phosphor::logging; 28 using namespace std::literals::string_literals; 29 using namespace sdbusplus::org::open_power::OCC::Device::Error; 30 31 using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode; 32 33 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 34 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 35 36 // Set the Master OCC 37 void PowerMode::setMasterOcc(const std::string& masterOccPath) 38 { 39 if (masterOccSet) 40 { 41 if (masterOccPath != path) 42 { 43 log<level::ERR>( 44 fmt::format( 45 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})", 46 occInstance, masterOccPath) 47 .c_str()); 48 if (occCmd) 49 { 50 occCmd.reset(); 51 } 52 } 53 } 54 path = masterOccPath; 55 occInstance = path.back() - '0'; 56 log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})", 57 occInstance, path.c_str()) 58 .c_str()); 59 if (!occCmd) 60 { 61 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance, 62 path.c_str()); 63 } 64 masterOccSet = true; 65 }; 66 67 // Called when DBus power mode gets changed 68 void PowerMode::modeChanged(sdbusplus::message_t& msg) 69 { 70 std::map<std::string, std::variant<std::string>> properties{}; 71 std::string interface; 72 std::string propVal; 73 msg.read(interface, properties); 74 const auto modeEntry = properties.find(POWER_MODE_PROP); 75 if (modeEntry != properties.end()) 76 { 77 auto modeEntryValue = modeEntry->second; 78 propVal = std::get<std::string>(modeEntryValue); 79 SysPwrMode newMode = convertStringToMode(propVal); 80 81 // If mode set was requested via direct dbus property change then we 82 // need to see if mode set is locked and ignore the request. We should 83 // not get to this path since the property change method should also be 84 // checking the mode lock status. 85 if (persistedData.getModeLock()) 86 { 87 // Fix up the mode property since we are going to ignore this 88 // set mode request. 89 log<level::ERR>( 90 "PowerMode::modeChanged: mode property changed while locked"); 91 SysPwrMode currentMode; 92 uint16_t oemModeData; 93 getMode(currentMode, oemModeData); 94 updateDbusMode(currentMode); 95 return; 96 } 97 98 if (newMode != SysPwrMode::NO_CHANGE) 99 { 100 // Update persisted data with new mode 101 persistedData.updateMode(newMode, 0); 102 103 log<level::INFO>( 104 fmt::format("DBus Power Mode Changed: {}", propVal).c_str()); 105 106 // Send mode change to OCC 107 sendModeChange(); 108 } 109 } 110 } 111 112 // Set the state of power mode lock. Writing persistent data via dbus method. 113 bool PowerMode::powerModeLock() 114 { 115 log<level::INFO>("PowerMode::powerModeLock: locking mode change"); 116 persistedData.updateModeLock(true); // write persistent data 117 return true; 118 } 119 120 // Get the state of power mode. Reading persistent data via dbus method. 121 bool PowerMode::powerModeLockStatus() 122 { 123 bool status = persistedData.getModeLock(); // read persistent data 124 log<level::INFO>(fmt::format("PowerMode::powerModeLockStatus: {}", 125 status ? "locked" : "unlocked") 126 .c_str()); 127 return status; 128 } 129 130 // Called from OCC PassThrough interface (via CE login / BMC command line) 131 bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData) 132 { 133 if (persistedData.getModeLock()) 134 { 135 log<level::INFO>("PowerMode::setMode: mode change blocked"); 136 return false; 137 } 138 139 if (updateDbusMode(newMode) == false) 140 { 141 // Unsupported mode 142 return false; 143 } 144 145 // Save mode 146 persistedData.updateMode(newMode, oemModeData); 147 148 // Send mode change to OCC 149 if (sendModeChange() != CmdStatus::SUCCESS) 150 { 151 // Mode change failed 152 return false; 153 } 154 155 return true; 156 } 157 158 // Convert PowerMode string to OCC SysPwrMode 159 // Returns NO_CHANGE if OEM or unsupported mode 160 SysPwrMode convertStringToMode(const std::string& i_modeString) 161 { 162 SysPwrMode pmode = SysPwrMode::NO_CHANGE; 163 164 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString); 165 if (mode == Mode::PowerMode::MaximumPerformance) 166 { 167 pmode = SysPwrMode::MAX_PERF; 168 } 169 else if (mode == Mode::PowerMode::PowerSaving) 170 { 171 pmode = SysPwrMode::POWER_SAVING; 172 } 173 else if (mode == Mode::PowerMode::Static) 174 { 175 pmode = SysPwrMode::STATIC; 176 } 177 else 178 { 179 if (mode != Mode::PowerMode::OEM) 180 { 181 log<level::ERR>( 182 fmt::format( 183 "convertStringToMode: Invalid Power Mode specified: {}", 184 i_modeString) 185 .c_str()); 186 } 187 } 188 189 return pmode; 190 } 191 192 // Check if Hypervisor target is PowerVM 193 bool isPowerVM() 194 { 195 bool powerVmTarget = true; 196 #ifdef POWERVM_CHECK 197 namespace Hyper = sdbusplus::com::ibm::Host::server; 198 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor"; 199 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target"; 200 constexpr auto HYPE_PROP = "Target"; 201 202 // This will throw exception on failure 203 auto& bus = utils::getBus(); 204 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE); 205 auto method = bus.new_method_call(service.c_str(), HYPE_PATH, 206 "org.freedesktop.DBus.Properties", "Get"); 207 method.append(HYPE_INTERFACE, HYPE_PROP); 208 auto reply = bus.call(method); 209 210 std::variant<std::string> hyperEntryValue; 211 reply.read(hyperEntryValue); 212 auto propVal = std::get<std::string>(hyperEntryValue); 213 if (Hyper::Target::convertHypervisorFromString(propVal) == 214 Hyper::Target::Hypervisor::PowerVM) 215 { 216 powerVmTarget = true; 217 } 218 219 log<level::DEBUG>( 220 fmt::format("isPowerVM returning {}", powerVmTarget).c_str()); 221 #endif 222 223 return powerVmTarget; 224 } 225 226 // Initialize persistent data and return true if successful 227 bool PowerMode::initPersistentData() 228 { 229 if (!persistedData.modeAvailable()) 230 { 231 // Read the default mode 232 SysPwrMode currentMode; 233 if (!getDefaultMode(currentMode)) 234 { 235 // Unable to read defaults 236 return false; 237 } 238 log<level::INFO>( 239 fmt::format("PowerMode::initPersistentData: Using default mode: {}", 240 currentMode) 241 .c_str()); 242 243 // Save default mode as current mode 244 persistedData.updateMode(currentMode, 0); 245 246 // Write default mode to DBus 247 updateDbusMode(currentMode); 248 } 249 250 if (!persistedData.ipsAvailable()) 251 { 252 // Read the default IPS parameters, write persistent file and update 253 // DBus 254 return useDefaultIPSParms(); 255 } 256 return true; 257 } 258 259 // Get the requested power mode and return true if successful 260 bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData) 261 { 262 currentMode = SysPwrMode::NO_CHANGE; 263 oemModeData = 0; 264 265 if (!persistedData.getMode(currentMode, oemModeData)) 266 { 267 // Persistent data not initialized, read defaults and update DBus 268 if (!initPersistentData()) 269 { 270 // Unable to read defaults from entity manager yet 271 return false; 272 } 273 return persistedData.getMode(currentMode, oemModeData); 274 } 275 276 return true; 277 } 278 279 // Set the power mode on DBus 280 bool PowerMode::updateDbusMode(const SysPwrMode newMode) 281 { 282 if (!VALID_POWER_MODE_SETTING(newMode) && 283 !VALID_OEM_POWER_MODE_SETTING(newMode)) 284 { 285 log<level::ERR>( 286 fmt::format( 287 "PowerMode::updateDbusMode - Requested power mode not supported: {}", 288 newMode) 289 .c_str()); 290 return false; 291 } 292 293 // Convert mode for DBus 294 ModeInterface::PowerMode dBusMode; 295 switch (newMode) 296 { 297 case SysPwrMode::STATIC: 298 dBusMode = Mode::PowerMode::Static; 299 break; 300 case SysPwrMode::POWER_SAVING: 301 dBusMode = Mode::PowerMode::PowerSaving; 302 break; 303 case SysPwrMode::MAX_PERF: 304 dBusMode = Mode::PowerMode::MaximumPerformance; 305 break; 306 default: 307 dBusMode = Mode::PowerMode::OEM; 308 } 309 310 // true = skip update signal 311 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true); 312 313 return true; 314 } 315 316 // Send mode change request to the master OCC 317 CmdStatus PowerMode::sendModeChange() 318 { 319 CmdStatus status; 320 321 if (!masterActive || !masterOccSet) 322 { 323 // Nothing to do 324 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active"); 325 return CmdStatus::SUCCESS; 326 } 327 328 if (!isPowerVM()) 329 { 330 // Mode change is only supported on PowerVM systems 331 log<level::DEBUG>( 332 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems"); 333 return CmdStatus::SUCCESS; 334 } 335 336 SysPwrMode newMode; 337 uint16_t oemModeData = 0; 338 getMode(newMode, oemModeData); 339 340 if (VALID_POWER_MODE_SETTING(newMode) || 341 VALID_OEM_POWER_MODE_SETTING(newMode)) 342 { 343 std::vector<std::uint8_t> cmd, rsp; 344 cmd.reserve(9); 345 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE)); 346 cmd.push_back(0x00); // Data Length (2 bytes) 347 cmd.push_back(0x06); 348 cmd.push_back(0x30); // Data (Version) 349 cmd.push_back(uint8_t(OccState::NO_CHANGE)); 350 cmd.push_back(uint8_t(newMode)); 351 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point) 352 cmd.push_back(oemModeData & 0xFF); // 353 cmd.push_back(0x00); // reserved 354 log<level::INFO>( 355 fmt::format( 356 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)", 357 newMode, oemModeData, occInstance, cmd.size()) 358 .c_str()); 359 status = occCmd->send(cmd, rsp); 360 if (status == CmdStatus::SUCCESS) 361 { 362 if (rsp.size() == 5) 363 { 364 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 365 { 366 log<level::ERR>( 367 fmt::format( 368 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}", 369 rsp[2]) 370 .c_str()); 371 dump_hex(rsp); 372 status = CmdStatus::FAILURE; 373 } 374 } 375 else 376 { 377 log<level::ERR>( 378 "PowerMode::sendModeChange: INVALID SET MODE response"); 379 dump_hex(rsp); 380 status = CmdStatus::FAILURE; 381 } 382 } 383 else 384 { 385 log<level::ERR>( 386 fmt::format( 387 "PowerMode::sendModeChange: SET_MODE FAILED with status={}", 388 status) 389 .c_str()); 390 } 391 } 392 else 393 { 394 log<level::ERR>( 395 fmt::format( 396 "PowerMode::sendModeChange: Unable to set power mode to {}", 397 newMode) 398 .c_str()); 399 status = CmdStatus::FAILURE; 400 } 401 402 return status; 403 } 404 405 void PowerMode::ipsChanged(sdbusplus::message_t& msg) 406 { 407 bool parmsChanged = false; 408 std::string interface; 409 std::map<std::string, std::variant<bool, uint8_t, uint64_t>> 410 ipsProperties{}; 411 msg.read(interface, ipsProperties); 412 413 // Read persisted values 414 bool ipsEnabled; 415 uint8_t enterUtil, exitUtil; 416 uint16_t enterTime, exitTime; 417 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 418 419 // Check for any changed data 420 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP); 421 if (ipsEntry != ipsProperties.end()) 422 { 423 ipsEnabled = std::get<bool>(ipsEntry->second); 424 log<level::INFO>( 425 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled) 426 .c_str()); 427 parmsChanged = true; 428 } 429 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL); 430 if (ipsEntry != ipsProperties.end()) 431 { 432 enterUtil = std::get<uint8_t>(ipsEntry->second); 433 log<level::INFO>( 434 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil) 435 .c_str()); 436 parmsChanged = true; 437 } 438 ipsEntry = ipsProperties.find(IPS_ENTER_TIME); 439 if (ipsEntry != ipsProperties.end()) 440 { 441 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 442 enterTime = 443 std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 444 log<level::INFO>( 445 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime) 446 .c_str()); 447 parmsChanged = true; 448 } 449 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL); 450 if (ipsEntry != ipsProperties.end()) 451 { 452 exitUtil = std::get<uint8_t>(ipsEntry->second); 453 log<level::INFO>( 454 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil) 455 .c_str()); 456 parmsChanged = true; 457 } 458 ipsEntry = ipsProperties.find(IPS_EXIT_TIME); 459 if (ipsEntry != ipsProperties.end()) 460 { 461 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 462 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 463 log<level::INFO>( 464 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime) 465 .c_str()); 466 parmsChanged = true; 467 } 468 469 if (parmsChanged) 470 { 471 if (exitUtil == 0) 472 { 473 // Setting the exitUtil to 0 will force restoring the default IPS 474 // parmeters (0 is not valid exit utilization) 475 log<level::INFO>( 476 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters"); 477 // Read the default IPS parameters, write persistent file and update 478 // DBus 479 useDefaultIPSParms(); 480 } 481 else 482 { 483 // Update persistant data with new DBus values 484 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 485 exitTime); 486 } 487 488 // Trigger IPS data to get sent to the OCC 489 sendIpsData(); 490 } 491 492 return; 493 } 494 495 /** @brief Get the Idle Power Saver properties from persisted data 496 * @return true if IPS parameters were read 497 */ 498 bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil, 499 uint16_t& enterTime, uint8_t& exitUtil, 500 uint16_t& exitTime) 501 { 502 // Defaults: 503 ipsEnabled = true; // Enabled 504 enterUtil = 8; // Enter Utilization (8%) 505 enterTime = 240; // Enter Delay Time (240s) 506 exitUtil = 12; // Exit Utilization (12%) 507 exitTime = 10; // Exit Delay Time (10s) 508 509 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 510 exitTime)) 511 { 512 // Persistent data not initialized, read defaults and update DBus 513 if (!initPersistentData()) 514 { 515 // Unable to read defaults from entity manager yet 516 return false; 517 } 518 519 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 520 exitTime); 521 } 522 523 if (enterUtil > exitUtil) 524 { 525 log<level::ERR>( 526 fmt::format( 527 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", 528 enterUtil, exitUtil) 529 .c_str()); 530 enterUtil = exitUtil; 531 } 532 533 return true; 534 } 535 536 // Set the Idle Power Saver data on DBus 537 bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil, 538 const uint16_t enterTime, const uint8_t exitUtil, 539 const uint16_t exitTime) 540 { 541 // true = skip update signal 542 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true); 543 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true); 544 // Convert time from seconds to ms 545 uint64_t msTime = enterTime * 1000; 546 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true); 547 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true); 548 msTime = exitTime * 1000; 549 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true); 550 551 return true; 552 } 553 554 // Send Idle Power Saver config data to the master OCC 555 CmdStatus PowerMode::sendIpsData() 556 { 557 if (!masterActive || !masterOccSet) 558 { 559 // Nothing to do 560 return CmdStatus::SUCCESS; 561 } 562 563 if (!isPowerVM()) 564 { 565 // Idle Power Saver data is only supported on PowerVM systems 566 log<level::DEBUG>( 567 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems"); 568 return CmdStatus::SUCCESS; 569 } 570 571 bool ipsEnabled; 572 uint8_t enterUtil, exitUtil; 573 uint16_t enterTime, exitTime; 574 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 575 576 log<level::INFO>( 577 fmt::format( 578 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s", 579 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) 580 .c_str()); 581 582 std::vector<std::uint8_t> cmd, rsp; 583 cmd.reserve(12); 584 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA)); 585 cmd.push_back(0x00); // Data Length (2 bytes) 586 cmd.push_back(0x09); // 587 cmd.push_back(0x11); // Config Format: IPS Settings 588 cmd.push_back(0x00); // Version 589 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable 590 cmd.push_back(enterTime >> 8); // Enter Delay Time 591 cmd.push_back(enterTime & 0xFF); // 592 cmd.push_back(enterUtil); // Enter Utilization 593 cmd.push_back(exitTime >> 8); // Exit Delay Time 594 cmd.push_back(exitTime & 0xFF); // 595 cmd.push_back(exitUtil); // Exit Utilization 596 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] " 597 "command to OCC{} ({} bytes)", 598 occInstance, cmd.size()) 599 .c_str()); 600 CmdStatus status = occCmd->send(cmd, rsp); 601 if (status == CmdStatus::SUCCESS) 602 { 603 if (rsp.size() == 5) 604 { 605 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 606 { 607 log<level::ERR>( 608 fmt::format( 609 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}", 610 rsp[2]) 611 .c_str()); 612 dump_hex(rsp); 613 status = CmdStatus::FAILURE; 614 } 615 } 616 else 617 { 618 log<level::ERR>( 619 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response"); 620 dump_hex(rsp); 621 status = CmdStatus::FAILURE; 622 } 623 } 624 else 625 { 626 log<level::ERR>( 627 fmt::format( 628 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}", 629 status) 630 .c_str()); 631 } 632 633 return status; 634 } 635 636 // Print the current values 637 void OccPersistData::print() 638 { 639 if (modeData.modeInitialized) 640 { 641 log<level::INFO>( 642 fmt::format( 643 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})", 644 modeData.mode, modeData.oemModeData, modeData.oemModeData, 645 modeData.modeLocked) 646 .c_str()); 647 } 648 if (modeData.ipsInitialized) 649 { 650 log<level::INFO>( 651 fmt::format( 652 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s", 653 modeData.ipsEnabled, modeData.ipsEnterUtil, 654 modeData.ipsEnterTime, modeData.ipsExitUtil, 655 modeData.ipsExitTime) 656 .c_str()); 657 } 658 } 659 660 // Saves the OEM mode data in the filesystem using cereal. 661 void OccPersistData::save() 662 { 663 std::filesystem::path opath = 664 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; 665 666 if (!std::filesystem::exists(opath.parent_path())) 667 { 668 std::filesystem::create_directory(opath.parent_path()); 669 } 670 671 log<level::DEBUG>( 672 fmt::format( 673 "OccPersistData::save: Writing Power Mode persisted data to {}", 674 opath.c_str()) 675 .c_str()); 676 // print(); 677 678 std::ofstream stream{opath.c_str()}; 679 cereal::JSONOutputArchive oarchive{stream}; 680 681 oarchive(modeData); 682 } 683 684 // Loads the OEM mode data in the filesystem using cereal. 685 void OccPersistData::load() 686 { 687 std::filesystem::path ipath = 688 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; 689 690 if (!std::filesystem::exists(ipath)) 691 { 692 modeData.modeInitialized = false; 693 modeData.ipsInitialized = false; 694 return; 695 } 696 697 log<level::DEBUG>( 698 fmt::format( 699 "OccPersistData::load: Reading Power Mode persisted data from {}", 700 ipath.c_str()) 701 .c_str()); 702 try 703 { 704 std::ifstream stream{ipath.c_str()}; 705 cereal::JSONInputArchive iarchive(stream); 706 iarchive(modeData); 707 } 708 catch (const std::exception& e) 709 { 710 auto error = errno; 711 log<level::ERR>( 712 fmt::format("OccPersistData::load: failed to read {}, errno={}", 713 ipath.c_str(), error) 714 .c_str()); 715 modeData.modeInitialized = false; 716 modeData.ipsInitialized = false; 717 } 718 719 // print(); 720 } 721 722 // Called when PowerModeProperties defaults are available on DBus 723 void PowerMode::defaultsReady(sdbusplus::message_t& msg) 724 { 725 std::map<std::string, std::variant<std::string>> properties{}; 726 std::string interface; 727 msg.read(interface, properties); 728 729 // If persistent data exists, then don't need to read defaults 730 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable())) 731 { 732 log<level::INFO>( 733 fmt::format( 734 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})", 735 persistedData.modeAvailable() ? 'y' : 'n', 736 persistedData.modeAvailable() ? 'y' : 'n') 737 .c_str()); 738 739 // Read default power mode defaults and update DBus 740 initPersistentData(); 741 } 742 } 743 744 // Get the default power mode from DBus and return true if success 745 bool PowerMode::getDefaultMode(SysPwrMode& defaultMode) 746 { 747 try 748 { 749 auto& bus = utils::getBus(); 750 std::string path = "/"; 751 std::string service = 752 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); 753 auto method = bus.new_method_call(service.c_str(), path.c_str(), 754 "org.freedesktop.DBus.Properties", 755 "Get"); 756 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode"); 757 auto reply = bus.call(method); 758 759 std::variant<std::string> stateEntryValue; 760 reply.read(stateEntryValue); 761 auto propVal = std::get<std::string>(stateEntryValue); 762 763 const std::string fullModeString = PMODE_INTERFACE + ".PowerMode."s + 764 propVal; 765 defaultMode = powermode::convertStringToMode(fullModeString); 766 if (!VALID_POWER_MODE_SETTING(defaultMode)) 767 { 768 log<level::ERR>( 769 fmt::format( 770 "PowerMode::getDefaultMode: Invalid default power mode found: {}", 771 defaultMode) 772 .c_str()); 773 // If default was read but not valid, use Max Performance 774 defaultMode = SysPwrMode::MAX_PERF; 775 return true; 776 } 777 } 778 catch (const sdbusplus::exception_t& e) 779 { 780 log<level::ERR>( 781 fmt::format("Unable to read Default Power Mode: {}", e.what()) 782 .c_str()); 783 return false; 784 } 785 786 return true; 787 } 788 789 /* Get the default Idle Power Saver properties and return true if successful */ 790 bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil, 791 uint16_t& enterTime, uint8_t& exitUtil, 792 uint16_t& exitTime) 793 { 794 // Defaults: 795 ipsEnabled = true; // Enabled 796 enterUtil = 8; // Enter Utilization (8%) 797 enterTime = 240; // Enter Delay Time (240s) 798 exitUtil = 12; // Exit Utilization (12%) 799 exitTime = 10; // Exit Delay Time (10s) 800 801 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>> 802 ipsProperties{}; 803 804 // Get all IPS properties from DBus 805 try 806 { 807 auto& bus = utils::getBus(); 808 std::string path = "/"; 809 std::string service = 810 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); 811 auto method = bus.new_method_call(service.c_str(), path.c_str(), 812 "org.freedesktop.DBus.Properties", 813 "GetAll"); 814 method.append(PMODE_DEFAULT_INTERFACE); 815 auto reply = bus.call(method); 816 reply.read(ipsProperties); 817 } 818 catch (const sdbusplus::exception_t& e) 819 { 820 log<level::ERR>( 821 fmt::format( 822 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}", 823 e.what()) 824 .c_str()); 825 return false; 826 } 827 828 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled"); 829 if (ipsEntry != ipsProperties.end()) 830 { 831 ipsEnabled = std::get<bool>(ipsEntry->second); 832 } 833 else 834 { 835 log<level::ERR>( 836 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled"); 837 } 838 839 ipsEntry = ipsProperties.find("EnterUtilizationPercent"); 840 if (ipsEntry != ipsProperties.end()) 841 { 842 enterUtil = std::get<uint64_t>(ipsEntry->second); 843 } 844 else 845 { 846 log<level::ERR>( 847 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent"); 848 } 849 850 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime"); 851 if (ipsEntry != ipsProperties.end()) 852 { 853 enterTime = std::get<uint64_t>(ipsEntry->second); 854 } 855 else 856 { 857 log<level::ERR>( 858 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime"); 859 } 860 861 ipsEntry = ipsProperties.find("ExitUtilizationPercent"); 862 if (ipsEntry != ipsProperties.end()) 863 { 864 exitUtil = std::get<uint64_t>(ipsEntry->second); 865 } 866 else 867 { 868 log<level::ERR>( 869 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent"); 870 } 871 872 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime"); 873 if (ipsEntry != ipsProperties.end()) 874 { 875 exitTime = std::get<uint64_t>(ipsEntry->second); 876 } 877 else 878 { 879 log<level::ERR>( 880 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime"); 881 } 882 883 if (enterUtil > exitUtil) 884 { 885 log<level::ERR>( 886 fmt::format( 887 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", 888 enterUtil, exitUtil) 889 .c_str()); 890 enterUtil = exitUtil; 891 } 892 893 return true; 894 } 895 896 /* Read default IPS parameters, save them to the persistent file and update 897 DBus. Return true if successful */ 898 bool PowerMode::useDefaultIPSParms() 899 { 900 // Read the default IPS parameters 901 bool ipsEnabled; 902 uint8_t enterUtil, exitUtil; 903 uint16_t enterTime, exitTime; 904 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, 905 exitTime)) 906 { 907 // Unable to read defaults 908 return false; 909 } 910 log<level::INFO>( 911 fmt::format( 912 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s", 913 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) 914 .c_str()); 915 916 // Save IPS parms to the persistent file 917 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 918 exitTime); 919 920 // Write IPS parms to DBus 921 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 922 } 923 924 #ifdef POWER10 925 926 // Starts to watch for IPS active state changes. 927 bool PowerMode::openIpsFile() 928 { 929 bool rc = true; 930 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK); 931 const int open_errno = errno; 932 if (fd < 0) 933 { 934 log<level::ERR>(fmt::format("openIpsFile Error({})={} : File={}", 935 open_errno, strerror(open_errno), 936 ipsStatusFile.c_str()) 937 .c_str()); 938 939 close(fd); 940 941 using namespace sdbusplus::org::open_power::OCC::Device::Error; 942 report<OpenFailure>( 943 phosphor::logging::org::open_power::OCC::Device::OpenFailure:: 944 CALLOUT_ERRNO(open_errno), 945 phosphor::logging::org::open_power::OCC::Device::OpenFailure:: 946 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str())); 947 948 // We are no longer watching the error 949 active(false); 950 951 watching = false; 952 rc = false; 953 // NOTE: this will leave the system not reporting IPS active state to 954 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 955 } 956 return rc; 957 } 958 959 // Starts to watch for IPS active state changes. 960 void PowerMode::addIpsWatch(bool poll) 961 { 962 // open file and register callback on file if we are not currently watching, 963 // and if poll=true, and if we are the master. 964 if ((!watching) && poll) 965 { 966 // Open the file 967 if (openIpsFile()) 968 { 969 // register the callback handler which sets 'watching' 970 registerIpsStatusCallBack(); 971 } 972 } 973 } 974 975 // Stops watching for IPS active state changes. 976 void PowerMode::removeIpsWatch() 977 { 978 // NOTE: we want to remove event, close file, and IPS active false no 979 // matter what the 'watching' flags is set to. 980 981 // We are no longer watching the error 982 active(false); 983 984 watching = false; 985 986 // Close file 987 close(fd); 988 989 // clears sourcePtr in the event source. 990 eventSource.reset(); 991 } 992 993 // Attaches the FD to event loop and registers the callback handler 994 void PowerMode::registerIpsStatusCallBack() 995 { 996 decltype(eventSource.get()) sourcePtr = nullptr; 997 998 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR, 999 ipsStatusCallBack, this); 1000 if (r < 0) 1001 { 1002 log<level::ERR>(fmt::format("sd_event_add_io: Error({})={} : File={}", 1003 r, strerror(-r), ipsStatusFile.c_str()) 1004 .c_str()); 1005 1006 using InternalFailure = 1007 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 1008 report<InternalFailure>(); 1009 1010 removeIpsWatch(); 1011 // NOTE: this will leave the system not reporting IPS active state to 1012 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 1013 } 1014 else 1015 { 1016 // puts sourcePtr in the event source. 1017 eventSource.reset(sourcePtr); 1018 // Set we are watching the error 1019 watching = true; 1020 } 1021 } 1022 1023 // Static function to redirect to non static analyze event function to be 1024 // able to read file and push onto dBus. 1025 int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/, 1026 uint32_t /*revents*/, void* userData) 1027 { 1028 auto pmode = static_cast<PowerMode*>(userData); 1029 pmode->analyzeIpsEvent(); 1030 return 0; 1031 } 1032 1033 // Function to Read SysFs file change on IPS state and push on dBus. 1034 void PowerMode::analyzeIpsEvent() 1035 { 1036 // Need to seek to START, else the poll returns immediately telling 1037 // there is data to be read. if not done this floods the system. 1038 auto r = lseek(fd, 0, SEEK_SET); 1039 const int open_errno = errno; 1040 if (r < 0) 1041 { 1042 // NOTE: upon file access error we can not just re-open file, we have to 1043 // remove and add to watch. 1044 removeIpsWatch(); 1045 addIpsWatch(true); 1046 } 1047 1048 // if we are 'watching' that is the file seek, or the re-open passed.. we 1049 // can read the data 1050 if (watching) 1051 { 1052 // This file gets created when polling OCCs. A value or length of 0 is 1053 // deemed success. That means we would disable IPS active on dbus. 1054 char data; 1055 bool ipsState = false; 1056 const auto len = read(fd, &data, sizeof(data)); 1057 const int readErrno = errno; 1058 if (len <= 0) 1059 { 1060 removeIpsWatch(); 1061 1062 log<level::ERR>( 1063 fmt::format("IPS state Read Error({})={} : File={} : len={}", 1064 readErrno, strerror(readErrno), 1065 ipsStatusFile.c_str(), len) 1066 .c_str()); 1067 1068 report<ReadFailure>( 1069 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 1070 CALLOUT_ERRNO(readErrno), 1071 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 1072 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str())); 1073 1074 // NOTE: this will leave the system not reporting IPS active state 1075 // to Fan Controls, Until an APP reload, or IPL and we will attempt 1076 // again. 1077 } 1078 else 1079 { 1080 // Data returned in ASCII. 1081 // convert to integer. atoi() 1082 // from OCC_P10_FW_Interfaces spec 1083 // Bit 6: IPS active 1 indicates enabled. 1084 // Bit 7: IPS enabled. 1 indicates enabled. 1085 // mask off bit 6 --> & 0x02 1086 // Shift left one bit and store as bool. >> 1 1087 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1); 1088 } 1089 1090 // This will only set IPS active dbus if different than current. 1091 active(ipsState); 1092 } 1093 else 1094 { 1095 removeIpsWatch(); 1096 1097 // If the Retry did not get to "watching = true" we already have an 1098 // error log, just post trace. 1099 log<level::ERR>(fmt::format("Retry on File seek Error({})={} : File={}", 1100 open_errno, strerror(open_errno), 1101 ipsStatusFile.c_str()) 1102 .c_str()); 1103 1104 // NOTE: this will leave the system not reporting IPS active state to 1105 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 1106 } 1107 1108 return; 1109 } 1110 1111 // overrides read/write to powerMode dbus property. 1112 Mode::PowerMode PowerMode::powerMode(Mode::PowerMode value) 1113 { 1114 if (persistedData.getModeLock()) 1115 { 1116 log<level::INFO>("PowerMode::powerMode: mode property change blocked"); 1117 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 1118 "mode change not allowed due to lock")); 1119 return value; 1120 } 1121 else 1122 { 1123 return Mode::powerMode(value); 1124 } 1125 } 1126 #endif 1127 1128 /* Set dbus property to SAFE mode(true) or clear(false) only if different */ 1129 void PowerMode::updateDbusSafeMode(const bool safeModeReq) 1130 { 1131 log<level::DEBUG>( 1132 fmt::format("PowerMode:updateDbusSafeMode: Update dbus state ({})", 1133 safeModeReq) 1134 .c_str()); 1135 1136 // Note; this function checks and only updates if different. 1137 Mode::safeMode(safeModeReq); 1138 } 1139 1140 } // namespace powermode 1141 1142 } // namespace occ 1143 1144 } // namespace open_power 1145