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