xref: /openbmc/openpower-occ-control/powermode.cpp (revision a21c4719ec0ec4cb2884bef26e1f7ca926db2b25)
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             else
458             {
459                 if (!watching)
460                 {
461                     // Starts watching for IPS state changes.
462                     addIpsWatch(true);
463                 }
464             }
465         }
466 
467         if (!masterActive || !masterOccSet)
468         {
469             // Nothing to do until OCC goes active
470             lg2::debug("PowerMode::sendModeChange: OCC master not active");
471             return CmdStatus::SUCCESS;
472         }
473 
474         if (!isPowerVM())
475         {
476             // Mode change is only supported on PowerVM systems
477             lg2::debug(
478                 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
479             return CmdStatus::SUCCESS;
480         }
481 
482         std::vector<std::uint8_t> cmd, rsp;
483         cmd.reserve(9);
484         cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
485         cmd.push_back(0x00); // Data Length (2 bytes)
486         cmd.push_back(0x06);
487         cmd.push_back(0x30); // Data (Version)
488         cmd.push_back(uint8_t(OccState::NO_CHANGE));
489         cmd.push_back(uint8_t(newMode));
490         cmd.push_back(oemModeData >> 8);   // Mode Data (Freq Point)
491         cmd.push_back(oemModeData & 0xFF); //
492         cmd.push_back(0x00);               // reserved
493         lg2::info(
494             "PowerMode::sendModeChange: SET_MODE({MODE},{DATA}) command to OCC{INST} ({LEN} bytes)",
495             "MODE", uint8_t(newMode), "DATA", oemModeData, "INST", occInstance,
496             "LEN", cmd.size());
497         status = occCmd->send(cmd, rsp);
498         if (status == CmdStatus::SUCCESS)
499         {
500             if (rsp.size() == 5)
501             {
502                 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
503                 {
504                     if (needToSendIpsData)
505                     {
506                         // Successful mode change and IPS is now allowed, so
507                         // send IPS config
508                         sendIpsData();
509                     }
510                 }
511                 else
512                 {
513                     lg2::error(
514                         "PowerMode::sendModeChange: SET MODE failed with status {STATUS}",
515                         "STATUS", lg2::hex, rsp[2]);
516                     dump_hex(rsp);
517                     status = CmdStatus::FAILURE;
518                 }
519             }
520             else
521             {
522                 lg2::error(
523                     "PowerMode::sendModeChange: INVALID SET MODE response");
524                 dump_hex(rsp);
525                 status = CmdStatus::FAILURE;
526             }
527         }
528         else
529         {
530             lg2::error(
531                 "PowerMode::sendModeChange: SET_MODE FAILED with status={STATUS}",
532                 "STATUS", lg2::hex, uint8_t(status));
533         }
534     }
535     else
536     {
537         lg2::error(
538             "PowerMode::sendModeChange: Unable to set power mode to {MODE}",
539             "MODE", newMode);
540         status = CmdStatus::FAILURE;
541     }
542 
543     return status;
544 }
545 
546 // Handle IPS changed event (from GUI/Redfish)
ipsChanged(sdbusplus::message_t & msg)547 void PowerMode::ipsChanged(sdbusplus::message_t& msg)
548 {
549     bool parmsChanged = false;
550     std::string interface;
551     std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
552         ipsProperties{};
553     msg.read(interface, ipsProperties);
554 
555     // Read persisted values
556     bool ipsEnabled;
557     uint8_t enterUtil, exitUtil;
558     uint16_t enterTime, exitTime;
559     getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
560 
561     if (!ipsObject)
562     {
563         lg2::warning(
564             "ipsChanged: Idle Power Saver can not be modified in an ECO power mode");
565         return;
566     }
567 
568     // Check for any changed data
569     auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
570     if (ipsEntry != ipsProperties.end())
571     {
572         ipsEnabled = std::get<bool>(ipsEntry->second);
573         lg2::info("Idle Power Saver change: Enabled={STAT}", "STAT",
574                   ipsEnabled);
575         parmsChanged = true;
576     }
577     ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
578     if (ipsEntry != ipsProperties.end())
579     {
580         enterUtil = std::get<uint8_t>(ipsEntry->second);
581         lg2::info("Idle Power Saver change: Enter Util={UTIL}%", "UTIL",
582                   enterUtil);
583         parmsChanged = true;
584     }
585     ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
586     if (ipsEntry != ipsProperties.end())
587     {
588         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
589         enterTime =
590             std::chrono::duration_cast<std::chrono::seconds>(ms).count();
591         lg2::info("Idle Power Saver change: Enter Time={TIME}sec", "TIME",
592                   enterTime);
593         parmsChanged = true;
594     }
595     ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
596     if (ipsEntry != ipsProperties.end())
597     {
598         exitUtil = std::get<uint8_t>(ipsEntry->second);
599         lg2::info("Idle Power Saver change: Exit Util={UTIL}%", "UTIL",
600                   exitUtil);
601         parmsChanged = true;
602     }
603     ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
604     if (ipsEntry != ipsProperties.end())
605     {
606         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
607         exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
608         lg2::info("Idle Power Saver change: Exit Time={TIME}sec", "TIME",
609                   exitTime);
610         parmsChanged = true;
611     }
612 
613     if (parmsChanged)
614     {
615         if (exitUtil == 0)
616         {
617             // Setting the exitUtil to 0 will force restoring the default IPS
618             // parmeters (0 is not valid exit utilization)
619             lg2::info(
620                 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
621             // Read the default IPS parameters, write persistent file and update
622             // DBus
623             useDefaultIPSParms();
624         }
625         else
626         {
627             // Update persistant data with new DBus values
628             persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
629                                     exitTime);
630         }
631 
632         // Trigger IPS data to get sent to the OCC
633         sendIpsData();
634     }
635 
636     return;
637 }
638 
639 /** @brief Get the Idle Power Saver properties from persisted data
640  * @return true if IPS parameters were read
641  */
getIPSParms(bool & ipsEnabled,uint8_t & enterUtil,uint16_t & enterTime,uint8_t & exitUtil,uint16_t & exitTime)642 bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
643                             uint16_t& enterTime, uint8_t& exitUtil,
644                             uint16_t& exitTime)
645 {
646     // Defaults:
647     ipsEnabled = true; // Enabled
648     enterUtil = 8;     // Enter Utilization (8%)
649     enterTime = 240;   // Enter Delay Time (240s)
650     exitUtil = 12;     // Exit Utilization (12%)
651     exitTime = 10;     // Exit Delay Time (10s)
652 
653     if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
654                               exitTime))
655     {
656         // Persistent data not initialized, read defaults and update DBus
657         if (!initPersistentData())
658         {
659             // Unable to read defaults from entity manager yet
660             return false;
661         }
662 
663         persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
664                              exitTime);
665     }
666 
667     if (enterUtil > exitUtil)
668     {
669         lg2::error(
670             "ERROR: Idle Power Saver Enter Utilization ({ENTER}%) is > Exit Utilization ({EXIT}%) - using Exit for both",
671             "ENTER", enterUtil, "EXIT", exitUtil);
672         enterUtil = exitUtil;
673     }
674 
675     return true;
676 }
677 
678 // 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)679 bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
680                               const uint16_t enterTime, const uint8_t exitUtil,
681                               const uint16_t exitTime)
682 {
683     if (ipsObject)
684     {
685         // true = skip update signal
686         ipsObject->setPropertyByName(IPS_ENABLED_PROP, enabled, true);
687         ipsObject->setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
688         // Convert time from seconds to ms
689         uint64_t msTime = enterTime * 1000;
690         ipsObject->setPropertyByName(IPS_ENTER_TIME, msTime, true);
691         ipsObject->setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
692         msTime = exitTime * 1000;
693         ipsObject->setPropertyByName(IPS_EXIT_TIME, msTime, true);
694     }
695     else
696     {
697         lg2::warning("updateDbusIPS: No IPS object was found");
698     }
699 
700     return true;
701 }
702 
703 // Send Idle Power Saver config data to the master OCC
sendIpsData()704 CmdStatus PowerMode::sendIpsData()
705 {
706     if (!masterActive || !masterOccSet)
707     {
708         // Nothing to do
709         return CmdStatus::SUCCESS;
710     }
711 
712     if (!isPowerVM())
713     {
714         // Idle Power Saver data is only supported on PowerVM systems
715         lg2::debug(
716             "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
717         return CmdStatus::SUCCESS;
718     }
719 
720     if (!ipsObject)
721     {
722         // Idle Power Saver data is not available in Eco Modes
723         lg2::info(
724             "PowerMode::sendIpsData: Skipping IPS data due to being in an ECO Power Mode");
725         return CmdStatus::SUCCESS;
726     }
727 
728     bool ipsEnabled;
729     uint8_t enterUtil, exitUtil;
730     uint16_t enterTime, exitTime;
731     getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
732 
733     lg2::info(
734         "Idle Power Saver Parameters: enabled:{ENABLE}, enter:{EUTIL}%/{ETIME}s, exit:{XUTIL}%/{XTIME}s",
735         "ENABLE", ipsEnabled, "EUTIL", enterUtil, "ETIME", enterTime, "XUTIL",
736         exitUtil, "XTIME", exitTime);
737 
738     std::vector<std::uint8_t> cmd, rsp;
739     cmd.reserve(12);
740     cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
741     cmd.push_back(0x00);               // Data Length (2 bytes)
742     cmd.push_back(0x09);               //
743     cmd.push_back(0x11);               // Config Format: IPS Settings
744     cmd.push_back(0x00);               // Version
745     cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
746     cmd.push_back(enterTime >> 8);     // Enter Delay Time
747     cmd.push_back(enterTime & 0xFF);   //
748     cmd.push_back(enterUtil);          // Enter Utilization
749     cmd.push_back(exitTime >> 8);      // Exit Delay Time
750     cmd.push_back(exitTime & 0xFF);    //
751     cmd.push_back(exitUtil);           // Exit Utilization
752     lg2::info("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
753               "command to OCC{INST} ({LEN} bytes)",
754               "INST", occInstance, "LEN", cmd.size());
755     CmdStatus status = occCmd->send(cmd, rsp);
756     if (status == CmdStatus::SUCCESS)
757     {
758         if (rsp.size() == 5)
759         {
760             if (RspStatus::SUCCESS == RspStatus(rsp[2]))
761             {
762                 needToSendIpsData = false;
763             }
764             else
765             {
766                 lg2::error(
767                     "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status {STATUS}",
768                     "STATUS", lg2::hex, rsp[2]);
769                 dump_hex(rsp);
770                 status = CmdStatus::FAILURE;
771             }
772         }
773         else
774         {
775             lg2::error(
776                 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
777             dump_hex(rsp);
778             status = CmdStatus::FAILURE;
779         }
780     }
781     else
782     {
783         lg2::error(
784             "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={STATUS}",
785             "STATUS", lg2::hex, uint8_t(status));
786     }
787 
788     return status;
789 }
790 
791 // Print the current values
print()792 void OccPersistData::print()
793 {
794     if (modeData.modeInitialized)
795     {
796         lg2::info(
797             "OccPersistData: Mode: {MODE}, OEM Mode Data: {DATA} ({DATAHEX} Locked{LOCK})",
798             "MODE", lg2::hex, uint8_t(modeData.mode), "DATA",
799             modeData.oemModeData, "DATAHEX", lg2::hex, modeData.oemModeData,
800             "LOCK", modeData.modeLocked);
801     }
802     if (modeData.ipsInitialized)
803     {
804         lg2::info(
805             "OccPersistData: IPS enabled:{ENABLE}, enter:{EUTIL}%/{ETIME}s, exit:{XUTIL}%/{XTIME}s",
806             "ENABLE", modeData.ipsEnabled, "EUTIL", modeData.ipsEnterUtil,
807             "ETIME", modeData.ipsEnterTime, "XUTIL", modeData.ipsExitUtil,
808             "XTIME", modeData.ipsExitTime);
809     }
810 }
811 
812 // Saves the OEM mode data in the filesystem using cereal.
save()813 void OccPersistData::save()
814 {
815     std::filesystem::path opath =
816         std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
817 
818     if (!std::filesystem::exists(opath.parent_path()))
819     {
820         std::filesystem::create_directory(opath.parent_path());
821     }
822 
823     lg2::debug(
824         "OccPersistData::save: Writing Power Mode persisted data to {FILE}",
825         "FILE", opath);
826     // print();
827 
828     std::ofstream stream{opath.c_str()};
829     cereal::JSONOutputArchive oarchive{stream};
830 
831     oarchive(modeData);
832 }
833 
834 // Loads the OEM mode data in the filesystem using cereal.
load()835 void OccPersistData::load()
836 {
837     std::filesystem::path ipath =
838         std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
839 
840     if (!std::filesystem::exists(ipath))
841     {
842         modeData.modeInitialized = false;
843         modeData.ipsInitialized = false;
844         return;
845     }
846 
847     lg2::debug(
848         "OccPersistData::load: Reading Power Mode persisted data from {FILE}",
849         "FILE", ipath);
850     try
851     {
852         std::ifstream stream{ipath.c_str()};
853         cereal::JSONInputArchive iarchive(stream);
854         iarchive(modeData);
855     }
856     catch (const std::exception& e)
857     {
858         auto error = errno;
859         lg2::error("OccPersistData::load: failed to read {FILE}, errno={ERR}",
860                    "FILE", ipath, "ERR", error);
861         modeData.modeInitialized = false;
862         modeData.ipsInitialized = false;
863     }
864 
865     // print();
866 }
867 
868 // Called when PowerModeProperties defaults are available on DBus
defaultsReady(sdbusplus::message_t & msg)869 void PowerMode::defaultsReady(sdbusplus::message_t& msg)
870 {
871     std::map<std::string, std::variant<std::string>> properties{};
872     std::string interface;
873     msg.read(interface, properties);
874 
875     if (persistedData.modeAvailable())
876     {
877         // Validate persisted mode is supported
878         SysPwrMode pMode = SysPwrMode::NO_CHANGE;
879         uint16_t oemModeData = 0;
880         persistedData.getMode(pMode, oemModeData);
881         if (!isValidMode(pMode))
882         {
883             lg2::error(
884                 "defaultsReady: Persisted power mode ({MODE}/{DATA}) is not valid. Reading system default mode",
885                 "MODE", pMode, "DATA", oemModeData);
886             persistedData.invalidateMode();
887         }
888     }
889 
890     // If persistent data exists, then don't need to read defaults
891     if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
892     {
893         lg2::info(
894             "Default PowerModeProperties are now available (persistent modeAvail={MAVAIL}, ipsAvail={IAVAIL})",
895             "MAVAIL", persistedData.modeAvailable(), "IAVAIL",
896             persistedData.ipsAvailable());
897 
898         // Read default power mode defaults and update DBus
899         initPersistentData();
900     }
901 }
902 
903 // Get the default power mode from DBus and return true if success
getDefaultMode(SysPwrMode & defaultMode)904 bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
905 {
906     try
907     {
908         auto& bus = utils::getBus();
909         std::string path = "/";
910         std::string service =
911             utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
912         auto method =
913             bus.new_method_call(service.c_str(), path.c_str(),
914                                 "org.freedesktop.DBus.Properties", "Get");
915         method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
916         auto reply = bus.call(method);
917 
918         std::variant<std::string> stateEntryValue;
919         reply.read(stateEntryValue);
920         auto propVal = std::get<std::string>(stateEntryValue);
921 
922         const std::string fullModeString =
923             PMODE_INTERFACE + ".PowerMode."s + propVal;
924         defaultMode = powermode::convertStringToMode(fullModeString);
925         if (!VALID_POWER_MODE_SETTING(defaultMode))
926         {
927             lg2::error("PowerMode::getDefaultMode: Invalid "
928                        "default power mode found: {MODE}",
929                        "MODE", defaultMode);
930             // If default was read but not valid, use Max Performance
931             defaultMode = SysPwrMode::MAX_PERF;
932             return true;
933         }
934     }
935     catch (const sdbusplus::exception_t& e)
936     {
937         lg2::error("Unable to read Default Power Mode: {ERR}", "ERR", e.what());
938         return false;
939     }
940 
941     return true;
942 }
943 
944 /* 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)945 bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
946                                    uint16_t& enterTime, uint8_t& exitUtil,
947                                    uint16_t& exitTime)
948 {
949     // Defaults:
950     ipsEnabled = true; // Enabled
951     enterUtil = 8;     // Enter Utilization (8%)
952     enterTime = 240;   // Enter Delay Time (240s)
953     exitUtil = 12;     // Exit Utilization (12%)
954     exitTime = 10;     // Exit Delay Time (10s)
955 
956     std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
957         ipsProperties{};
958 
959     // Get all IPS properties from DBus
960     try
961     {
962         auto& bus = utils::getBus();
963         std::string path = "/";
964         std::string service =
965             utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
966         auto method =
967             bus.new_method_call(service.c_str(), path.c_str(),
968                                 "org.freedesktop.DBus.Properties", "GetAll");
969         method.append(PMODE_DEFAULT_INTERFACE);
970         auto reply = bus.call(method);
971         reply.read(ipsProperties);
972     }
973     catch (const sdbusplus::exception_t& e)
974     {
975         lg2::error(
976             "Unable to read Default Idle Power Saver parameters so it will be disabled: {ERR}",
977             "ERR", e.what());
978         return false;
979     }
980 
981     auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
982     if (ipsEntry != ipsProperties.end())
983     {
984         ipsEnabled = std::get<bool>(ipsEntry->second);
985     }
986     else
987     {
988         lg2::error(
989             "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
990     }
991 
992     ipsEntry = ipsProperties.find("EnterUtilizationPercent");
993     if (ipsEntry != ipsProperties.end())
994     {
995         enterUtil = std::get<uint64_t>(ipsEntry->second);
996     }
997     else
998     {
999         lg2::error(
1000             "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
1001     }
1002 
1003     ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
1004     if (ipsEntry != ipsProperties.end())
1005     {
1006         enterTime = std::get<uint64_t>(ipsEntry->second);
1007     }
1008     else
1009     {
1010         lg2::error(
1011             "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
1012     }
1013 
1014     ipsEntry = ipsProperties.find("ExitUtilizationPercent");
1015     if (ipsEntry != ipsProperties.end())
1016     {
1017         exitUtil = std::get<uint64_t>(ipsEntry->second);
1018     }
1019     else
1020     {
1021         lg2::error(
1022             "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
1023     }
1024 
1025     ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
1026     if (ipsEntry != ipsProperties.end())
1027     {
1028         exitTime = std::get<uint64_t>(ipsEntry->second);
1029     }
1030     else
1031     {
1032         lg2::error(
1033             "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
1034     }
1035 
1036     if (enterUtil > exitUtil)
1037     {
1038         lg2::error(
1039             "ERROR: Default Idle Power Saver Enter Utilization ({ENTER}%) is > Exit Utilization ({EXIT}%) - using Exit for both",
1040             "ENTER", enterUtil, "EXIT", exitUtil);
1041         enterUtil = exitUtil;
1042     }
1043 
1044     return true;
1045 }
1046 
1047 /* Read default IPS parameters, save them to the persistent file and update
1048  DBus. Return true if successful */
useDefaultIPSParms()1049 bool PowerMode::useDefaultIPSParms()
1050 {
1051     // Read the default IPS parameters
1052     bool ipsEnabled;
1053     uint8_t enterUtil, exitUtil;
1054     uint16_t enterTime, exitTime;
1055     if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
1056                             exitTime))
1057     {
1058         // Unable to read defaults
1059         return false;
1060     }
1061     lg2::info("PowerMode::useDefaultIPSParms: Using default IPS parms: "
1062               "Enabled: {ENABLE}, EnterUtil: {EUTIL}%, EnterTime: {ETIME}s, "
1063               "ExitUtil: {XUTIL}%, ExitTime: {XTIME}s",
1064               "ENABLE", ipsEnabled, "EUTIL", enterUtil, "ETIME", enterTime,
1065               "XUTIL", exitUtil, "XTIME", exitTime);
1066 
1067     // Save IPS parms to the persistent file
1068     persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
1069                             exitTime);
1070 
1071     // Write IPS parms to DBus
1072     return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
1073 }
1074 
1075 // Starts to watch for IPS active state changes.
openIpsFile()1076 bool PowerMode::openIpsFile()
1077 {
1078     bool rc = true;
1079     fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
1080     const int open_errno = errno;
1081     if (fd < 0)
1082     {
1083         lg2::error("openIpsFile Error({ERR})={STR} : File={FILE}", "ERR",
1084                    open_errno, "STR", strerror(open_errno), "FILE",
1085                    ipsStatusFile);
1086 
1087         close(fd);
1088 
1089         using namespace sdbusplus::org::open_power::OCC::Device::Error;
1090         report<OpenFailure>(
1091             phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1092                 CALLOUT_ERRNO(open_errno),
1093             phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1094                 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1095 
1096         // We are no longer watching the error
1097         if (ipsObject)
1098         {
1099             ipsObject->active(false);
1100         }
1101 
1102         watching = false;
1103         rc = false;
1104         // NOTE: this will leave the system not reporting IPS active state to
1105         // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1106     }
1107     return rc;
1108 }
1109 
1110 // Starts to watch for IPS active state changes.
addIpsWatch(bool poll)1111 void PowerMode::addIpsWatch(bool poll)
1112 {
1113     // open file and register callback on file if we are not currently watching,
1114     // and if poll=true, and if we are the master.
1115     if ((!watching) && poll)
1116     {
1117         //  Open the file
1118         if (openIpsFile())
1119         {
1120             // register the callback handler which sets 'watching'
1121             registerIpsStatusCallBack();
1122         }
1123     }
1124 }
1125 
1126 // Stops watching for IPS active state changes.
removeIpsWatch()1127 void PowerMode::removeIpsWatch()
1128 {
1129     //  NOTE: we want to remove event, close file, and IPS active false no
1130     //  matter what the 'watching' flags is set to.
1131 
1132     // We are no longer watching the error
1133     if (ipsObject)
1134     {
1135         ipsObject->active(false);
1136     }
1137 
1138     watching = false;
1139 
1140     // Close file
1141     close(fd);
1142 
1143     // clears sourcePtr in the event source.
1144     eventSource.reset();
1145 }
1146 
1147 // Attaches the FD to event loop and registers the callback handler
registerIpsStatusCallBack()1148 void PowerMode::registerIpsStatusCallBack()
1149 {
1150     decltype(eventSource.get()) sourcePtr = nullptr;
1151 
1152     auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1153                              ipsStatusCallBack, this);
1154     if (r < 0)
1155     {
1156         lg2::error("sd_event_add_io: Error({ERR})={STR} : File={FILE}", "ERR",
1157                    r, "STR", strerror(-r), "FILE", ipsStatusFile);
1158 
1159         using InternalFailure =
1160             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1161         report<InternalFailure>();
1162 
1163         removeIpsWatch();
1164         // NOTE: this will leave the system not reporting IPS active state to
1165         // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1166     }
1167     else
1168     {
1169         // puts sourcePtr in the event source.
1170         eventSource.reset(sourcePtr);
1171         // Set we are watching the error
1172         watching = true;
1173     }
1174 }
1175 
1176 // Static function to redirect to non static analyze event function to be
1177 // able to read file and push onto dBus.
ipsStatusCallBack(sd_event_source *,int,uint32_t,void * userData)1178 int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1179                                  uint32_t /*revents*/, void* userData)
1180 {
1181     auto pmode = static_cast<PowerMode*>(userData);
1182     pmode->analyzeIpsEvent();
1183     return 0;
1184 }
1185 
1186 // Function to Read SysFs file change on IPS state and push on dBus.
analyzeIpsEvent()1187 void PowerMode::analyzeIpsEvent()
1188 {
1189     // Need to seek to START, else the poll returns immediately telling
1190     // there is data to be read. if not done this floods the system.
1191     auto r = lseek(fd, 0, SEEK_SET);
1192     const int open_errno = errno;
1193     if (r < 0)
1194     {
1195         // NOTE: upon file access error we can not just re-open file, we have to
1196         // remove and add to watch.
1197         removeIpsWatch();
1198         addIpsWatch(true);
1199     }
1200 
1201     // if we are 'watching' that is the file seek, or the re-open passed.. we
1202     // can read the data
1203     if (watching)
1204     {
1205         // This file gets created when polling OCCs. A value or length of 0 is
1206         // deemed success. That means we would disable IPS active on dbus.
1207         char data;
1208         bool ipsState = false;
1209         const auto len = read(fd, &data, sizeof(data));
1210         const int readErrno = errno;
1211         if (len <= 0)
1212         {
1213             removeIpsWatch();
1214 
1215             lg2::error(
1216                 "IPS state Read Error({ERR})={STR} : File={FILE} : len={LEN}",
1217                 "ERR", readErrno, "STR", strerror(readErrno), "FILE",
1218                 ipsStatusFile, "LEN", len);
1219 
1220             report<ReadFailure>(
1221                 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1222                     CALLOUT_ERRNO(readErrno),
1223                 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1224                     CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1225 
1226             // NOTE: this will leave the system not reporting IPS active state
1227             // to Fan Controls, Until an APP reload, or IPL and we will attempt
1228             // again.
1229         }
1230         else
1231         {
1232             // Data returned in ASCII.
1233             // convert to integer. atoi()
1234             // from OCC_P10_FW_Interfaces spec
1235             //          Bit 6: IPS active   1 indicates enabled.
1236             //          Bit 7: IPS enabled. 1 indicates enabled.
1237             //      mask off bit 6 --> & 0x02
1238             // Shift left one bit and store as bool. >> 1
1239             ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1240         }
1241 
1242         // This will only set IPS active dbus if different than current.
1243         if (ipsObject)
1244         {
1245             ipsObject->active(ipsState);
1246         }
1247     }
1248     else
1249     {
1250         removeIpsWatch();
1251 
1252         // If the Retry did not get to "watching = true" we already have an
1253         // error log, just post trace.
1254         lg2::error("Retry on File seek Error({ERR})={STR} : File={FILE}", "ERR",
1255                    open_errno, "STR", strerror(open_errno), "FILE",
1256                    ipsStatusFile);
1257 
1258         // NOTE: this will leave the system not reporting IPS active state to
1259         // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1260     }
1261 
1262     return;
1263 }
1264 
1265 // overrides read/write to powerMode dbus property.
powerMode(Mode::PowerMode requestedMode)1266 Mode::PowerMode PowerMode::powerMode(Mode::PowerMode requestedMode)
1267 {
1268     if (persistedData.getModeLock())
1269     {
1270         lg2::info("PowerMode::powerMode: mode property change blocked");
1271         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1272             "mode change not allowed due to lock"));
1273     }
1274     else
1275     {
1276         // Verify requested mode is allowed
1277 
1278         // Convert PowerMode to internal SysPwrMode
1279         SysPwrMode newMode = getInternalMode(requestedMode);
1280         if (newMode != SysPwrMode::NO_CHANGE)
1281         {
1282             // Validate it is an allowed customer mode
1283             if (customerModeList.contains(newMode))
1284             {
1285                 // Update persisted data with new mode
1286                 persistedData.updateMode(newMode, 0);
1287 
1288                 lg2::info("DBus PowerMode Changed: {MODE}", "MODE",
1289                           convertPowerModeToString(requestedMode));
1290 
1291                 // Send mode change to OCC
1292                 sendModeChange();
1293             }
1294             else
1295             {
1296                 // Not Allowed
1297                 lg2::error(
1298                     "PowerMode change not allowed. {MODE} is not in AllowedPowerModes",
1299                     "MODE", convertPowerModeToString(requestedMode));
1300                 elog<NotAllowed>(
1301                     xyz::openbmc_project::Common::NotAllowed::REASON(
1302                         "PowerMode value not allowed"));
1303             }
1304         }
1305         else
1306         {
1307             // Value is not valid
1308             using InvalidArgument =
1309                 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
1310             using Argument = xyz::openbmc_project::Common::InvalidArgument;
1311             lg2::error(
1312                 "PowerMode not valid. {MODE} is not in AllowedPowerModes",
1313                 "MODE", convertPowerModeToString(requestedMode));
1314             elog<InvalidArgument>(Argument::ARGUMENT_NAME("PowerMode"),
1315                                   Argument::ARGUMENT_VALUE("INVALID MODE"));
1316         }
1317     }
1318 
1319     // All elog<> calls will cause trap (so code will not make it here)
1320 
1321     return Mode::powerMode(requestedMode);
1322 }
1323 
1324 /*  Set dbus property to SAFE mode(true) or clear(false) only if different
1325  */
updateDbusSafeMode(const bool safeModeReq)1326 void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1327 {
1328     lg2::debug("PowerMode:updateDbusSafeMode: Update dbus state ({STATE})",
1329                "STATE", safeModeReq);
1330 
1331     // Note; this function checks and only updates if different.
1332     Mode::safeMode(safeModeReq);
1333 }
1334 
1335 // Get the supported power modes from DBus and return true if success
getSupportedModes()1336 bool PowerMode::getSupportedModes()
1337 {
1338     bool foundCustomerMode = false;
1339     using ModePropertyVariants =
1340         std::variant<bool, uint8_t, uint16_t, std::vector<std::string>>;
1341     std::map<std::string, ModePropertyVariants> powerModeProperties{};
1342 
1343     // Get all power mode properties from DBus
1344     try
1345     {
1346         auto& bus = utils::getBus();
1347         std::string path = "/";
1348         std::string service =
1349             utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
1350         auto method =
1351             bus.new_method_call(service.c_str(), path.c_str(),
1352                                 "org.freedesktop.DBus.Properties", "GetAll");
1353         method.append(PMODE_DEFAULT_INTERFACE);
1354         auto reply = bus.call(method);
1355         reply.read(powerModeProperties);
1356     }
1357     catch (const sdbusplus::exception_t& e)
1358     {
1359         lg2::error("Unable to read PowerModeProperties: {ERR}", "ERR",
1360                    e.what());
1361         return false;
1362     }
1363 
1364     // Determine if system suports EcoModes
1365     auto ecoSupport = powerModeProperties.find("EcoModeSupport");
1366     if (ecoSupport != powerModeProperties.end())
1367     {
1368         ecoModeSupport = std::get<bool>(ecoSupport->second);
1369         lg2::info("getSupportedModes(): ecoModeSupport: {SUPPORT}", "SUPPORT",
1370                   ecoModeSupport);
1371     }
1372 
1373     // Determine what customer modes are supported
1374     using PMode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
1375     std::set<PMode::PowerMode> modesToAllow;
1376     auto custList = powerModeProperties.find("CustomerModes");
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."s + 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."s + 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