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