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