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