blob: 776edc4694fda4ed6cf494c09edc1ba16f3b9859 [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))
47
48// Constructor
49PowerMode::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),
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
Patrick Williamsd7542c82024-08-16 15:20:28 -040074 ModeInterface::allowedPowerModes(
75 {Mode::PowerMode::Static, Mode::PowerMode::MaximumPerformance,
76 Mode::PowerMode::PowerSaving});
Chris Cain30040d92024-06-19 16:50:34 -050077 }
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};
Ben Tyner3576d652022-05-22 18:05:53 -0500113
Chris Cain6fa848a2022-01-24 14:54:38 -0600114// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -0600115void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -0600116{
Chris Cain1be43372021-12-09 19:29:37 -0600117 if (masterOccSet)
118 {
119 if (masterOccPath != path)
120 {
121 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600122 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600123 "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;
Chris Cain6fa848a2022-01-24 14:54:38 -0600133 occInstance = path.back() - '0';
Patrick Williams48002492024-02-13 21:43:32 -0600134 log<level::DEBUG>(std::format("PowerMode::setMasterOcc(OCC{}, {})",
Chris Cain6fa848a2022-01-24 14:54:38 -0600135 occInstance, path.c_str())
136 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600137 if (!occCmd)
138 {
Patrick Williamsd7542c82024-08-16 15:20:28 -0400139 occCmd = std::make_unique<open_power::occ::OccCommand>(
140 occInstance, path.c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600141 }
Chris Cain6fa848a2022-01-24 14:54:38 -0600142 masterOccSet = true;
143};
144
Ben Tyner3576d652022-05-22 18:05:53 -0500145// Set the state of power mode lock. Writing persistent data via dbus method.
146bool 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.
154bool PowerMode::powerModeLockStatus()
155{
156 bool status = persistedData.getModeLock(); // read persistent data
Patrick Williams48002492024-02-13 21:43:32 -0600157 log<level::INFO>(std::format("PowerMode::powerModeLockStatus: {}",
Ben Tyner3576d652022-05-22 18:05:53 -0500158 status ? "locked" : "unlocked")
159 .c_str());
160 return status;
161}
162
Chris Cain36f9cde2021-11-22 11:18:21 -0600163// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -0600164bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -0600165{
Ben Tyner3576d652022-05-22 18:05:53 -0500166 if (persistedData.getModeLock())
167 {
168 log<level::INFO>("PowerMode::setMode: mode change blocked");
169 return false;
170 }
171
Chris Cain36f9cde2021-11-22 11:18:21 -0600172 if (updateDbusMode(newMode) == false)
173 {
174 // Unsupported mode
175 return false;
176 }
177
Chris Cain1be43372021-12-09 19:29:37 -0600178 // Save mode
179 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600180
Chris Cain1be43372021-12-09 19:29:37 -0600181 // Send mode change to OCC
182 if (sendModeChange() != CmdStatus::SUCCESS)
183 {
184 // Mode change failed
185 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600186 }
187
Chris Cain36f9cde2021-11-22 11:18:21 -0600188 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600189}
190
Chris Cain30040d92024-06-19 16:50:34 -0500191// Convert PowerMode value to occ-control internal SysPwrMode
192// Returns SysPwrMode::NO_CHANGE if mode not valid
193SysPwrMode 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
Chris Cain78e86012021-03-04 16:15:31 -0600224// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600225// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600226SysPwrMode convertStringToMode(const std::string& i_modeString)
227{
Chris Cain30040d92024-06-19 16:50:34 -0500228 SysPwrMode newMode = SysPwrMode::NO_CHANGE;
229 try
Chris Cain78e86012021-03-04 16:15:31 -0600230 {
Chris Cain30040d92024-06-19 16:50:34 -0500231 Mode::PowerMode newPMode =
232 Mode::convertPowerModeFromString(i_modeString);
233 newMode = getInternalMode(newPMode);
Chris Cain78e86012021-03-04 16:15:31 -0600234 }
Chris Cain30040d92024-06-19 16:50:34 -0500235 catch (const std::exception& e)
Chris Cain78e86012021-03-04 16:15:31 -0600236 {
Chris Cain30040d92024-06-19 16:50:34 -0500237 // 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
Chris Cain36f9cde2021-11-22 11:18:21 -0600262 {
263 log<level::ERR>(
Chris Cain30040d92024-06-19 16:50:34 -0500264 std::format("convertStringToMode: Invalid Power Mode: {} ({})",
265 shortMode, e.what())
Chris Cain36f9cde2021-11-22 11:18:21 -0600266 .c_str());
267 }
Chris Cain78e86012021-03-04 16:15:31 -0600268 }
Chris Cain30040d92024-06-19 16:50:34 -0500269 return newMode;
Chris Cain78e86012021-03-04 16:15:31 -0600270}
271
Chris Cain36f9cde2021-11-22 11:18:21 -0600272// Check if Hypervisor target is PowerVM
273bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500274{
Chris Cain52cce032023-02-09 15:38:31 -0600275 bool powerVmTarget = true;
276#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -0600277 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
Chris Cain36f9cde2021-11-22 11:18:21 -0600282 // 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>(
Patrick Williams48002492024-02-13 21:43:32 -0600300 std::format("isPowerVM returning {}", powerVmTarget).c_str());
Chris Cain52cce032023-02-09 15:38:31 -0600301#endif
Chris Cain36f9cde2021-11-22 11:18:21 -0600302
303 return powerVmTarget;
304}
305
Chris Cain1be43372021-12-09 19:29:37 -0600306// Initialize persistent data and return true if successful
307bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600308{
Chris Cain1be43372021-12-09 19:29:37 -0600309 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600310 {
Chris Cain1be43372021-12-09 19:29:37 -0600311 // 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>(
Patrick Williams48002492024-02-13 21:43:32 -0600319 std::format("PowerMode::initPersistentData: Using default mode: {}",
Chris Cain1be43372021-12-09 19:29:37 -0600320 currentMode)
Chris Cain36f9cde2021-11-22 11:18:21 -0600321 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600322
323 // Save default mode as current mode
324 persistedData.updateMode(currentMode, 0);
325
326 // Write default mode to DBus
327 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600328 }
329
Chris Cain1be43372021-12-09 19:29:37 -0600330 if (!persistedData.ipsAvailable())
331 {
Chris Caincde7bea2022-01-28 15:54:24 -0600332 // Read the default IPS parameters, write persistent file and update
333 // DBus
334 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600335 }
336 return true;
337}
338
339// Get the requested power mode and return true if successful
340bool 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;
Chris Cain36f9cde2021-11-22 11:18:21 -0600357}
358
359// Set the power mode on DBus
360bool PowerMode::updateDbusMode(const SysPwrMode newMode)
361{
Chris Cain30040d92024-06-19 16:50:34 -0500362 if (!isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600363 {
364 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600365 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600366 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
367 newMode)
368 .c_str());
369 return false;
370 }
371
Chris Cain30040d92024-06-19 16:50:34 -0500372 ModeInterface::PowerMode dBusMode = Mode::PowerMode::OEM;
373 if (customerModeList.contains(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600374 {
Chris Cain30040d92024-06-19 16:50:34 -0500375 // 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 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600399 }
Chris Cain30040d92024-06-19 16:50:34 -0500400 // else return OEM mode
Chris Cain36f9cde2021-11-22 11:18:21 -0600401
Chris Cain30040d92024-06-19 16:50:34 -0500402 ModeInterface::powerMode(dBusMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600403
404 return true;
405}
406
407// Send mode change request to the master OCC
408CmdStatus PowerMode::sendModeChange()
409{
Chris Cain6fa848a2022-01-24 14:54:38 -0600410 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600411
Chris Cain6fa848a2022-01-24 14:54:38 -0600412 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600413 {
414 // Nothing to do
Chris Cain6fa848a2022-01-24 14:54:38 -0600415 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
Chris Cain36f9cde2021-11-22 11:18:21 -0600416 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
Chris Cain1be43372021-12-09 19:29:37 -0600427 SysPwrMode newMode;
428 uint16_t oemModeData = 0;
429 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600430
Chris Cain30040d92024-06-19 16:50:34 -0500431 if (isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600432 {
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));
Chris Cain1be43372021-12-09 19:29:37 -0600441 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
442 cmd.push_back(oemModeData & 0xFF); //
443 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600444 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600445 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600446 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600447 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600448 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600449 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600450 if (status == CmdStatus::SUCCESS)
451 {
452 if (rsp.size() == 5)
453 {
454 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
455 {
456 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600457 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600458 "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 {
Chris Cainc567dc82022-04-01 15:09:17 -0500475 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600476 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500477 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
478 status)
479 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600480 }
481 }
482 else
483 {
484 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600485 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600486 "PowerMode::sendModeChange: Unable to set power mode to {}",
487 newMode)
488 .c_str());
489 status = CmdStatus::FAILURE;
490 }
491
492 return status;
493}
494
Patrick Williamsaf408082022-07-22 19:26:54 -0500495void PowerMode::ipsChanged(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600496{
Chris Cain1d51da22021-09-21 14:13:41 -0500497 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
Chris Cain1be43372021-12-09 19:29:37 -0600503 // 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
Chris Cain1d51da22021-09-21 14:13:41 -0500510 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
511 if (ipsEntry != ipsProperties.end())
512 {
Chris Cain1be43372021-12-09 19:29:37 -0600513 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500514 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600515 std::format("Idle Power Saver change: Enabled={}", ipsEnabled)
Chris Cain1d51da22021-09-21 14:13:41 -0500516 .c_str());
517 parmsChanged = true;
518 }
519 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
520 if (ipsEntry != ipsProperties.end())
521 {
Chris Cain1be43372021-12-09 19:29:37 -0600522 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500523 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600524 std::format("Idle Power Saver change: Enter Util={}%", enterUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500525 .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));
Chris Cain1be43372021-12-09 19:29:37 -0600532 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500533 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
534 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600535 std::format("Idle Power Saver change: Enter Time={}sec", enterTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500536 .c_str());
537 parmsChanged = true;
538 }
539 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
540 if (ipsEntry != ipsProperties.end())
541 {
Chris Cain1be43372021-12-09 19:29:37 -0600542 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500543 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600544 std::format("Idle Power Saver change: Exit Util={}%", exitUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500545 .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));
Chris Cain1be43372021-12-09 19:29:37 -0600552 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500553 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600554 std::format("Idle Power Saver change: Exit Time={}sec", exitTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500555 .c_str());
556 parmsChanged = true;
557 }
558
559 if (parmsChanged)
560 {
Chris Caincde7bea2022-01-28 15:54:24 -0600561 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 }
Chris Cain1be43372021-12-09 19:29:37 -0600577
578 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600579 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500580 }
581
582 return;
583}
584
Chris Cain1be43372021-12-09 19:29:37 -0600585/** @brief Get the Idle Power Saver properties from persisted data
586 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600587 */
Chris Cain1be43372021-12-09 19:29:37 -0600588bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
589 uint16_t& enterTime, uint8_t& exitUtil,
590 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600591{
Chris Cain36f9cde2021-11-22 11:18:21 -0600592 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600593 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)
Chris Cain36f9cde2021-11-22 11:18:21 -0600598
Chris Cain1be43372021-12-09 19:29:37 -0600599 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 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600608
Chris Cain1be43372021-12-09 19:29:37 -0600609 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
610 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600611 }
612
613 if (enterUtil > exitUtil)
614 {
615 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600616 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600617 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
618 enterUtil, exitUtil)
619 .c_str());
620 enterUtil = exitUtil;
621 }
622
Chris Cain1be43372021-12-09 19:29:37 -0600623 return true;
624}
625
626// Set the Idle Power Saver data on DBus
627bool 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;
Chris Cain36f9cde2021-11-22 11:18:21 -0600642}
643
644// Send Idle Power Saver config data to the master OCC
645CmdStatus PowerMode::sendIpsData()
646{
Chris Cain6fa848a2022-01-24 14:54:38 -0600647 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600648 {
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
Chris Cain1be43372021-12-09 19:29:37 -0600661 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600662 uint8_t enterUtil, exitUtil;
663 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600664 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600665
666 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600667 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600668 "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
Patrick Williams48002492024-02-13 21:43:32 -0600686 log<level::INFO>(std::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
Chris Cain36f9cde2021-11-22 11:18:21 -0600687 "command to OCC{} ({} bytes)",
688 occInstance, cmd.size())
689 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600690 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600691 if (status == CmdStatus::SUCCESS)
692 {
693 if (rsp.size() == 5)
694 {
695 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
696 {
697 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600698 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600699 "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 {
Chris Cainc567dc82022-04-01 15:09:17 -0500716 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600717 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500718 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
719 status)
720 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600721 }
722
723 return status;
724}
725
Chris Caincde7bea2022-01-28 15:54:24 -0600726// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600727void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600728{
Chris Cain1be43372021-12-09 19:29:37 -0600729 if (modeData.modeInitialized)
730 {
731 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600732 std::format(
Ben Tyner3576d652022-05-22 18:05:53 -0500733 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})",
734 modeData.mode, modeData.oemModeData, modeData.oemModeData,
735 modeData.modeLocked)
Chris Cain1be43372021-12-09 19:29:37 -0600736 .c_str());
737 }
738 if (modeData.ipsInitialized)
739 {
740 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600741 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600742 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
743 modeData.ipsEnabled, modeData.ipsEnterUtil,
744 modeData.ipsEnterTime, modeData.ipsExitUtil,
745 modeData.ipsExitTime)
746 .c_str());
747 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600748}
749
750// Saves the OEM mode data in the filesystem using cereal.
751void OccPersistData::save()
752{
753 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600754 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600755
756 if (!std::filesystem::exists(opath.parent_path()))
757 {
758 std::filesystem::create_directory(opath.parent_path());
759 }
760
761 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600762 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600763 "OccPersistData::save: Writing Power Mode persisted data to {}",
764 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600765 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600766 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600767
768 std::ofstream stream{opath.c_str()};
769 cereal::JSONOutputArchive oarchive{stream};
770
Chris Cain1be43372021-12-09 19:29:37 -0600771 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600772}
773
774// Loads the OEM mode data in the filesystem using cereal.
775void OccPersistData::load()
776{
Chris Cain36f9cde2021-11-22 11:18:21 -0600777 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600778 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600779
780 if (!std::filesystem::exists(ipath))
781 {
Chris Cain1be43372021-12-09 19:29:37 -0600782 modeData.modeInitialized = false;
783 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600784 return;
785 }
786
787 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600788 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600789 "OccPersistData::load: Reading Power Mode persisted data from {}",
790 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600791 .c_str());
792 try
793 {
794 std::ifstream stream{ipath.c_str()};
795 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600796 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600797 }
798 catch (const std::exception& e)
799 {
800 auto error = errno;
801 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600802 std::format("OccPersistData::load: failed to read {}, errno={}",
Chris Cain36f9cde2021-11-22 11:18:21 -0600803 ipath.c_str(), error)
804 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600805 modeData.modeInitialized = false;
806 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600807 }
808
Chris Caincde7bea2022-01-28 15:54:24 -0600809 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600810}
811
Chris Cain1be43372021-12-09 19:29:37 -0600812// Called when PowerModeProperties defaults are available on DBus
Patrick Williamsaf408082022-07-22 19:26:54 -0500813void PowerMode::defaultsReady(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600814{
Chris Cain1be43372021-12-09 19:29:37 -0600815 std::map<std::string, std::variant<std::string>> properties{};
816 std::string interface;
817 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600818
Chris Cain30040d92024-06-19 16:50:34 -0500819 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
Chris Cain1be43372021-12-09 19:29:37 -0600836 // If persistent data exists, then don't need to read defaults
837 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600838 {
Chris Cain1be43372021-12-09 19:29:37 -0600839 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600840 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600841 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
842 persistedData.modeAvailable() ? 'y' : 'n',
Chris Cain30040d92024-06-19 16:50:34 -0500843 persistedData.ipsAvailable() ? 'y' : 'n')
Chris Cain1be43372021-12-09 19:29:37 -0600844 .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
852bool 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);
Patrick Williamsd7542c82024-08-16 15:20:28 -0400860 auto method =
861 bus.new_method_call(service.c_str(), path.c_str(),
862 "org.freedesktop.DBus.Properties", "Get");
Chris Cain1be43372021-12-09 19:29:37 -0600863 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
Patrick Williamsd7542c82024-08-16 15:20:28 -0400870 const std::string fullModeString =
871 PMODE_INTERFACE + ".PowerMode."s + propVal;
Chris Cain1be43372021-12-09 19:29:37 -0600872 defaultMode = powermode::convertStringToMode(fullModeString);
873 if (!VALID_POWER_MODE_SETTING(defaultMode))
874 {
Chris Cain30040d92024-06-19 16:50:34 -0500875 log<level::ERR>(std::format("PowerMode::getDefaultMode: Invalid "
876 "default power mode found: {}",
877 defaultMode)
878 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600879 // If default was read but not valid, use Max Performance
880 defaultMode = SysPwrMode::MAX_PERF;
881 return true;
882 }
883 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500884 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600885 {
886 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600887 std::format("Unable to read Default Power Mode: {}", e.what())
Chris Cain1be43372021-12-09 19:29:37 -0600888 .c_str());
889 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600890 }
891
Chris Cain1be43372021-12-09 19:29:37 -0600892 return true;
893}
Chris Cain36f9cde2021-11-22 11:18:21 -0600894
Chris Cain1be43372021-12-09 19:29:37 -0600895/* Get the default Idle Power Saver properties and return true if successful */
896bool 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);
Patrick Williamsd7542c82024-08-16 15:20:28 -0400917 auto method =
918 bus.new_method_call(service.c_str(), path.c_str(),
919 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain1be43372021-12-09 19:29:37 -0600920 method.append(PMODE_DEFAULT_INTERFACE);
921 auto reply = bus.call(method);
922 reply.read(ipsProperties);
923 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500924 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600925 {
926 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600927 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600928 "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>(
Patrick Williams48002492024-02-13 21:43:32 -0600992 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600993 "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;
Chris Cain36f9cde2021-11-22 11:18:21 -06001000}
1001
Chris Caincde7bea2022-01-28 15:54:24 -06001002/* Read default IPS parameters, save them to the persistent file and update
1003 DBus. Return true if successful */
1004bool 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>(
Patrick Williams48002492024-02-13 21:43:32 -06001017 std::format(
Chris Caincde7bea2022-01-28 15:54:24 -06001018 "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
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001030// Starts to watch for IPS active state changes.
1031bool 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 {
Patrick Williamsd7542c82024-08-16 15:20:28 -04001038 log<level::ERR>(
1039 std::format("openIpsFile Error({})={} : File={}", open_errno,
1040 strerror(open_errno), ipsStatusFile.c_str())
1041 .c_str());
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001042
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.
1064void 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.
1080void 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
1098void 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 {
Patrick Williams48002492024-02-13 21:43:32 -06001106 log<level::ERR>(std::format("sd_event_add_io: Error({})={} : File={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001107 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.
1129int 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.
1138void 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>(
Patrick Williams48002492024-02-13 21:43:32 -06001167 std::format("IPS state Read Error({})={} : File={} : len={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001168 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.
Patrick Williamsd7542c82024-08-16 15:20:28 -04001203 log<level::ERR>(
1204 std::format("Retry on File seek Error({})={} : File={}", open_errno,
1205 strerror(open_errno), ipsStatusFile.c_str())
1206 .c_str());
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001207
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}
Ben Tyner3576d652022-05-22 18:05:53 -05001214
1215// overrides read/write to powerMode dbus property.
Chris Cain30040d92024-06-19 16:50:34 -05001216Mode::PowerMode PowerMode::powerMode(Mode::PowerMode requestedMode)
Ben Tyner3576d652022-05-22 18:05:53 -05001217{
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"));
Ben Tyner3576d652022-05-22 18:05:53 -05001223 }
1224 else
1225 {
Chris Cain30040d92024-06-19 16:50:34 -05001226 // Verify requested mode is allowed
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001227
Chris Cain30040d92024-06-19 16:50:34 -05001228 // 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 */
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001282void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1283{
1284 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -06001285 std::format("PowerMode:updateDbusSafeMode: Update dbus state ({})",
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001286 safeModeReq)
1287 .c_str());
1288
1289 // Note; this function checks and only updates if different.
1290 Mode::safeMode(safeModeReq);
1291}
1292
Chris Cain30040d92024-06-19 16:50:34 -05001293// Get the supported power modes from DBus and return true if success
1294bool 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);
Patrick Williamsd7542c82024-08-16 15:20:28 -04001308 auto method =
1309 bus.new_method_call(service.c_str(), path.c_str(),
1310 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain30040d92024-06-19 16:50:34 -05001311 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
Patrick Williamsd7542c82024-08-16 15:20:28 -04001343 const std::string fullModeString =
1344 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain30040d92024-06-19 16:50:34 -05001345 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
Patrick Williamsd7542c82024-08-16 15:20:28 -04001388 const std::string fullModeString =
1389 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain30040d92024-06-19 16:50:34 -05001390 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
1418bool PowerMode::isValidMode(const SysPwrMode mode)
1419{
1420 if (customerModeList.contains(mode) || oemModeList.contains(mode))
1421 {
1422 return true;
1423 }
1424 return false;
1425}
1426
Chris Cain78e86012021-03-04 16:15:31 -06001427} // namespace powermode
1428
1429} // namespace occ
1430
1431} // namespace open_power