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