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