1 #include "powermode.hpp"
2
3 #include <fcntl.h>
4 #include <sys/ioctl.h>
5
6 #include <format>
7
8 #ifdef POWERVM_CHECK
9 #include <com/ibm/Host/Target/server.hpp>
10 #endif
11 #include <org/open_power/OCC/Device/error.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/log.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
16
17 #include <cassert>
18 #include <fstream>
19 #include <regex>
20
21 namespace open_power
22 {
23 namespace occ
24 {
25 namespace powermode
26 {
27
28 using namespace phosphor::logging;
29 using namespace std::literals::string_literals;
30 using namespace sdbusplus::org::open_power::OCC::Device::Error;
31
32 using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
33
34 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
35 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
36
37 // Set the Master OCC
setMasterOcc(const std::string & masterOccPath)38 void PowerMode::setMasterOcc(const std::string& masterOccPath)
39 {
40 if (masterOccSet)
41 {
42 if (masterOccPath != path)
43 {
44 log<level::ERR>(
45 std::format(
46 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
47 occInstance, masterOccPath)
48 .c_str());
49 if (occCmd)
50 {
51 occCmd.reset();
52 }
53 }
54 }
55 path = masterOccPath;
56 occInstance = path.back() - '0';
57 log<level::DEBUG>(std::format("PowerMode::setMasterOcc(OCC{}, {})",
58 occInstance, path.c_str())
59 .c_str());
60 if (!occCmd)
61 {
62 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
63 path.c_str());
64 }
65 masterOccSet = true;
66 };
67
68 // Called when DBus power mode gets changed
modeChanged(sdbusplus::message_t & msg)69 void PowerMode::modeChanged(sdbusplus::message_t& msg)
70 {
71 std::map<std::string, std::variant<std::string>> properties{};
72 std::string interface;
73 std::string propVal;
74 msg.read(interface, properties);
75 const auto modeEntry = properties.find(POWER_MODE_PROP);
76 if (modeEntry != properties.end())
77 {
78 auto modeEntryValue = modeEntry->second;
79 propVal = std::get<std::string>(modeEntryValue);
80 SysPwrMode newMode = convertStringToMode(propVal);
81
82 // If mode set was requested via direct dbus property change then we
83 // need to see if mode set is locked and ignore the request. We should
84 // not get to this path since the property change method should also be
85 // checking the mode lock status.
86 if (persistedData.getModeLock())
87 {
88 // Fix up the mode property since we are going to ignore this
89 // set mode request.
90 log<level::ERR>(
91 "PowerMode::modeChanged: mode property changed while locked");
92 SysPwrMode currentMode;
93 uint16_t oemModeData;
94 getMode(currentMode, oemModeData);
95 updateDbusMode(currentMode);
96 return;
97 }
98
99 if (newMode != SysPwrMode::NO_CHANGE)
100 {
101 // Update persisted data with new mode
102 persistedData.updateMode(newMode, 0);
103
104 log<level::INFO>(
105 std::format("DBus Power Mode Changed: {}", propVal).c_str());
106
107 // Send mode change to OCC
108 sendModeChange();
109 }
110 }
111 }
112
113 // Set the state of power mode lock. Writing persistent data via dbus method.
powerModeLock()114 bool PowerMode::powerModeLock()
115 {
116 log<level::INFO>("PowerMode::powerModeLock: locking mode change");
117 persistedData.updateModeLock(true); // write persistent data
118 return true;
119 }
120
121 // Get the state of power mode. Reading persistent data via dbus method.
powerModeLockStatus()122 bool PowerMode::powerModeLockStatus()
123 {
124 bool status = persistedData.getModeLock(); // read persistent data
125 log<level::INFO>(std::format("PowerMode::powerModeLockStatus: {}",
126 status ? "locked" : "unlocked")
127 .c_str());
128 return status;
129 }
130
131 // Called from OCC PassThrough interface (via CE login / BMC command line)
setMode(const SysPwrMode newMode,const uint16_t oemModeData)132 bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
133 {
134 if (persistedData.getModeLock())
135 {
136 log<level::INFO>("PowerMode::setMode: mode change blocked");
137 return false;
138 }
139
140 if (updateDbusMode(newMode) == false)
141 {
142 // Unsupported mode
143 return false;
144 }
145
146 // Save mode
147 persistedData.updateMode(newMode, oemModeData);
148
149 // Send mode change to OCC
150 if (sendModeChange() != CmdStatus::SUCCESS)
151 {
152 // Mode change failed
153 return false;
154 }
155
156 return true;
157 }
158
159 // Convert PowerMode string to OCC SysPwrMode
160 // Returns NO_CHANGE if OEM or unsupported mode
convertStringToMode(const std::string & i_modeString)161 SysPwrMode convertStringToMode(const std::string& i_modeString)
162 {
163 SysPwrMode pmode = SysPwrMode::NO_CHANGE;
164
165 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString);
166 if (mode == Mode::PowerMode::MaximumPerformance)
167 {
168 pmode = SysPwrMode::MAX_PERF;
169 }
170 else if (mode == Mode::PowerMode::PowerSaving)
171 {
172 pmode = SysPwrMode::POWER_SAVING;
173 }
174 else if (mode == Mode::PowerMode::Static)
175 {
176 pmode = SysPwrMode::STATIC;
177 }
178 else if (mode == Mode::PowerMode::EfficiencyFavorPower)
179 {
180 pmode = SysPwrMode::EFF_FAVOR_POWER;
181 }
182 else if (mode == Mode::PowerMode::EfficiencyFavorPerformance)
183 {
184 pmode = SysPwrMode::EFF_FAVOR_PERF;
185 }
186 else if (mode == Mode::PowerMode::BalancedPerformance)
187 {
188 pmode = SysPwrMode::BALANCED_PERF;
189 }
190 else
191 {
192 if (mode != Mode::PowerMode::OEM)
193 {
194 log<level::ERR>(
195 std::format(
196 "convertStringToMode: Invalid Power Mode specified: {}",
197 i_modeString)
198 .c_str());
199 }
200 }
201
202 return pmode;
203 }
204
205 // Check if Hypervisor target is PowerVM
isPowerVM()206 bool isPowerVM()
207 {
208 bool powerVmTarget = true;
209 #ifdef POWERVM_CHECK
210 namespace Hyper = sdbusplus::com::ibm::Host::server;
211 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
212 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
213 constexpr auto HYPE_PROP = "Target";
214
215 // This will throw exception on failure
216 auto& bus = utils::getBus();
217 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
218 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
219 "org.freedesktop.DBus.Properties", "Get");
220 method.append(HYPE_INTERFACE, HYPE_PROP);
221 auto reply = bus.call(method);
222
223 std::variant<std::string> hyperEntryValue;
224 reply.read(hyperEntryValue);
225 auto propVal = std::get<std::string>(hyperEntryValue);
226 if (Hyper::Target::convertHypervisorFromString(propVal) ==
227 Hyper::Target::Hypervisor::PowerVM)
228 {
229 powerVmTarget = true;
230 }
231
232 log<level::DEBUG>(
233 std::format("isPowerVM returning {}", powerVmTarget).c_str());
234 #endif
235
236 return powerVmTarget;
237 }
238
239 // Initialize persistent data and return true if successful
initPersistentData()240 bool PowerMode::initPersistentData()
241 {
242 if (!persistedData.modeAvailable())
243 {
244 // Read the default mode
245 SysPwrMode currentMode;
246 if (!getDefaultMode(currentMode))
247 {
248 // Unable to read defaults
249 return false;
250 }
251 log<level::INFO>(
252 std::format("PowerMode::initPersistentData: Using default mode: {}",
253 currentMode)
254 .c_str());
255
256 // Save default mode as current mode
257 persistedData.updateMode(currentMode, 0);
258
259 // Write default mode to DBus
260 updateDbusMode(currentMode);
261 }
262
263 if (!persistedData.ipsAvailable())
264 {
265 // Read the default IPS parameters, write persistent file and update
266 // DBus
267 return useDefaultIPSParms();
268 }
269 return true;
270 }
271
272 // Get the requested power mode and return true if successful
getMode(SysPwrMode & currentMode,uint16_t & oemModeData)273 bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
274 {
275 currentMode = SysPwrMode::NO_CHANGE;
276 oemModeData = 0;
277
278 if (!persistedData.getMode(currentMode, oemModeData))
279 {
280 // Persistent data not initialized, read defaults and update DBus
281 if (!initPersistentData())
282 {
283 // Unable to read defaults from entity manager yet
284 return false;
285 }
286 return persistedData.getMode(currentMode, oemModeData);
287 }
288
289 return true;
290 }
291
292 // Set the power mode on DBus
updateDbusMode(const SysPwrMode newMode)293 bool PowerMode::updateDbusMode(const SysPwrMode newMode)
294 {
295 if (!VALID_POWER_MODE_SETTING(newMode) &&
296 !VALID_OEM_POWER_MODE_SETTING(newMode))
297 {
298 log<level::ERR>(
299 std::format(
300 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
301 newMode)
302 .c_str());
303 return false;
304 }
305
306 // Convert mode for DBus
307 ModeInterface::PowerMode dBusMode;
308 switch (newMode)
309 {
310 case SysPwrMode::STATIC:
311 dBusMode = Mode::PowerMode::Static;
312 break;
313 case SysPwrMode::POWER_SAVING:
314 dBusMode = Mode::PowerMode::PowerSaving;
315 break;
316 case SysPwrMode::MAX_PERF:
317 dBusMode = Mode::PowerMode::MaximumPerformance;
318 break;
319 case SysPwrMode::EFF_FAVOR_POWER:
320 dBusMode = Mode::PowerMode::EfficiencyFavorPower;
321 break;
322 case SysPwrMode::EFF_FAVOR_PERF:
323 dBusMode = Mode::PowerMode::EfficiencyFavorPerformance;
324 break;
325 case SysPwrMode::BALANCED_PERF:
326 dBusMode = Mode::PowerMode::BalancedPerformance;
327 break;
328 default:
329 dBusMode = Mode::PowerMode::OEM;
330 }
331
332 // true = skip update signal
333 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
334
335 return true;
336 }
337
338 // Send mode change request to the master OCC
sendModeChange()339 CmdStatus PowerMode::sendModeChange()
340 {
341 CmdStatus status;
342
343 if (!masterActive || !masterOccSet)
344 {
345 // Nothing to do
346 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
347 return CmdStatus::SUCCESS;
348 }
349
350 if (!isPowerVM())
351 {
352 // Mode change is only supported on PowerVM systems
353 log<level::DEBUG>(
354 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
355 return CmdStatus::SUCCESS;
356 }
357
358 SysPwrMode newMode;
359 uint16_t oemModeData = 0;
360 getMode(newMode, oemModeData);
361
362 if (VALID_POWER_MODE_SETTING(newMode) ||
363 VALID_OEM_POWER_MODE_SETTING(newMode))
364 {
365 std::vector<std::uint8_t> cmd, rsp;
366 cmd.reserve(9);
367 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
368 cmd.push_back(0x00); // Data Length (2 bytes)
369 cmd.push_back(0x06);
370 cmd.push_back(0x30); // Data (Version)
371 cmd.push_back(uint8_t(OccState::NO_CHANGE));
372 cmd.push_back(uint8_t(newMode));
373 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
374 cmd.push_back(oemModeData & 0xFF); //
375 cmd.push_back(0x00); // reserved
376 log<level::INFO>(
377 std::format(
378 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
379 newMode, oemModeData, occInstance, cmd.size())
380 .c_str());
381 status = occCmd->send(cmd, rsp);
382 if (status == CmdStatus::SUCCESS)
383 {
384 if (rsp.size() == 5)
385 {
386 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
387 {
388 log<level::ERR>(
389 std::format(
390 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
391 rsp[2])
392 .c_str());
393 dump_hex(rsp);
394 status = CmdStatus::FAILURE;
395 }
396 }
397 else
398 {
399 log<level::ERR>(
400 "PowerMode::sendModeChange: INVALID SET MODE response");
401 dump_hex(rsp);
402 status = CmdStatus::FAILURE;
403 }
404 }
405 else
406 {
407 log<level::ERR>(
408 std::format(
409 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
410 status)
411 .c_str());
412 }
413 }
414 else
415 {
416 log<level::ERR>(
417 std::format(
418 "PowerMode::sendModeChange: Unable to set power mode to {}",
419 newMode)
420 .c_str());
421 status = CmdStatus::FAILURE;
422 }
423
424 return status;
425 }
426
ipsChanged(sdbusplus::message_t & msg)427 void PowerMode::ipsChanged(sdbusplus::message_t& msg)
428 {
429 bool parmsChanged = false;
430 std::string interface;
431 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
432 ipsProperties{};
433 msg.read(interface, ipsProperties);
434
435 // Read persisted values
436 bool ipsEnabled;
437 uint8_t enterUtil, exitUtil;
438 uint16_t enterTime, exitTime;
439 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
440
441 // Check for any changed data
442 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
443 if (ipsEntry != ipsProperties.end())
444 {
445 ipsEnabled = std::get<bool>(ipsEntry->second);
446 log<level::INFO>(
447 std::format("Idle Power Saver change: Enabled={}", ipsEnabled)
448 .c_str());
449 parmsChanged = true;
450 }
451 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
452 if (ipsEntry != ipsProperties.end())
453 {
454 enterUtil = std::get<uint8_t>(ipsEntry->second);
455 log<level::INFO>(
456 std::format("Idle Power Saver change: Enter Util={}%", enterUtil)
457 .c_str());
458 parmsChanged = true;
459 }
460 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
461 if (ipsEntry != ipsProperties.end())
462 {
463 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
464 enterTime =
465 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
466 log<level::INFO>(
467 std::format("Idle Power Saver change: Enter Time={}sec", enterTime)
468 .c_str());
469 parmsChanged = true;
470 }
471 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
472 if (ipsEntry != ipsProperties.end())
473 {
474 exitUtil = std::get<uint8_t>(ipsEntry->second);
475 log<level::INFO>(
476 std::format("Idle Power Saver change: Exit Util={}%", exitUtil)
477 .c_str());
478 parmsChanged = true;
479 }
480 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
481 if (ipsEntry != ipsProperties.end())
482 {
483 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
484 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
485 log<level::INFO>(
486 std::format("Idle Power Saver change: Exit Time={}sec", exitTime)
487 .c_str());
488 parmsChanged = true;
489 }
490
491 if (parmsChanged)
492 {
493 if (exitUtil == 0)
494 {
495 // Setting the exitUtil to 0 will force restoring the default IPS
496 // parmeters (0 is not valid exit utilization)
497 log<level::INFO>(
498 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
499 // Read the default IPS parameters, write persistent file and update
500 // DBus
501 useDefaultIPSParms();
502 }
503 else
504 {
505 // Update persistant data with new DBus values
506 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
507 exitTime);
508 }
509
510 // Trigger IPS data to get sent to the OCC
511 sendIpsData();
512 }
513
514 return;
515 }
516
517 /** @brief Get the Idle Power Saver properties from persisted data
518 * @return true if IPS parameters were read
519 */
getIPSParms(bool & ipsEnabled,uint8_t & enterUtil,uint16_t & enterTime,uint8_t & exitUtil,uint16_t & exitTime)520 bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
521 uint16_t& enterTime, uint8_t& exitUtil,
522 uint16_t& exitTime)
523 {
524 // Defaults:
525 ipsEnabled = true; // Enabled
526 enterUtil = 8; // Enter Utilization (8%)
527 enterTime = 240; // Enter Delay Time (240s)
528 exitUtil = 12; // Exit Utilization (12%)
529 exitTime = 10; // Exit Delay Time (10s)
530
531 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
532 exitTime))
533 {
534 // Persistent data not initialized, read defaults and update DBus
535 if (!initPersistentData())
536 {
537 // Unable to read defaults from entity manager yet
538 return false;
539 }
540
541 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
542 exitTime);
543 }
544
545 if (enterUtil > exitUtil)
546 {
547 log<level::ERR>(
548 std::format(
549 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
550 enterUtil, exitUtil)
551 .c_str());
552 enterUtil = exitUtil;
553 }
554
555 return true;
556 }
557
558 // Set the Idle Power Saver data on DBus
updateDbusIPS(const bool enabled,const uint8_t enterUtil,const uint16_t enterTime,const uint8_t exitUtil,const uint16_t exitTime)559 bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
560 const uint16_t enterTime, const uint8_t exitUtil,
561 const uint16_t exitTime)
562 {
563 // true = skip update signal
564 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
565 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
566 // Convert time from seconds to ms
567 uint64_t msTime = enterTime * 1000;
568 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
569 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
570 msTime = exitTime * 1000;
571 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
572
573 return true;
574 }
575
576 // Send Idle Power Saver config data to the master OCC
sendIpsData()577 CmdStatus PowerMode::sendIpsData()
578 {
579 if (!masterActive || !masterOccSet)
580 {
581 // Nothing to do
582 return CmdStatus::SUCCESS;
583 }
584
585 if (!isPowerVM())
586 {
587 // Idle Power Saver data is only supported on PowerVM systems
588 log<level::DEBUG>(
589 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
590 return CmdStatus::SUCCESS;
591 }
592
593 bool ipsEnabled;
594 uint8_t enterUtil, exitUtil;
595 uint16_t enterTime, exitTime;
596 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
597
598 log<level::INFO>(
599 std::format(
600 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
601 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
602 .c_str());
603
604 std::vector<std::uint8_t> cmd, rsp;
605 cmd.reserve(12);
606 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
607 cmd.push_back(0x00); // Data Length (2 bytes)
608 cmd.push_back(0x09); //
609 cmd.push_back(0x11); // Config Format: IPS Settings
610 cmd.push_back(0x00); // Version
611 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
612 cmd.push_back(enterTime >> 8); // Enter Delay Time
613 cmd.push_back(enterTime & 0xFF); //
614 cmd.push_back(enterUtil); // Enter Utilization
615 cmd.push_back(exitTime >> 8); // Exit Delay Time
616 cmd.push_back(exitTime & 0xFF); //
617 cmd.push_back(exitUtil); // Exit Utilization
618 log<level::INFO>(std::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
619 "command to OCC{} ({} bytes)",
620 occInstance, cmd.size())
621 .c_str());
622 CmdStatus status = occCmd->send(cmd, rsp);
623 if (status == CmdStatus::SUCCESS)
624 {
625 if (rsp.size() == 5)
626 {
627 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
628 {
629 log<level::ERR>(
630 std::format(
631 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
632 rsp[2])
633 .c_str());
634 dump_hex(rsp);
635 status = CmdStatus::FAILURE;
636 }
637 }
638 else
639 {
640 log<level::ERR>(
641 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
642 dump_hex(rsp);
643 status = CmdStatus::FAILURE;
644 }
645 }
646 else
647 {
648 log<level::ERR>(
649 std::format(
650 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
651 status)
652 .c_str());
653 }
654
655 return status;
656 }
657
658 // Print the current values
print()659 void OccPersistData::print()
660 {
661 if (modeData.modeInitialized)
662 {
663 log<level::INFO>(
664 std::format(
665 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})",
666 modeData.mode, modeData.oemModeData, modeData.oemModeData,
667 modeData.modeLocked)
668 .c_str());
669 }
670 if (modeData.ipsInitialized)
671 {
672 log<level::INFO>(
673 std::format(
674 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
675 modeData.ipsEnabled, modeData.ipsEnterUtil,
676 modeData.ipsEnterTime, modeData.ipsExitUtil,
677 modeData.ipsExitTime)
678 .c_str());
679 }
680 }
681
682 // Saves the OEM mode data in the filesystem using cereal.
save()683 void OccPersistData::save()
684 {
685 std::filesystem::path opath =
686 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
687
688 if (!std::filesystem::exists(opath.parent_path()))
689 {
690 std::filesystem::create_directory(opath.parent_path());
691 }
692
693 log<level::DEBUG>(
694 std::format(
695 "OccPersistData::save: Writing Power Mode persisted data to {}",
696 opath.c_str())
697 .c_str());
698 // print();
699
700 std::ofstream stream{opath.c_str()};
701 cereal::JSONOutputArchive oarchive{stream};
702
703 oarchive(modeData);
704 }
705
706 // Loads the OEM mode data in the filesystem using cereal.
load()707 void OccPersistData::load()
708 {
709 std::filesystem::path ipath =
710 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
711
712 if (!std::filesystem::exists(ipath))
713 {
714 modeData.modeInitialized = false;
715 modeData.ipsInitialized = false;
716 return;
717 }
718
719 log<level::DEBUG>(
720 std::format(
721 "OccPersistData::load: Reading Power Mode persisted data from {}",
722 ipath.c_str())
723 .c_str());
724 try
725 {
726 std::ifstream stream{ipath.c_str()};
727 cereal::JSONInputArchive iarchive(stream);
728 iarchive(modeData);
729 }
730 catch (const std::exception& e)
731 {
732 auto error = errno;
733 log<level::ERR>(
734 std::format("OccPersistData::load: failed to read {}, errno={}",
735 ipath.c_str(), error)
736 .c_str());
737 modeData.modeInitialized = false;
738 modeData.ipsInitialized = false;
739 }
740
741 // print();
742 }
743
744 // Called when PowerModeProperties defaults are available on DBus
defaultsReady(sdbusplus::message_t & msg)745 void PowerMode::defaultsReady(sdbusplus::message_t& msg)
746 {
747 std::map<std::string, std::variant<std::string>> properties{};
748 std::string interface;
749 msg.read(interface, properties);
750
751 // If persistent data exists, then don't need to read defaults
752 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
753 {
754 log<level::INFO>(
755 std::format(
756 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
757 persistedData.modeAvailable() ? 'y' : 'n',
758 persistedData.modeAvailable() ? 'y' : 'n')
759 .c_str());
760
761 // Read default power mode defaults and update DBus
762 initPersistentData();
763 }
764 }
765
766 // Get the default power mode from DBus and return true if success
getDefaultMode(SysPwrMode & defaultMode)767 bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
768 {
769 try
770 {
771 auto& bus = utils::getBus();
772 std::string path = "/";
773 std::string service =
774 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
775 auto method = bus.new_method_call(service.c_str(), path.c_str(),
776 "org.freedesktop.DBus.Properties",
777 "Get");
778 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
779 auto reply = bus.call(method);
780
781 std::variant<std::string> stateEntryValue;
782 reply.read(stateEntryValue);
783 auto propVal = std::get<std::string>(stateEntryValue);
784
785 const std::string fullModeString = PMODE_INTERFACE + ".PowerMode."s +
786 propVal;
787 defaultMode = powermode::convertStringToMode(fullModeString);
788 if (!VALID_POWER_MODE_SETTING(defaultMode))
789 {
790 log<level::ERR>(
791 std::format(
792 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
793 defaultMode)
794 .c_str());
795 // If default was read but not valid, use Max Performance
796 defaultMode = SysPwrMode::MAX_PERF;
797 return true;
798 }
799 }
800 catch (const sdbusplus::exception_t& e)
801 {
802 log<level::ERR>(
803 std::format("Unable to read Default Power Mode: {}", e.what())
804 .c_str());
805 return false;
806 }
807
808 return true;
809 }
810
811 /* Get the default Idle Power Saver properties and return true if successful */
getDefaultIPSParms(bool & ipsEnabled,uint8_t & enterUtil,uint16_t & enterTime,uint8_t & exitUtil,uint16_t & exitTime)812 bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
813 uint16_t& enterTime, uint8_t& exitUtil,
814 uint16_t& exitTime)
815 {
816 // Defaults:
817 ipsEnabled = true; // Enabled
818 enterUtil = 8; // Enter Utilization (8%)
819 enterTime = 240; // Enter Delay Time (240s)
820 exitUtil = 12; // Exit Utilization (12%)
821 exitTime = 10; // Exit Delay Time (10s)
822
823 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
824 ipsProperties{};
825
826 // Get all IPS properties from DBus
827 try
828 {
829 auto& bus = utils::getBus();
830 std::string path = "/";
831 std::string service =
832 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
833 auto method = bus.new_method_call(service.c_str(), path.c_str(),
834 "org.freedesktop.DBus.Properties",
835 "GetAll");
836 method.append(PMODE_DEFAULT_INTERFACE);
837 auto reply = bus.call(method);
838 reply.read(ipsProperties);
839 }
840 catch (const sdbusplus::exception_t& e)
841 {
842 log<level::ERR>(
843 std::format(
844 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
845 e.what())
846 .c_str());
847 return false;
848 }
849
850 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
851 if (ipsEntry != ipsProperties.end())
852 {
853 ipsEnabled = std::get<bool>(ipsEntry->second);
854 }
855 else
856 {
857 log<level::ERR>(
858 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
859 }
860
861 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
862 if (ipsEntry != ipsProperties.end())
863 {
864 enterUtil = std::get<uint64_t>(ipsEntry->second);
865 }
866 else
867 {
868 log<level::ERR>(
869 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
870 }
871
872 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
873 if (ipsEntry != ipsProperties.end())
874 {
875 enterTime = std::get<uint64_t>(ipsEntry->second);
876 }
877 else
878 {
879 log<level::ERR>(
880 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
881 }
882
883 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
884 if (ipsEntry != ipsProperties.end())
885 {
886 exitUtil = std::get<uint64_t>(ipsEntry->second);
887 }
888 else
889 {
890 log<level::ERR>(
891 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
892 }
893
894 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
895 if (ipsEntry != ipsProperties.end())
896 {
897 exitTime = std::get<uint64_t>(ipsEntry->second);
898 }
899 else
900 {
901 log<level::ERR>(
902 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
903 }
904
905 if (enterUtil > exitUtil)
906 {
907 log<level::ERR>(
908 std::format(
909 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
910 enterUtil, exitUtil)
911 .c_str());
912 enterUtil = exitUtil;
913 }
914
915 return true;
916 }
917
918 /* Read default IPS parameters, save them to the persistent file and update
919 DBus. Return true if successful */
useDefaultIPSParms()920 bool PowerMode::useDefaultIPSParms()
921 {
922 // Read the default IPS parameters
923 bool ipsEnabled;
924 uint8_t enterUtil, exitUtil;
925 uint16_t enterTime, exitTime;
926 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
927 exitTime))
928 {
929 // Unable to read defaults
930 return false;
931 }
932 log<level::INFO>(
933 std::format(
934 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
935 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
936 .c_str());
937
938 // Save IPS parms to the persistent file
939 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
940 exitTime);
941
942 // Write IPS parms to DBus
943 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
944 }
945
946 #ifdef POWER10
947
948 // Starts to watch for IPS active state changes.
openIpsFile()949 bool PowerMode::openIpsFile()
950 {
951 bool rc = true;
952 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
953 const int open_errno = errno;
954 if (fd < 0)
955 {
956 log<level::ERR>(std::format("openIpsFile Error({})={} : File={}",
957 open_errno, strerror(open_errno),
958 ipsStatusFile.c_str())
959 .c_str());
960
961 close(fd);
962
963 using namespace sdbusplus::org::open_power::OCC::Device::Error;
964 report<OpenFailure>(
965 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
966 CALLOUT_ERRNO(open_errno),
967 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
968 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
969
970 // We are no longer watching the error
971 active(false);
972
973 watching = false;
974 rc = false;
975 // NOTE: this will leave the system not reporting IPS active state to
976 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
977 }
978 return rc;
979 }
980
981 // Starts to watch for IPS active state changes.
addIpsWatch(bool poll)982 void PowerMode::addIpsWatch(bool poll)
983 {
984 // open file and register callback on file if we are not currently watching,
985 // and if poll=true, and if we are the master.
986 if ((!watching) && poll)
987 {
988 // Open the file
989 if (openIpsFile())
990 {
991 // register the callback handler which sets 'watching'
992 registerIpsStatusCallBack();
993 }
994 }
995 }
996
997 // Stops watching for IPS active state changes.
removeIpsWatch()998 void PowerMode::removeIpsWatch()
999 {
1000 // NOTE: we want to remove event, close file, and IPS active false no
1001 // matter what the 'watching' flags is set to.
1002
1003 // We are no longer watching the error
1004 active(false);
1005
1006 watching = false;
1007
1008 // Close file
1009 close(fd);
1010
1011 // clears sourcePtr in the event source.
1012 eventSource.reset();
1013 }
1014
1015 // Attaches the FD to event loop and registers the callback handler
registerIpsStatusCallBack()1016 void PowerMode::registerIpsStatusCallBack()
1017 {
1018 decltype(eventSource.get()) sourcePtr = nullptr;
1019
1020 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1021 ipsStatusCallBack, this);
1022 if (r < 0)
1023 {
1024 log<level::ERR>(std::format("sd_event_add_io: Error({})={} : File={}",
1025 r, strerror(-r), ipsStatusFile.c_str())
1026 .c_str());
1027
1028 using InternalFailure =
1029 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1030 report<InternalFailure>();
1031
1032 removeIpsWatch();
1033 // NOTE: this will leave the system not reporting IPS active state to
1034 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1035 }
1036 else
1037 {
1038 // puts sourcePtr in the event source.
1039 eventSource.reset(sourcePtr);
1040 // Set we are watching the error
1041 watching = true;
1042 }
1043 }
1044
1045 // Static function to redirect to non static analyze event function to be
1046 // able to read file and push onto dBus.
ipsStatusCallBack(sd_event_source *,int,uint32_t,void * userData)1047 int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1048 uint32_t /*revents*/, void* userData)
1049 {
1050 auto pmode = static_cast<PowerMode*>(userData);
1051 pmode->analyzeIpsEvent();
1052 return 0;
1053 }
1054
1055 // Function to Read SysFs file change on IPS state and push on dBus.
analyzeIpsEvent()1056 void PowerMode::analyzeIpsEvent()
1057 {
1058 // Need to seek to START, else the poll returns immediately telling
1059 // there is data to be read. if not done this floods the system.
1060 auto r = lseek(fd, 0, SEEK_SET);
1061 const int open_errno = errno;
1062 if (r < 0)
1063 {
1064 // NOTE: upon file access error we can not just re-open file, we have to
1065 // remove and add to watch.
1066 removeIpsWatch();
1067 addIpsWatch(true);
1068 }
1069
1070 // if we are 'watching' that is the file seek, or the re-open passed.. we
1071 // can read the data
1072 if (watching)
1073 {
1074 // This file gets created when polling OCCs. A value or length of 0 is
1075 // deemed success. That means we would disable IPS active on dbus.
1076 char data;
1077 bool ipsState = false;
1078 const auto len = read(fd, &data, sizeof(data));
1079 const int readErrno = errno;
1080 if (len <= 0)
1081 {
1082 removeIpsWatch();
1083
1084 log<level::ERR>(
1085 std::format("IPS state Read Error({})={} : File={} : len={}",
1086 readErrno, strerror(readErrno),
1087 ipsStatusFile.c_str(), len)
1088 .c_str());
1089
1090 report<ReadFailure>(
1091 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1092 CALLOUT_ERRNO(readErrno),
1093 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1094 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1095
1096 // NOTE: this will leave the system not reporting IPS active state
1097 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1098 // again.
1099 }
1100 else
1101 {
1102 // Data returned in ASCII.
1103 // convert to integer. atoi()
1104 // from OCC_P10_FW_Interfaces spec
1105 // Bit 6: IPS active 1 indicates enabled.
1106 // Bit 7: IPS enabled. 1 indicates enabled.
1107 // mask off bit 6 --> & 0x02
1108 // Shift left one bit and store as bool. >> 1
1109 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1110 }
1111
1112 // This will only set IPS active dbus if different than current.
1113 active(ipsState);
1114 }
1115 else
1116 {
1117 removeIpsWatch();
1118
1119 // If the Retry did not get to "watching = true" we already have an
1120 // error log, just post trace.
1121 log<level::ERR>(std::format("Retry on File seek Error({})={} : File={}",
1122 open_errno, strerror(open_errno),
1123 ipsStatusFile.c_str())
1124 .c_str());
1125
1126 // NOTE: this will leave the system not reporting IPS active state to
1127 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1128 }
1129
1130 return;
1131 }
1132
1133 // overrides read/write to powerMode dbus property.
powerMode(Mode::PowerMode value)1134 Mode::PowerMode PowerMode::powerMode(Mode::PowerMode value)
1135 {
1136 if (persistedData.getModeLock())
1137 {
1138 log<level::INFO>("PowerMode::powerMode: mode property change blocked");
1139 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1140 "mode change not allowed due to lock"));
1141 return value;
1142 }
1143 else
1144 {
1145 return Mode::powerMode(value);
1146 }
1147 }
1148 #endif
1149
1150 /* Set dbus property to SAFE mode(true) or clear(false) only if different */
updateDbusSafeMode(const bool safeModeReq)1151 void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1152 {
1153 log<level::DEBUG>(
1154 std::format("PowerMode:updateDbusSafeMode: Update dbus state ({})",
1155 safeModeReq)
1156 .c_str());
1157
1158 // Note; this function checks and only updates if different.
1159 Mode::safeMode(safeModeReq);
1160 }
1161
1162 } // namespace powermode
1163
1164 } // namespace occ
1165
1166 } // namespace open_power
1167