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(
75 {Mode::PowerMode::Static, Mode::PowerMode::MaximumPerformance,
76 Mode::PowerMode::PowerSaving});
77 }
78
79 SysPwrMode currentMode;
80 uint16_t oemModeData = 0;
81 // Read the persisted power mode
82 if (getMode(currentMode, oemModeData))
83 {
84 // Validate persisted mode is supported
85 if (isValidMode(currentMode))
86 {
87 // Update power mode on DBus
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>(
140 occInstance, 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 =
861 bus.new_method_call(service.c_str(), path.c_str(),
862 "org.freedesktop.DBus.Properties", "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 =
871 PMODE_INTERFACE + ".PowerMode."s + 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 =
918 bus.new_method_call(service.c_str(), path.c_str(),
919 "org.freedesktop.DBus.Properties", "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>(
1039 std::format("openIpsFile Error({})={} : File={}", open_errno,
1040 strerror(open_errno), 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>(
1204 std::format("Retry on File seek Error({})={} : File={}", open_errno,
1205 strerror(open_errno), 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 =
1309 bus.new_method_call(service.c_str(), path.c_str(),
1310 "org.freedesktop.DBus.Properties", "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 =
1344 PMODE_INTERFACE + ".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 =
1389 PMODE_INTERFACE + ".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