blob: 2179bd88d61766764390109c5f2c7658791cd5be [file] [log] [blame]
Chris Cain36f9cde2021-11-22 11:18:21 -06001#include "powermode.hpp"
2
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05003#include <fcntl.h>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05004#include <sys/ioctl.h>
Chris Cain78e86012021-03-04 16:15:31 -06005
Patrick Williams48002492024-02-13 21:43:32 -06006#include <format>
7
Chris Cain52cce032023-02-09 15:38:31 -06008#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -06009#include <com/ibm/Host/Target/server.hpp>
Chris Cain52cce032023-02-09 15:38:31 -060010#endif
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050011#include <org/open_power/OCC/Device/error.hpp>
Ben Tyner3576d652022-05-22 18:05:53 -050012#include <phosphor-logging/elog-errors.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060013#include <phosphor-logging/log.hpp>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050014#include <xyz/openbmc_project/Common/error.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060015#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
16
George Liub5ca1012021-09-10 12:53:11 +080017#include <cassert>
Chris Cain36f9cde2021-11-22 11:18:21 -060018#include <fstream>
George Liub5ca1012021-09-10 12:53:11 +080019#include <regex>
20
Chris Cain78e86012021-03-04 16:15:31 -060021namespace open_power
22{
23namespace occ
24{
25namespace powermode
26{
27
28using namespace phosphor::logging;
Chris Cain1be43372021-12-09 19:29:37 -060029using namespace std::literals::string_literals;
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050030using namespace sdbusplus::org::open_power::OCC::Device::Error;
Chris Cain1be43372021-12-09 19:29:37 -060031
Chris Cain78e86012021-03-04 16:15:31 -060032using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
33
Ben Tyner3576d652022-05-22 18:05:53 -050034using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
Chris Cain30040d92024-06-19 16:50:34 -050035
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))
Chris Cain1fe436d2024-10-10 09:41:03 -050047// List of all Power Modes that disable IPS
48#define IS_ECO_MODE(mode) \
49 ((mode == SysPwrMode::EFF_FAVOR_POWER) || \
50 (mode == SysPwrMode::EFF_FAVOR_PERF))
Chris Cain30040d92024-06-19 16:50:34 -050051
52// Constructor
53PowerMode::PowerMode(const Manager& managerRef, const char* modePath,
54 const char* ipsPath, EventPtr& event) :
55 ModeInterface(utils::getBus(), modePath,
56 ModeInterface::action::emit_no_signals),
Chris Cain30040d92024-06-19 16:50:34 -050057 manager(managerRef),
58 ipsMatch(utils::getBus(),
59 sdbusplus::bus::match::rules::propertiesChanged(PIPS_PATH,
60 PIPS_INTERFACE),
61 [this](auto& msg) { this->ipsChanged(msg); }),
62 defaultsUpdateMatch(
63 utils::getBus(),
64 sdbusplus::bus::match::rules::propertiesChangedNamespace(
65 "/xyz/openbmc_project/inventory", PMODE_DEFAULT_INTERFACE),
66 [this](auto& msg) { this->defaultsReady(msg); }),
Chris Cain1fe436d2024-10-10 09:41:03 -050067 masterOccSet(false), masterActive(false), ipsObjectPath(ipsPath),
68 event(event)
Chris Cain30040d92024-06-19 16:50:34 -050069{
70 // Get supported power modes from entity manager
71 if (false == getSupportedModes())
72 {
73 // Did not find them so use default customer modes
74 using Mode =
75 sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
76 // Update power modes that will be allowed by the Redfish interface
Patrick Williamsd7542c82024-08-16 15:20:28 -040077 ModeInterface::allowedPowerModes(
78 {Mode::PowerMode::Static, Mode::PowerMode::MaximumPerformance,
79 Mode::PowerMode::PowerSaving});
Chris Cain30040d92024-06-19 16:50:34 -050080 }
81
82 SysPwrMode currentMode;
83 uint16_t oemModeData = 0;
84 // Read the persisted power mode
85 if (getMode(currentMode, oemModeData))
86 {
87 // Validate persisted mode is supported
88 if (isValidMode(currentMode))
89 {
Chris Cain1fe436d2024-10-10 09:41:03 -050090 // Update power mode on DBus and create IPS object if allowed
Chris Cain30040d92024-06-19 16:50:34 -050091 updateDbusMode(currentMode);
92 }
93 else
94 {
95 log<level::ERR>(
96 std::format(
97 "PowerMode: Persisted power mode ({}/{}) is not valid. Reading system default mode",
98 currentMode, oemModeData)
99 .c_str());
100 persistedData.invalidateMode();
101 // Read default power mode
102 initPersistentData();
103 }
104 }
Chris Cain30040d92024-06-19 16:50:34 -0500105};
Ben Tyner3576d652022-05-22 18:05:53 -0500106
Chris Cain1fe436d2024-10-10 09:41:03 -0500107void PowerMode::createIpsObject()
108{
109 if (!ipsObject)
110 {
111 log<level::INFO>("createIpsObject: Creating IPS object");
112 ipsObject =
113 std::make_unique<IpsInterface>(utils::getBus(), ipsObjectPath);
114
115 uint8_t enterUtil, exitUtil;
116 uint16_t enterTime, exitTime;
117 bool ipsEnabled;
118 // Read the persisted Idle Power Saver parametres
119 if (getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime))
120 {
121 // Update Idle Power Saver parameters on DBus
122 updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
123 }
124
125 // Starts watching for IPS state changes.
126 addIpsWatch(true);
127
128 needToSendIpsData = true;
129 }
130}
131
132void PowerMode::removeIpsObject()
133{
134 if (ipsObject)
135 {
136 // Stop watching for IPS state changes.
137 removeIpsWatch();
138
139 log<level::INFO>("removeIpsObject: Deleting IPS object");
140 ipsObject.reset(nullptr);
141 }
142 needToSendIpsData = false;
143}
144
Chris Cain6fa848a2022-01-24 14:54:38 -0600145// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -0600146void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -0600147{
Chris Cain1be43372021-12-09 19:29:37 -0600148 if (masterOccSet)
149 {
150 if (masterOccPath != path)
151 {
152 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600153 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600154 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
155 occInstance, masterOccPath)
156 .c_str());
157 if (occCmd)
158 {
159 occCmd.reset();
160 }
161 }
162 }
163 path = masterOccPath;
Chris Cain6fa848a2022-01-24 14:54:38 -0600164 occInstance = path.back() - '0';
Patrick Williams48002492024-02-13 21:43:32 -0600165 log<level::DEBUG>(std::format("PowerMode::setMasterOcc(OCC{}, {})",
Chris Cain6fa848a2022-01-24 14:54:38 -0600166 occInstance, path.c_str())
167 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600168 if (!occCmd)
169 {
Patrick Williamsd7542c82024-08-16 15:20:28 -0400170 occCmd = std::make_unique<open_power::occ::OccCommand>(
171 occInstance, path.c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600172 }
Chris Cain6fa848a2022-01-24 14:54:38 -0600173 masterOccSet = true;
174};
175
Ben Tyner3576d652022-05-22 18:05:53 -0500176// Set the state of power mode lock. Writing persistent data via dbus method.
177bool PowerMode::powerModeLock()
178{
179 log<level::INFO>("PowerMode::powerModeLock: locking mode change");
180 persistedData.updateModeLock(true); // write persistent data
181 return true;
182}
183
184// Get the state of power mode. Reading persistent data via dbus method.
185bool PowerMode::powerModeLockStatus()
186{
187 bool status = persistedData.getModeLock(); // read persistent data
Patrick Williams48002492024-02-13 21:43:32 -0600188 log<level::INFO>(std::format("PowerMode::powerModeLockStatus: {}",
Ben Tyner3576d652022-05-22 18:05:53 -0500189 status ? "locked" : "unlocked")
190 .c_str());
191 return status;
192}
193
Chris Cain36f9cde2021-11-22 11:18:21 -0600194// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -0600195bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -0600196{
Ben Tyner3576d652022-05-22 18:05:53 -0500197 if (persistedData.getModeLock())
198 {
199 log<level::INFO>("PowerMode::setMode: mode change blocked");
200 return false;
201 }
202
Chris Cain36f9cde2021-11-22 11:18:21 -0600203 if (updateDbusMode(newMode) == false)
204 {
205 // Unsupported mode
206 return false;
207 }
208
Chris Cain1be43372021-12-09 19:29:37 -0600209 // Save mode
210 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600211
Chris Cain1be43372021-12-09 19:29:37 -0600212 // Send mode change to OCC
213 if (sendModeChange() != CmdStatus::SUCCESS)
214 {
215 // Mode change failed
216 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600217 }
218
Chris Cain36f9cde2021-11-22 11:18:21 -0600219 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600220}
221
Chris Cain30040d92024-06-19 16:50:34 -0500222// Convert PowerMode value to occ-control internal SysPwrMode
223// Returns SysPwrMode::NO_CHANGE if mode not valid
224SysPwrMode getInternalMode(const Mode::PowerMode& mode)
225{
226 if (mode == Mode::PowerMode::MaximumPerformance)
227 {
228 return SysPwrMode::MAX_PERF;
229 }
230 else if (mode == Mode::PowerMode::PowerSaving)
231 {
232 return SysPwrMode::POWER_SAVING;
233 }
234 else if (mode == Mode::PowerMode::Static)
235 {
236 return SysPwrMode::STATIC;
237 }
238 else if (mode == Mode::PowerMode::EfficiencyFavorPower)
239 {
240 return SysPwrMode::EFF_FAVOR_POWER;
241 }
242 else if (mode == Mode::PowerMode::EfficiencyFavorPerformance)
243 {
244 return SysPwrMode::EFF_FAVOR_PERF;
245 }
246 else if (mode == Mode::PowerMode::BalancedPerformance)
247 {
248 return SysPwrMode::BALANCED_PERF;
249 }
250
251 log<level::WARNING>("getInternalMode: Invalid PowerMode specified");
252 return SysPwrMode::NO_CHANGE;
253}
254
Chris Cain78e86012021-03-04 16:15:31 -0600255// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600256// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600257SysPwrMode convertStringToMode(const std::string& i_modeString)
258{
Chris Cain30040d92024-06-19 16:50:34 -0500259 SysPwrMode newMode = SysPwrMode::NO_CHANGE;
260 try
Chris Cain78e86012021-03-04 16:15:31 -0600261 {
Chris Cain30040d92024-06-19 16:50:34 -0500262 Mode::PowerMode newPMode =
263 Mode::convertPowerModeFromString(i_modeString);
264 newMode = getInternalMode(newPMode);
Chris Cain78e86012021-03-04 16:15:31 -0600265 }
Chris Cain30040d92024-06-19 16:50:34 -0500266 catch (const std::exception& e)
Chris Cain78e86012021-03-04 16:15:31 -0600267 {
Chris Cain30040d92024-06-19 16:50:34 -0500268 // Strip off prefix to to search OEM modes not part of Redfish
269 auto prefix = PMODE_INTERFACE + ".PowerMode."s;
270 std::string shortMode = i_modeString;
271 std::string::size_type index = i_modeString.find(prefix);
272 if (index != std::string::npos)
273 {
274 shortMode.erase(0, prefix.length());
275 }
276 if (shortMode == "FFO")
277 {
278 newMode = SysPwrMode::FFO;
279 }
280 else if (shortMode == "SFP")
281 {
282 newMode = SysPwrMode::SFP;
283 }
284 else if (shortMode == "MaxFrequency")
285 {
286 newMode = SysPwrMode::MAX_FREQ;
287 }
288 else if (shortMode == "NonDeterministic")
289 {
290 newMode = SysPwrMode::NON_DETERMINISTIC;
291 }
292 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600293 {
294 log<level::ERR>(
Chris Cain30040d92024-06-19 16:50:34 -0500295 std::format("convertStringToMode: Invalid Power Mode: {} ({})",
296 shortMode, e.what())
Chris Cain36f9cde2021-11-22 11:18:21 -0600297 .c_str());
298 }
Chris Cain78e86012021-03-04 16:15:31 -0600299 }
Chris Cain30040d92024-06-19 16:50:34 -0500300 return newMode;
Chris Cain78e86012021-03-04 16:15:31 -0600301}
302
Chris Cain36f9cde2021-11-22 11:18:21 -0600303// Check if Hypervisor target is PowerVM
304bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500305{
Chris Cain52cce032023-02-09 15:38:31 -0600306 bool powerVmTarget = true;
307#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -0600308 namespace Hyper = sdbusplus::com::ibm::Host::server;
309 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
310 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
311 constexpr auto HYPE_PROP = "Target";
312
Chris Cain36f9cde2021-11-22 11:18:21 -0600313 // This will throw exception on failure
314 auto& bus = utils::getBus();
315 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
316 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
317 "org.freedesktop.DBus.Properties", "Get");
318 method.append(HYPE_INTERFACE, HYPE_PROP);
319 auto reply = bus.call(method);
320
321 std::variant<std::string> hyperEntryValue;
322 reply.read(hyperEntryValue);
323 auto propVal = std::get<std::string>(hyperEntryValue);
324 if (Hyper::Target::convertHypervisorFromString(propVal) ==
325 Hyper::Target::Hypervisor::PowerVM)
326 {
327 powerVmTarget = true;
328 }
329
330 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600331 std::format("isPowerVM returning {}", powerVmTarget).c_str());
Chris Cain52cce032023-02-09 15:38:31 -0600332#endif
Chris Cain36f9cde2021-11-22 11:18:21 -0600333
334 return powerVmTarget;
335}
336
Chris Cain1be43372021-12-09 19:29:37 -0600337// Initialize persistent data and return true if successful
338bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600339{
Chris Cain1be43372021-12-09 19:29:37 -0600340 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600341 {
Chris Cain1be43372021-12-09 19:29:37 -0600342 // Read the default mode
343 SysPwrMode currentMode;
344 if (!getDefaultMode(currentMode))
345 {
346 // Unable to read defaults
347 return false;
348 }
349 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600350 std::format("PowerMode::initPersistentData: Using default mode: {}",
Chris Cain1be43372021-12-09 19:29:37 -0600351 currentMode)
Chris Cain36f9cde2021-11-22 11:18:21 -0600352 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600353
354 // Save default mode as current mode
355 persistedData.updateMode(currentMode, 0);
356
Chris Cain1fe436d2024-10-10 09:41:03 -0500357 // Write default mode to DBus and create IPS object if allowed
Chris Cain1be43372021-12-09 19:29:37 -0600358 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600359 }
360
Chris Cain1be43372021-12-09 19:29:37 -0600361 if (!persistedData.ipsAvailable())
362 {
Chris Caincde7bea2022-01-28 15:54:24 -0600363 // Read the default IPS parameters, write persistent file and update
364 // DBus
365 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600366 }
367 return true;
368}
369
370// Get the requested power mode and return true if successful
371bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
372{
373 currentMode = SysPwrMode::NO_CHANGE;
374 oemModeData = 0;
375
376 if (!persistedData.getMode(currentMode, oemModeData))
377 {
378 // Persistent data not initialized, read defaults and update DBus
379 if (!initPersistentData())
380 {
381 // Unable to read defaults from entity manager yet
382 return false;
383 }
384 return persistedData.getMode(currentMode, oemModeData);
385 }
386
387 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600388}
389
Chris Cain1fe436d2024-10-10 09:41:03 -0500390// Set the power mode on DBus and create IPS object if allowed/needed
Chris Cain36f9cde2021-11-22 11:18:21 -0600391bool PowerMode::updateDbusMode(const SysPwrMode newMode)
392{
Chris Cain30040d92024-06-19 16:50:34 -0500393 if (!isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600394 {
395 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600396 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600397 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
398 newMode)
399 .c_str());
400 return false;
401 }
402
Chris Cain30040d92024-06-19 16:50:34 -0500403 ModeInterface::PowerMode dBusMode = Mode::PowerMode::OEM;
404 if (customerModeList.contains(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600405 {
Chris Cain30040d92024-06-19 16:50:34 -0500406 // Convert mode for DBus
407 switch (newMode)
408 {
409 case SysPwrMode::STATIC:
410 dBusMode = Mode::PowerMode::Static;
411 break;
412 case SysPwrMode::POWER_SAVING:
413 dBusMode = Mode::PowerMode::PowerSaving;
414 break;
415 case SysPwrMode::MAX_PERF:
416 dBusMode = Mode::PowerMode::MaximumPerformance;
417 break;
418 case SysPwrMode::EFF_FAVOR_POWER:
419 dBusMode = Mode::PowerMode::EfficiencyFavorPower;
420 break;
421 case SysPwrMode::EFF_FAVOR_PERF:
422 dBusMode = Mode::PowerMode::EfficiencyFavorPerformance;
423 break;
424 case SysPwrMode::BALANCED_PERF:
425 dBusMode = Mode::PowerMode::BalancedPerformance;
426 break;
427 default:
428 break;
429 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600430 }
Chris Cain30040d92024-06-19 16:50:34 -0500431 // else return OEM mode
Chris Cain36f9cde2021-11-22 11:18:21 -0600432
Chris Cain1fe436d2024-10-10 09:41:03 -0500433 // Determine if IPS is allowed and create/remove as needed
434 if (IS_ECO_MODE(newMode))
435 {
436 removeIpsObject();
437 }
438 else
439 {
440 createIpsObject();
441 }
442
Chris Cain30040d92024-06-19 16:50:34 -0500443 ModeInterface::powerMode(dBusMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600444
445 return true;
446}
447
448// Send mode change request to the master OCC
449CmdStatus PowerMode::sendModeChange()
450{
Chris Cain6fa848a2022-01-24 14:54:38 -0600451 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600452
Chris Cain1be43372021-12-09 19:29:37 -0600453 SysPwrMode newMode;
454 uint16_t oemModeData = 0;
455 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600456
Chris Cain30040d92024-06-19 16:50:34 -0500457 if (isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600458 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500459 if (IS_ECO_MODE(newMode))
460 {
461 // Customer no longer able to enable IPS
462 removeIpsObject();
463 }
464 else
465 {
466 // Customer now able to enable IPS
467 if (!ipsObject)
468 {
469 createIpsObject();
470 }
471 }
472
473 if (!masterActive || !masterOccSet)
474 {
475 // Nothing to do until OCC goes active
476 log<level::DEBUG>(
477 "PowerMode::sendModeChange: OCC master not active");
478 return CmdStatus::SUCCESS;
479 }
480
481 if (!isPowerVM())
482 {
483 // Mode change is only supported on PowerVM systems
484 log<level::DEBUG>(
485 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
486 return CmdStatus::SUCCESS;
487 }
488
Chris Cain36f9cde2021-11-22 11:18:21 -0600489 std::vector<std::uint8_t> cmd, rsp;
490 cmd.reserve(9);
491 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
492 cmd.push_back(0x00); // Data Length (2 bytes)
493 cmd.push_back(0x06);
494 cmd.push_back(0x30); // Data (Version)
495 cmd.push_back(uint8_t(OccState::NO_CHANGE));
496 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600497 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
498 cmd.push_back(oemModeData & 0xFF); //
499 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600500 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600501 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600502 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600503 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600504 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600505 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600506 if (status == CmdStatus::SUCCESS)
507 {
508 if (rsp.size() == 5)
509 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500510 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
511 {
512 if (needToSendIpsData)
513 {
514 // Successful mode change and IPS is now allowed, so
515 // send IPS config
516 sendIpsData();
517 }
518 }
519 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600520 {
521 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600522 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600523 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
524 rsp[2])
525 .c_str());
526 dump_hex(rsp);
527 status = CmdStatus::FAILURE;
528 }
529 }
530 else
531 {
532 log<level::ERR>(
533 "PowerMode::sendModeChange: INVALID SET MODE response");
534 dump_hex(rsp);
535 status = CmdStatus::FAILURE;
536 }
537 }
538 else
539 {
Chris Cainc567dc82022-04-01 15:09:17 -0500540 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600541 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500542 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
543 status)
544 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600545 }
546 }
547 else
548 {
549 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600550 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600551 "PowerMode::sendModeChange: Unable to set power mode to {}",
552 newMode)
553 .c_str());
554 status = CmdStatus::FAILURE;
555 }
556
557 return status;
558}
559
Chris Cain1fe436d2024-10-10 09:41:03 -0500560// Handle IPS changed event (from GUI/Redfish)
Patrick Williamsaf408082022-07-22 19:26:54 -0500561void PowerMode::ipsChanged(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600562{
Chris Cain1d51da22021-09-21 14:13:41 -0500563 bool parmsChanged = false;
564 std::string interface;
565 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
566 ipsProperties{};
567 msg.read(interface, ipsProperties);
568
Chris Cain1be43372021-12-09 19:29:37 -0600569 // Read persisted values
570 bool ipsEnabled;
571 uint8_t enterUtil, exitUtil;
572 uint16_t enterTime, exitTime;
573 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
574
Chris Cain1fe436d2024-10-10 09:41:03 -0500575 if (!ipsObject)
576 {
577 log<level::WARNING>(
578 "ipsChanged: Idle Power Saver can not be modified in an ECO power mode");
579 return;
580 }
581
Chris Cain1be43372021-12-09 19:29:37 -0600582 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500583 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
584 if (ipsEntry != ipsProperties.end())
585 {
Chris Cain1be43372021-12-09 19:29:37 -0600586 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500587 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600588 std::format("Idle Power Saver change: Enabled={}", ipsEnabled)
Chris Cain1d51da22021-09-21 14:13:41 -0500589 .c_str());
590 parmsChanged = true;
591 }
592 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
593 if (ipsEntry != ipsProperties.end())
594 {
Chris Cain1be43372021-12-09 19:29:37 -0600595 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500596 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600597 std::format("Idle Power Saver change: Enter Util={}%", enterUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500598 .c_str());
599 parmsChanged = true;
600 }
601 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
602 if (ipsEntry != ipsProperties.end())
603 {
604 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600605 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500606 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
607 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600608 std::format("Idle Power Saver change: Enter Time={}sec", enterTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500609 .c_str());
610 parmsChanged = true;
611 }
612 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
613 if (ipsEntry != ipsProperties.end())
614 {
Chris Cain1be43372021-12-09 19:29:37 -0600615 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500616 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600617 std::format("Idle Power Saver change: Exit Util={}%", exitUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500618 .c_str());
619 parmsChanged = true;
620 }
621 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
622 if (ipsEntry != ipsProperties.end())
623 {
624 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600625 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500626 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600627 std::format("Idle Power Saver change: Exit Time={}sec", exitTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500628 .c_str());
629 parmsChanged = true;
630 }
631
632 if (parmsChanged)
633 {
Chris Caincde7bea2022-01-28 15:54:24 -0600634 if (exitUtil == 0)
635 {
636 // Setting the exitUtil to 0 will force restoring the default IPS
637 // parmeters (0 is not valid exit utilization)
638 log<level::INFO>(
639 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
640 // Read the default IPS parameters, write persistent file and update
641 // DBus
642 useDefaultIPSParms();
643 }
644 else
645 {
646 // Update persistant data with new DBus values
647 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
648 exitTime);
649 }
Chris Cain1be43372021-12-09 19:29:37 -0600650
651 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600652 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500653 }
654
655 return;
656}
657
Chris Cain1be43372021-12-09 19:29:37 -0600658/** @brief Get the Idle Power Saver properties from persisted data
659 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600660 */
Chris Cain1be43372021-12-09 19:29:37 -0600661bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
662 uint16_t& enterTime, uint8_t& exitUtil,
663 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600664{
Chris Cain36f9cde2021-11-22 11:18:21 -0600665 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600666 ipsEnabled = true; // Enabled
667 enterUtil = 8; // Enter Utilization (8%)
668 enterTime = 240; // Enter Delay Time (240s)
669 exitUtil = 12; // Exit Utilization (12%)
670 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600671
Chris Cain1be43372021-12-09 19:29:37 -0600672 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
673 exitTime))
674 {
675 // Persistent data not initialized, read defaults and update DBus
676 if (!initPersistentData())
677 {
678 // Unable to read defaults from entity manager yet
679 return false;
680 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600681
Chris Cain1be43372021-12-09 19:29:37 -0600682 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
683 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600684 }
685
686 if (enterUtil > exitUtil)
687 {
688 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600689 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600690 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
691 enterUtil, exitUtil)
692 .c_str());
693 enterUtil = exitUtil;
694 }
695
Chris Cain1be43372021-12-09 19:29:37 -0600696 return true;
697}
698
699// Set the Idle Power Saver data on DBus
700bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
701 const uint16_t enterTime, const uint8_t exitUtil,
702 const uint16_t exitTime)
703{
Chris Cain1fe436d2024-10-10 09:41:03 -0500704 if (ipsObject)
705 {
706 // true = skip update signal
707 ipsObject->setPropertyByName(IPS_ENABLED_PROP, enabled, true);
708 ipsObject->setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
709 // Convert time from seconds to ms
710 uint64_t msTime = enterTime * 1000;
711 ipsObject->setPropertyByName(IPS_ENTER_TIME, msTime, true);
712 ipsObject->setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
713 msTime = exitTime * 1000;
714 ipsObject->setPropertyByName(IPS_EXIT_TIME, msTime, true);
715 }
716 else
717 {
718 log<level::WARNING>("updateDbusIPS: No IPS object was found");
719 }
Chris Cain1be43372021-12-09 19:29:37 -0600720
721 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600722}
723
724// Send Idle Power Saver config data to the master OCC
725CmdStatus PowerMode::sendIpsData()
726{
Chris Cain6fa848a2022-01-24 14:54:38 -0600727 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600728 {
729 // Nothing to do
730 return CmdStatus::SUCCESS;
731 }
732
733 if (!isPowerVM())
734 {
735 // Idle Power Saver data is only supported on PowerVM systems
736 log<level::DEBUG>(
737 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
738 return CmdStatus::SUCCESS;
739 }
740
Chris Cain1fe436d2024-10-10 09:41:03 -0500741 if (!ipsObject)
742 {
743 // Idle Power Saver data is not available in Eco Modes
744 log<level::INFO>(
745 "PowerMode::sendIpsData: Skipping IPS data due to being in an ECO Power Mode");
746 return CmdStatus::SUCCESS;
747 }
748
Chris Cain1be43372021-12-09 19:29:37 -0600749 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600750 uint8_t enterUtil, exitUtil;
751 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600752 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600753
754 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600755 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600756 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
757 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
758 .c_str());
759
760 std::vector<std::uint8_t> cmd, rsp;
761 cmd.reserve(12);
762 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
763 cmd.push_back(0x00); // Data Length (2 bytes)
764 cmd.push_back(0x09); //
765 cmd.push_back(0x11); // Config Format: IPS Settings
766 cmd.push_back(0x00); // Version
767 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
768 cmd.push_back(enterTime >> 8); // Enter Delay Time
769 cmd.push_back(enterTime & 0xFF); //
770 cmd.push_back(enterUtil); // Enter Utilization
771 cmd.push_back(exitTime >> 8); // Exit Delay Time
772 cmd.push_back(exitTime & 0xFF); //
773 cmd.push_back(exitUtil); // Exit Utilization
Patrick Williams48002492024-02-13 21:43:32 -0600774 log<level::INFO>(std::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
Chris Cain36f9cde2021-11-22 11:18:21 -0600775 "command to OCC{} ({} bytes)",
776 occInstance, cmd.size())
777 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600778 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600779 if (status == CmdStatus::SUCCESS)
780 {
781 if (rsp.size() == 5)
782 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500783 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
784 {
785 needToSendIpsData = false;
786 }
787 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600788 {
789 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600790 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600791 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
792 rsp[2])
793 .c_str());
794 dump_hex(rsp);
795 status = CmdStatus::FAILURE;
796 }
797 }
798 else
799 {
800 log<level::ERR>(
801 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
802 dump_hex(rsp);
803 status = CmdStatus::FAILURE;
804 }
805 }
806 else
807 {
Chris Cainc567dc82022-04-01 15:09:17 -0500808 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600809 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500810 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
811 status)
812 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600813 }
814
815 return status;
816}
817
Chris Caincde7bea2022-01-28 15:54:24 -0600818// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600819void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600820{
Chris Cain1be43372021-12-09 19:29:37 -0600821 if (modeData.modeInitialized)
822 {
823 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600824 std::format(
Ben Tyner3576d652022-05-22 18:05:53 -0500825 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})",
826 modeData.mode, modeData.oemModeData, modeData.oemModeData,
827 modeData.modeLocked)
Chris Cain1be43372021-12-09 19:29:37 -0600828 .c_str());
829 }
830 if (modeData.ipsInitialized)
831 {
832 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600833 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600834 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
835 modeData.ipsEnabled, modeData.ipsEnterUtil,
836 modeData.ipsEnterTime, modeData.ipsExitUtil,
837 modeData.ipsExitTime)
838 .c_str());
839 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600840}
841
842// Saves the OEM mode data in the filesystem using cereal.
843void OccPersistData::save()
844{
845 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600846 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600847
848 if (!std::filesystem::exists(opath.parent_path()))
849 {
850 std::filesystem::create_directory(opath.parent_path());
851 }
852
853 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600854 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600855 "OccPersistData::save: Writing Power Mode persisted data to {}",
856 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600857 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600858 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600859
860 std::ofstream stream{opath.c_str()};
861 cereal::JSONOutputArchive oarchive{stream};
862
Chris Cain1be43372021-12-09 19:29:37 -0600863 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600864}
865
866// Loads the OEM mode data in the filesystem using cereal.
867void OccPersistData::load()
868{
Chris Cain36f9cde2021-11-22 11:18:21 -0600869 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600870 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600871
872 if (!std::filesystem::exists(ipath))
873 {
Chris Cain1be43372021-12-09 19:29:37 -0600874 modeData.modeInitialized = false;
875 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600876 return;
877 }
878
879 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600880 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600881 "OccPersistData::load: Reading Power Mode persisted data from {}",
882 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600883 .c_str());
884 try
885 {
886 std::ifstream stream{ipath.c_str()};
887 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600888 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600889 }
890 catch (const std::exception& e)
891 {
892 auto error = errno;
893 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600894 std::format("OccPersistData::load: failed to read {}, errno={}",
Chris Cain36f9cde2021-11-22 11:18:21 -0600895 ipath.c_str(), error)
896 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600897 modeData.modeInitialized = false;
898 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600899 }
900
Chris Caincde7bea2022-01-28 15:54:24 -0600901 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600902}
903
Chris Cain1be43372021-12-09 19:29:37 -0600904// Called when PowerModeProperties defaults are available on DBus
Patrick Williamsaf408082022-07-22 19:26:54 -0500905void PowerMode::defaultsReady(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600906{
Chris Cain1be43372021-12-09 19:29:37 -0600907 std::map<std::string, std::variant<std::string>> properties{};
908 std::string interface;
909 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600910
Chris Cain30040d92024-06-19 16:50:34 -0500911 if (persistedData.modeAvailable())
912 {
913 // Validate persisted mode is supported
914 SysPwrMode pMode = SysPwrMode::NO_CHANGE;
915 uint16_t oemModeData = 0;
916 persistedData.getMode(pMode, oemModeData);
917 if (!isValidMode(pMode))
918 {
919 log<level::ERR>(
920 std::format(
921 "defaultsReady: Persisted power mode ({}/{}) is not valid. Reading system default mode",
922 pMode, oemModeData)
923 .c_str());
924 persistedData.invalidateMode();
925 }
926 }
927
Chris Cain1be43372021-12-09 19:29:37 -0600928 // If persistent data exists, then don't need to read defaults
929 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600930 {
Chris Cain1be43372021-12-09 19:29:37 -0600931 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600932 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600933 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
934 persistedData.modeAvailable() ? 'y' : 'n',
Chris Cain30040d92024-06-19 16:50:34 -0500935 persistedData.ipsAvailable() ? 'y' : 'n')
Chris Cain1be43372021-12-09 19:29:37 -0600936 .c_str());
937
938 // Read default power mode defaults and update DBus
939 initPersistentData();
940 }
941}
942
943// Get the default power mode from DBus and return true if success
944bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
945{
946 try
947 {
948 auto& bus = utils::getBus();
949 std::string path = "/";
950 std::string service =
951 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -0400952 auto method =
953 bus.new_method_call(service.c_str(), path.c_str(),
954 "org.freedesktop.DBus.Properties", "Get");
Chris Cain1be43372021-12-09 19:29:37 -0600955 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
956 auto reply = bus.call(method);
957
958 std::variant<std::string> stateEntryValue;
959 reply.read(stateEntryValue);
960 auto propVal = std::get<std::string>(stateEntryValue);
961
Patrick Williamsd7542c82024-08-16 15:20:28 -0400962 const std::string fullModeString =
963 PMODE_INTERFACE + ".PowerMode."s + propVal;
Chris Cain1be43372021-12-09 19:29:37 -0600964 defaultMode = powermode::convertStringToMode(fullModeString);
965 if (!VALID_POWER_MODE_SETTING(defaultMode))
966 {
Chris Cain30040d92024-06-19 16:50:34 -0500967 log<level::ERR>(std::format("PowerMode::getDefaultMode: Invalid "
968 "default power mode found: {}",
969 defaultMode)
970 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600971 // If default was read but not valid, use Max Performance
972 defaultMode = SysPwrMode::MAX_PERF;
973 return true;
974 }
975 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500976 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600977 {
978 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600979 std::format("Unable to read Default Power Mode: {}", e.what())
Chris Cain1be43372021-12-09 19:29:37 -0600980 .c_str());
981 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600982 }
983
Chris Cain1be43372021-12-09 19:29:37 -0600984 return true;
985}
Chris Cain36f9cde2021-11-22 11:18:21 -0600986
Chris Cain1be43372021-12-09 19:29:37 -0600987/* Get the default Idle Power Saver properties and return true if successful */
988bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
989 uint16_t& enterTime, uint8_t& exitUtil,
990 uint16_t& exitTime)
991{
992 // Defaults:
993 ipsEnabled = true; // Enabled
994 enterUtil = 8; // Enter Utilization (8%)
995 enterTime = 240; // Enter Delay Time (240s)
996 exitUtil = 12; // Exit Utilization (12%)
997 exitTime = 10; // Exit Delay Time (10s)
998
999 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
1000 ipsProperties{};
1001
1002 // Get all IPS properties from DBus
1003 try
1004 {
1005 auto& bus = utils::getBus();
1006 std::string path = "/";
1007 std::string service =
1008 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -04001009 auto method =
1010 bus.new_method_call(service.c_str(), path.c_str(),
1011 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain1be43372021-12-09 19:29:37 -06001012 method.append(PMODE_DEFAULT_INTERFACE);
1013 auto reply = bus.call(method);
1014 reply.read(ipsProperties);
1015 }
Patrick Williamsaf408082022-07-22 19:26:54 -05001016 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -06001017 {
1018 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -06001019 std::format(
Chris Cain1be43372021-12-09 19:29:37 -06001020 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
1021 e.what())
1022 .c_str());
1023 return false;
1024 }
1025
1026 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
1027 if (ipsEntry != ipsProperties.end())
1028 {
1029 ipsEnabled = std::get<bool>(ipsEntry->second);
1030 }
1031 else
1032 {
1033 log<level::ERR>(
1034 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
1035 }
1036
1037 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
1038 if (ipsEntry != ipsProperties.end())
1039 {
1040 enterUtil = std::get<uint64_t>(ipsEntry->second);
1041 }
1042 else
1043 {
1044 log<level::ERR>(
1045 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
1046 }
1047
1048 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
1049 if (ipsEntry != ipsProperties.end())
1050 {
1051 enterTime = std::get<uint64_t>(ipsEntry->second);
1052 }
1053 else
1054 {
1055 log<level::ERR>(
1056 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
1057 }
1058
1059 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
1060 if (ipsEntry != ipsProperties.end())
1061 {
1062 exitUtil = std::get<uint64_t>(ipsEntry->second);
1063 }
1064 else
1065 {
1066 log<level::ERR>(
1067 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
1068 }
1069
1070 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
1071 if (ipsEntry != ipsProperties.end())
1072 {
1073 exitTime = std::get<uint64_t>(ipsEntry->second);
1074 }
1075 else
1076 {
1077 log<level::ERR>(
1078 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
1079 }
1080
1081 if (enterUtil > exitUtil)
1082 {
1083 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -06001084 std::format(
Chris Cain1be43372021-12-09 19:29:37 -06001085 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
1086 enterUtil, exitUtil)
1087 .c_str());
1088 enterUtil = exitUtil;
1089 }
1090
1091 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -06001092}
1093
Chris Caincde7bea2022-01-28 15:54:24 -06001094/* Read default IPS parameters, save them to the persistent file and update
1095 DBus. Return true if successful */
1096bool PowerMode::useDefaultIPSParms()
1097{
1098 // Read the default IPS parameters
1099 bool ipsEnabled;
1100 uint8_t enterUtil, exitUtil;
1101 uint16_t enterTime, exitTime;
1102 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
1103 exitTime))
1104 {
1105 // Unable to read defaults
1106 return false;
1107 }
1108 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -06001109 std::format(
Chris Caincde7bea2022-01-28 15:54:24 -06001110 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
1111 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
1112 .c_str());
1113
1114 // Save IPS parms to the persistent file
1115 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
1116 exitTime);
1117
1118 // Write IPS parms to DBus
1119 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
1120}
1121
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001122// Starts to watch for IPS active state changes.
1123bool PowerMode::openIpsFile()
1124{
1125 bool rc = true;
1126 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
1127 const int open_errno = errno;
1128 if (fd < 0)
1129 {
Patrick Williamsd7542c82024-08-16 15:20:28 -04001130 log<level::ERR>(
1131 std::format("openIpsFile Error({})={} : File={}", open_errno,
1132 strerror(open_errno), ipsStatusFile.c_str())
1133 .c_str());
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001134
1135 close(fd);
1136
1137 using namespace sdbusplus::org::open_power::OCC::Device::Error;
1138 report<OpenFailure>(
1139 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1140 CALLOUT_ERRNO(open_errno),
1141 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1142 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1143
1144 // We are no longer watching the error
Chris Cain1fe436d2024-10-10 09:41:03 -05001145 if (ipsObject)
1146 {
1147 ipsObject->active(false);
1148 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001149
1150 watching = false;
1151 rc = false;
1152 // NOTE: this will leave the system not reporting IPS active state to
1153 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1154 }
1155 return rc;
1156}
1157
1158// Starts to watch for IPS active state changes.
1159void PowerMode::addIpsWatch(bool poll)
1160{
1161 // open file and register callback on file if we are not currently watching,
1162 // and if poll=true, and if we are the master.
1163 if ((!watching) && poll)
1164 {
1165 // Open the file
1166 if (openIpsFile())
1167 {
1168 // register the callback handler which sets 'watching'
1169 registerIpsStatusCallBack();
1170 }
1171 }
1172}
1173
1174// Stops watching for IPS active state changes.
1175void PowerMode::removeIpsWatch()
1176{
1177 // NOTE: we want to remove event, close file, and IPS active false no
1178 // matter what the 'watching' flags is set to.
1179
1180 // We are no longer watching the error
Chris Cain1fe436d2024-10-10 09:41:03 -05001181 if (ipsObject)
1182 {
1183 ipsObject->active(false);
1184 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001185
1186 watching = false;
1187
1188 // Close file
1189 close(fd);
1190
1191 // clears sourcePtr in the event source.
1192 eventSource.reset();
1193}
1194
1195// Attaches the FD to event loop and registers the callback handler
1196void PowerMode::registerIpsStatusCallBack()
1197{
1198 decltype(eventSource.get()) sourcePtr = nullptr;
1199
1200 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1201 ipsStatusCallBack, this);
1202 if (r < 0)
1203 {
Patrick Williams48002492024-02-13 21:43:32 -06001204 log<level::ERR>(std::format("sd_event_add_io: Error({})={} : File={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001205 r, strerror(-r), ipsStatusFile.c_str())
1206 .c_str());
1207
1208 using InternalFailure =
1209 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1210 report<InternalFailure>();
1211
1212 removeIpsWatch();
1213 // NOTE: this will leave the system not reporting IPS active state to
1214 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1215 }
1216 else
1217 {
1218 // puts sourcePtr in the event source.
1219 eventSource.reset(sourcePtr);
1220 // Set we are watching the error
1221 watching = true;
1222 }
1223}
1224
1225// Static function to redirect to non static analyze event function to be
1226// able to read file and push onto dBus.
1227int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1228 uint32_t /*revents*/, void* userData)
1229{
1230 auto pmode = static_cast<PowerMode*>(userData);
1231 pmode->analyzeIpsEvent();
1232 return 0;
1233}
1234
1235// Function to Read SysFs file change on IPS state and push on dBus.
1236void PowerMode::analyzeIpsEvent()
1237{
1238 // Need to seek to START, else the poll returns immediately telling
1239 // there is data to be read. if not done this floods the system.
1240 auto r = lseek(fd, 0, SEEK_SET);
1241 const int open_errno = errno;
1242 if (r < 0)
1243 {
1244 // NOTE: upon file access error we can not just re-open file, we have to
1245 // remove and add to watch.
1246 removeIpsWatch();
1247 addIpsWatch(true);
1248 }
1249
1250 // if we are 'watching' that is the file seek, or the re-open passed.. we
1251 // can read the data
1252 if (watching)
1253 {
1254 // This file gets created when polling OCCs. A value or length of 0 is
1255 // deemed success. That means we would disable IPS active on dbus.
1256 char data;
1257 bool ipsState = false;
1258 const auto len = read(fd, &data, sizeof(data));
1259 const int readErrno = errno;
1260 if (len <= 0)
1261 {
1262 removeIpsWatch();
1263
1264 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -06001265 std::format("IPS state Read Error({})={} : File={} : len={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001266 readErrno, strerror(readErrno),
1267 ipsStatusFile.c_str(), len)
1268 .c_str());
1269
1270 report<ReadFailure>(
1271 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1272 CALLOUT_ERRNO(readErrno),
1273 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1274 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1275
1276 // NOTE: this will leave the system not reporting IPS active state
1277 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1278 // again.
1279 }
1280 else
1281 {
1282 // Data returned in ASCII.
1283 // convert to integer. atoi()
1284 // from OCC_P10_FW_Interfaces spec
1285 // Bit 6: IPS active 1 indicates enabled.
1286 // Bit 7: IPS enabled. 1 indicates enabled.
1287 // mask off bit 6 --> & 0x02
1288 // Shift left one bit and store as bool. >> 1
1289 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1290 }
1291
1292 // This will only set IPS active dbus if different than current.
Chris Cain1fe436d2024-10-10 09:41:03 -05001293 if (ipsObject)
1294 {
1295 ipsObject->active(ipsState);
1296 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001297 }
1298 else
1299 {
1300 removeIpsWatch();
1301
1302 // If the Retry did not get to "watching = true" we already have an
1303 // error log, just post trace.
Patrick Williamsd7542c82024-08-16 15:20:28 -04001304 log<level::ERR>(
1305 std::format("Retry on File seek Error({})={} : File={}", open_errno,
1306 strerror(open_errno), ipsStatusFile.c_str())
1307 .c_str());
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001308
1309 // NOTE: this will leave the system not reporting IPS active state to
1310 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1311 }
1312
1313 return;
1314}
Ben Tyner3576d652022-05-22 18:05:53 -05001315
1316// overrides read/write to powerMode dbus property.
Chris Cain30040d92024-06-19 16:50:34 -05001317Mode::PowerMode PowerMode::powerMode(Mode::PowerMode requestedMode)
Ben Tyner3576d652022-05-22 18:05:53 -05001318{
1319 if (persistedData.getModeLock())
1320 {
1321 log<level::INFO>("PowerMode::powerMode: mode property change blocked");
1322 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1323 "mode change not allowed due to lock"));
Ben Tyner3576d652022-05-22 18:05:53 -05001324 }
1325 else
1326 {
Chris Cain30040d92024-06-19 16:50:34 -05001327 // Verify requested mode is allowed
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001328
Chris Cain30040d92024-06-19 16:50:34 -05001329 // Convert PowerMode to internal SysPwrMode
1330 SysPwrMode newMode = getInternalMode(requestedMode);
1331 if (newMode != SysPwrMode::NO_CHANGE)
1332 {
1333 // Validate it is an allowed customer mode
1334 if (customerModeList.contains(newMode))
1335 {
1336 // Update persisted data with new mode
1337 persistedData.updateMode(newMode, 0);
1338
1339 log<level::INFO>(
1340 std::format("DBus PowerMode Changed: {}",
1341 convertPowerModeToString(requestedMode))
1342 .c_str());
1343
1344 // Send mode change to OCC
1345 sendModeChange();
1346 }
1347 else
1348 {
1349 // Not Allowed
1350 log<level::ERR>(
1351 std::format(
1352 "PowerMode change not allowed. {} is not in AllowedPowerModes",
1353 convertPowerModeToString(requestedMode))
1354 .c_str());
1355 elog<NotAllowed>(
1356 xyz::openbmc_project::Common::NotAllowed::REASON(
1357 "PowerMode value not allowed"));
1358 }
1359 }
1360 else
1361 {
1362 // Value is not valid
1363 using InvalidArgument =
1364 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
1365 using Argument = xyz::openbmc_project::Common::InvalidArgument;
1366 log<level::ERR>(
1367 std::format(
1368 "PowerMode not valid. {} is not in AllowedPowerModes",
1369 convertPowerModeToString(requestedMode))
1370 .c_str());
1371 elog<InvalidArgument>(Argument::ARGUMENT_NAME("PowerMode"),
1372 Argument::ARGUMENT_VALUE("INVALID MODE"));
1373 }
1374 }
1375
1376 // All elog<> calls will cause trap (so code will not make it here)
1377
1378 return Mode::powerMode(requestedMode);
1379}
1380
1381/* Set dbus property to SAFE mode(true) or clear(false) only if different
1382 */
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001383void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1384{
1385 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -06001386 std::format("PowerMode:updateDbusSafeMode: Update dbus state ({})",
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001387 safeModeReq)
1388 .c_str());
1389
1390 // Note; this function checks and only updates if different.
1391 Mode::safeMode(safeModeReq);
1392}
1393
Chris Cain30040d92024-06-19 16:50:34 -05001394// Get the supported power modes from DBus and return true if success
1395bool PowerMode::getSupportedModes()
1396{
1397 bool foundCustomerMode = false;
1398 using ModePropertyVariants =
1399 std::variant<bool, uint8_t, uint16_t, std::vector<std::string>>;
1400 std::map<std::string, ModePropertyVariants> powerModeProperties{};
1401
1402 // Get all power mode properties from DBus
1403 try
1404 {
1405 auto& bus = utils::getBus();
1406 std::string path = "/";
1407 std::string service =
1408 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -04001409 auto method =
1410 bus.new_method_call(service.c_str(), path.c_str(),
1411 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain30040d92024-06-19 16:50:34 -05001412 method.append(PMODE_DEFAULT_INTERFACE);
1413 auto reply = bus.call(method);
1414 reply.read(powerModeProperties);
1415 }
1416 catch (const sdbusplus::exception_t& e)
1417 {
1418 log<level::ERR>(
1419 std::format("Unable to read PowerModeProperties: {}", e.what())
1420 .c_str());
1421 return false;
1422 }
1423
1424 // Determine if system suports EcoModes
1425 auto ecoSupport = powerModeProperties.find("EcoModeSupport");
1426 if (ecoSupport != powerModeProperties.end())
1427 {
1428 ecoModeSupport = std::get<bool>(ecoSupport->second);
1429 log<level::INFO>(std::format("getSupportedModes(): ecoModeSupport: {}",
1430 ecoModeSupport)
1431 .c_str());
1432 }
1433
1434 // Determine what customer modes are supported
1435 using PMode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
1436 std::set<PMode::PowerMode> modesToAllow;
1437 auto custList = powerModeProperties.find("CustomerModes");
1438 if (custList != powerModeProperties.end())
1439 {
1440 auto modeList = std::get<std::vector<std::string>>(custList->second);
1441 for (auto mode : modeList)
1442 {
1443 // Ensure mode is valid
Patrick Williamsd7542c82024-08-16 15:20:28 -04001444 const std::string fullModeString =
1445 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain30040d92024-06-19 16:50:34 -05001446 log<level::INFO>(
1447 std::format("getSupportedModes(): {}", mode).c_str());
1448 SysPwrMode modeValue =
1449 powermode::convertStringToMode(fullModeString);
1450 if (VALID_POWER_MODE_SETTING(modeValue))
1451 {
1452 if (!foundCustomerMode)
1453 {
1454 // Start with empty list
1455 customerModeList.clear();
1456 foundCustomerMode = true;
1457 }
1458 // Add mode to list
1459 std::optional<PMode::PowerMode> cMode =
1460 PMode::convertStringToPowerMode(fullModeString);
1461 if (cMode)
1462 modesToAllow.insert(cMode.value());
1463 customerModeList.insert(modeValue);
1464 }
1465 else
1466 {
1467 log<level::ERR>(
1468 std::format(
1469 "getSupportedModes(): Ignoring unsupported customer mode {}",
1470 mode)
1471 .c_str());
1472 }
1473 }
1474 }
1475 if (foundCustomerMode)
1476 {
1477 ModeInterface::allowedPowerModes(modesToAllow);
1478 }
1479
1480 // Determine what OEM modes are supported
1481 auto oemList = powerModeProperties.find("OemModes");
1482 if (oemList != powerModeProperties.end())
1483 {
1484 bool foundValidMode = false;
1485 auto OmodeList = std::get<std::vector<std::string>>(oemList->second);
1486 for (auto mode : OmodeList)
1487 {
1488 // Ensure mode is valid
Patrick Williamsd7542c82024-08-16 15:20:28 -04001489 const std::string fullModeString =
1490 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain30040d92024-06-19 16:50:34 -05001491 SysPwrMode modeValue =
1492 powermode::convertStringToMode(fullModeString);
1493 if (VALID_POWER_MODE_SETTING(modeValue) ||
1494 VALID_OEM_POWER_MODE_SETTING(modeValue))
1495 {
1496 if (!foundValidMode)
1497 {
1498 // Start with empty list
1499 oemModeList.clear();
1500 foundValidMode = true;
1501 }
1502 // Add mode to list
1503 oemModeList.insert(modeValue);
1504 }
1505 else
1506 {
1507 log<level::ERR>(
1508 std::format(
1509 "getSupportedModes(): Ignoring unsupported OEM mode {}",
1510 mode)
1511 .c_str());
1512 }
1513 }
1514 }
1515
1516 return foundCustomerMode;
1517}
1518
1519bool PowerMode::isValidMode(const SysPwrMode mode)
1520{
1521 if (customerModeList.contains(mode) || oemModeList.contains(mode))
1522 {
1523 return true;
1524 }
1525 return false;
1526}
1527
Chris Cain78e86012021-03-04 16:15:31 -06001528} // namespace powermode
1529
1530} // namespace occ
1531
1532} // namespace open_power