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