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