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 if (std::filesystem::exists(ipsStatusFile))
1079 {
1080 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
1081 const int open_errno = errno;
1082 if (fd < 0)
1083 {
1084 lg2::error("openIpsFile Error({ERR})={STR} : File={FILE}", "ERR",
1085 open_errno, "STR", strerror(open_errno), "FILE",
1086 ipsStatusFile);
1087
1088 close(fd);
1089
1090 using namespace sdbusplus::org::open_power::OCC::Device::Error;
1091 report<OpenFailure>(
1092 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1093 CALLOUT_ERRNO(open_errno),
1094 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1095 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1096
1097 // We are no longer watching the error
1098 if (ipsObject)
1099 {
1100 ipsObject->active(false);
1101 }
1102
1103 watching = false;
1104 rc = false;
1105 }
1106 }
1107 else
1108 {
1109 // Not watching for IPS state changes yet
1110 watching = false;
1111 rc = false;
1112 }
1113 return rc;
1114 }
1115
1116 // Starts to watch for IPS active state changes.
addIpsWatch(bool poll)1117 void PowerMode::addIpsWatch(bool poll)
1118 {
1119 // open file and register callback on file if we are not currently watching,
1120 // and if poll=true, and if we are the master.
1121 if ((!watching) && poll)
1122 {
1123 // Open the file
1124 if (openIpsFile())
1125 {
1126 // register the callback handler which sets 'watching'
1127 registerIpsStatusCallBack();
1128 }
1129 }
1130 }
1131
1132 // Stops watching for IPS active state changes.
removeIpsWatch()1133 void PowerMode::removeIpsWatch()
1134 {
1135 // NOTE: we want to remove event, close file, and IPS active false no
1136 // matter what the 'watching' flags is set to.
1137
1138 // We are no longer watching the error
1139 if (ipsObject)
1140 {
1141 ipsObject->active(false);
1142 }
1143
1144 watching = false;
1145
1146 // Close file
1147 close(fd);
1148
1149 // clears sourcePtr in the event source.
1150 eventSource.reset();
1151 }
1152
1153 // Attaches the FD to event loop and registers the callback handler
registerIpsStatusCallBack()1154 void PowerMode::registerIpsStatusCallBack()
1155 {
1156 decltype(eventSource.get()) sourcePtr = nullptr;
1157
1158 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1159 ipsStatusCallBack, this);
1160 if (r < 0)
1161 {
1162 lg2::error("sd_event_add_io: Error({ERR})={STR} : File={FILE}", "ERR",
1163 r, "STR", strerror(-r), "FILE", ipsStatusFile);
1164
1165 using InternalFailure =
1166 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1167 report<InternalFailure>();
1168
1169 removeIpsWatch();
1170 // NOTE: this will leave the system not reporting IPS active state to
1171 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1172 }
1173 else
1174 {
1175 // puts sourcePtr in the event source.
1176 eventSource.reset(sourcePtr);
1177 // Set we are watching the error
1178 watching = true;
1179 }
1180 }
1181
1182 // Static function to redirect to non static analyze event function to be
1183 // able to read file and push onto dBus.
ipsStatusCallBack(sd_event_source *,int,uint32_t,void * userData)1184 int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1185 uint32_t /*revents*/, void* userData)
1186 {
1187 auto pmode = static_cast<PowerMode*>(userData);
1188 pmode->analyzeIpsEvent();
1189 return 0;
1190 }
1191
1192 // Function to Read SysFs file change on IPS state and push on dBus.
analyzeIpsEvent()1193 void PowerMode::analyzeIpsEvent()
1194 {
1195 // Need to seek to START, else the poll returns immediately telling
1196 // there is data to be read. if not done this floods the system.
1197 auto r = lseek(fd, 0, SEEK_SET);
1198 const int open_errno = errno;
1199 if (r < 0)
1200 {
1201 // NOTE: upon file access error we can not just re-open file, we have to
1202 // remove and add to watch.
1203 removeIpsWatch();
1204 addIpsWatch(true);
1205 }
1206
1207 // if we are 'watching' that is the file seek, or the re-open passed.. we
1208 // can read the data
1209 if (watching)
1210 {
1211 // This file gets created when polling OCCs. A value or length of 0 is
1212 // deemed success. That means we would disable IPS active on dbus.
1213 char data;
1214 bool ipsState = false;
1215 const auto len = read(fd, &data, sizeof(data));
1216 const int readErrno = errno;
1217 if (len <= 0)
1218 {
1219 removeIpsWatch();
1220
1221 lg2::error(
1222 "IPS state Read Error({ERR})={STR} : File={FILE} : len={LEN}",
1223 "ERR", readErrno, "STR", strerror(readErrno), "FILE",
1224 ipsStatusFile, "LEN", len);
1225
1226 report<ReadFailure>(
1227 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1228 CALLOUT_ERRNO(readErrno),
1229 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1230 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1231
1232 // NOTE: this will leave the system not reporting IPS active state
1233 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1234 // again.
1235 }
1236 else
1237 {
1238 // Data returned in ASCII.
1239 // convert to integer. atoi()
1240 // from OCC_P10_FW_Interfaces spec
1241 // Bit 6: IPS active 1 indicates enabled.
1242 // Bit 7: IPS enabled. 1 indicates enabled.
1243 // mask off bit 6 --> & 0x02
1244 // Shift left one bit and store as bool. >> 1
1245 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1246 }
1247
1248 // This will only set IPS active dbus if different than current.
1249 if (ipsObject)
1250 {
1251 ipsObject->active(ipsState);
1252 }
1253 }
1254 else
1255 {
1256 removeIpsWatch();
1257
1258 // If the Retry did not get to "watching = true" we already have an
1259 // error log, just post trace.
1260 lg2::error("Retry on File seek Error({ERR})={STR} : File={FILE}", "ERR",
1261 open_errno, "STR", strerror(open_errno), "FILE",
1262 ipsStatusFile);
1263
1264 // NOTE: this will leave the system not reporting IPS active state to
1265 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1266 }
1267
1268 return;
1269 }
1270
1271 // overrides read/write to powerMode dbus property.
powerMode(Mode::PowerMode requestedMode)1272 Mode::PowerMode PowerMode::powerMode(Mode::PowerMode requestedMode)
1273 {
1274 if (persistedData.getModeLock())
1275 {
1276 lg2::info("PowerMode::powerMode: mode property change blocked");
1277 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1278 "mode change not allowed due to lock"));
1279 }
1280 else
1281 {
1282 // Verify requested mode is allowed
1283
1284 // Convert PowerMode to internal SysPwrMode
1285 SysPwrMode newMode = getInternalMode(requestedMode);
1286 if (newMode != SysPwrMode::NO_CHANGE)
1287 {
1288 // Validate it is an allowed customer mode
1289 if (customerModeList.contains(newMode))
1290 {
1291 // Update persisted data with new mode
1292 persistedData.updateMode(newMode, 0);
1293
1294 lg2::info("DBus PowerMode Changed: {MODE}", "MODE",
1295 convertPowerModeToString(requestedMode));
1296
1297 // Send mode change to OCC
1298 sendModeChange();
1299 }
1300 else
1301 {
1302 // Not Allowed
1303 lg2::error(
1304 "PowerMode change not allowed. {MODE} is not in AllowedPowerModes",
1305 "MODE", convertPowerModeToString(requestedMode));
1306 elog<NotAllowed>(
1307 xyz::openbmc_project::Common::NotAllowed::REASON(
1308 "PowerMode value not allowed"));
1309 }
1310 }
1311 else
1312 {
1313 // Value is not valid
1314 using InvalidArgument =
1315 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
1316 using Argument = xyz::openbmc_project::Common::InvalidArgument;
1317 lg2::error(
1318 "PowerMode not valid. {MODE} is not in AllowedPowerModes",
1319 "MODE", convertPowerModeToString(requestedMode));
1320 elog<InvalidArgument>(Argument::ARGUMENT_NAME("PowerMode"),
1321 Argument::ARGUMENT_VALUE("INVALID MODE"));
1322 }
1323 }
1324
1325 // All elog<> calls will cause trap (so code will not make it here)
1326
1327 return Mode::powerMode(requestedMode);
1328 }
1329
1330 /* Set dbus property to SAFE mode(true) or clear(false) only if different
1331 */
updateDbusSafeMode(const bool safeModeReq)1332 void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1333 {
1334 lg2::debug("PowerMode:updateDbusSafeMode: Update dbus state ({STATE})",
1335 "STATE", safeModeReq);
1336
1337 // Note; this function checks and only updates if different.
1338 Mode::safeMode(safeModeReq);
1339 }
1340
1341 // Get the supported power modes from DBus and return true if success
getSupportedModes()1342 bool PowerMode::getSupportedModes()
1343 {
1344 bool foundCustomerMode = false;
1345 using ModePropertyVariants =
1346 std::variant<bool, uint8_t, uint16_t, std::vector<std::string>>;
1347 std::map<std::string, ModePropertyVariants> powerModeProperties{};
1348
1349 // Get all power mode properties from DBus
1350 try
1351 {
1352 auto& bus = utils::getBus();
1353 std::string path = "/";
1354 std::string service =
1355 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
1356 auto method =
1357 bus.new_method_call(service.c_str(), path.c_str(),
1358 "org.freedesktop.DBus.Properties", "GetAll");
1359 method.append(PMODE_DEFAULT_INTERFACE);
1360 auto reply = bus.call(method);
1361 reply.read(powerModeProperties);
1362 }
1363 catch (const sdbusplus::exception_t& e)
1364 {
1365 lg2::error("Unable to read PowerModeProperties: {ERR}", "ERR",
1366 e.what());
1367 return false;
1368 }
1369
1370 // Determine if system suports EcoModes
1371 auto ecoSupport = powerModeProperties.find("EcoModeSupport");
1372 if (ecoSupport != powerModeProperties.end())
1373 {
1374 ecoModeSupport = std::get<bool>(ecoSupport->second);
1375 lg2::info("getSupportedModes(): ecoModeSupport: {SUPPORT}", "SUPPORT",
1376 ecoModeSupport);
1377 }
1378
1379 // Determine what customer modes are supported
1380 using PMode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
1381 std::set<PMode::PowerMode> modesToAllow;
1382 auto custList = powerModeProperties.find("CustomerModes");
1383 const std::string POWERMODE_STRING = ".PowerMode.";
1384 if (custList != powerModeProperties.end())
1385 {
1386 auto modeList = std::get<std::vector<std::string>>(custList->second);
1387 for (auto mode : modeList)
1388 {
1389 // Ensure mode is valid
1390 const std::string fullModeString =
1391 PMODE_INTERFACE + POWERMODE_STRING + mode;
1392 lg2::info("getSupportedModes(): {MODE}", "MODE", mode);
1393 SysPwrMode modeValue =
1394 powermode::convertStringToMode(fullModeString);
1395 if (VALID_POWER_MODE_SETTING(modeValue))
1396 {
1397 if (!foundCustomerMode)
1398 {
1399 // Start with empty list
1400 customerModeList.clear();
1401 foundCustomerMode = true;
1402 }
1403 // Add mode to list
1404 std::optional<PMode::PowerMode> cMode =
1405 PMode::convertStringToPowerMode(fullModeString);
1406 if (cMode)
1407 modesToAllow.insert(cMode.value());
1408 customerModeList.insert(modeValue);
1409 }
1410 else
1411 {
1412 lg2::error(
1413 "getSupportedModes(): Ignoring unsupported customer mode {MODE}",
1414 "MODE", mode);
1415 }
1416 }
1417 }
1418 if (foundCustomerMode)
1419 {
1420 ModeInterface::allowedPowerModes(modesToAllow);
1421 }
1422
1423 // Determine what OEM modes are supported
1424 auto oemList = powerModeProperties.find("OemModes");
1425 if (oemList != powerModeProperties.end())
1426 {
1427 bool foundValidMode = false;
1428 auto OmodeList = std::get<std::vector<std::string>>(oemList->second);
1429 for (auto mode : OmodeList)
1430 {
1431 // Ensure mode is valid
1432 const std::string fullModeString =
1433 PMODE_INTERFACE + POWERMODE_STRING + mode;
1434 SysPwrMode modeValue =
1435 powermode::convertStringToMode(fullModeString);
1436 if (VALID_POWER_MODE_SETTING(modeValue) ||
1437 VALID_OEM_POWER_MODE_SETTING(modeValue))
1438 {
1439 if (!foundValidMode)
1440 {
1441 // Start with empty list
1442 oemModeList.clear();
1443 foundValidMode = true;
1444 }
1445 // Add mode to list
1446 oemModeList.insert(modeValue);
1447 }
1448 else
1449 {
1450 lg2::error(
1451 "getSupportedModes(): Ignoring unsupported OEM mode {MODE}",
1452 "MODE", mode);
1453 }
1454 }
1455 }
1456
1457 return foundCustomerMode;
1458 }
1459
isValidMode(const SysPwrMode mode)1460 bool PowerMode::isValidMode(const SysPwrMode mode)
1461 {
1462 if (customerModeList.contains(mode) || oemModeList.contains(mode))
1463 {
1464 return true;
1465 }
1466 return false;
1467 }
1468
1469 } // namespace powermode
1470
1471 } // namespace occ
1472
1473 } // namespace open_power
1474