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