1 #pragma once
2 
3 #include "config.h"
4 
5 #ifdef POWER10
6 #include "occ_command.hpp"
7 
8 #include <cereal/archives/json.hpp>
9 #include <cereal/cereal.hpp>
10 #include <cereal/types/string.hpp>
11 #include <cereal/types/tuple.hpp>
12 #include <cereal/types/vector.hpp>
13 #include <sdbusplus/bus.hpp>
14 #include <sdbusplus/bus/match.hpp>
15 #include <xyz/openbmc_project/Control/Power/IdlePowerSaver/server.hpp>
16 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
17 
18 #include <filesystem>
19 
20 namespace open_power
21 {
22 namespace occ
23 {
24 
25 class Manager;
26 
27 namespace powermode
28 {
29 namespace Base = sdbusplus::xyz::openbmc_project::Control::Power::server;
30 using ModeInterface = sdbusplus::server::object_t<Base::Mode>;
31 using IpsInterface = sdbusplus::server::object_t<Base::IdlePowerSaver>;
32 using namespace std::literals::string_literals;
33 
34 constexpr auto PMODE_PATH = "/xyz/openbmc_project/control/host0/power_mode";
35 constexpr auto PMODE_INTERFACE = "xyz.openbmc_project.Control.Power.Mode";
36 constexpr auto POWER_MODE_PROP = "PowerMode";
37 constexpr auto POWER_SAFE_MODE_PROP = "SafeMode";
38 
39 constexpr auto PIPS_PATH = "/xyz/openbmc_project/control/host0/power_ips";
40 constexpr auto PIPS_INTERFACE =
41     "xyz.openbmc_project.Control.Power.IdlePowerSaver";
42 constexpr auto IPS_ACTIVE_PROP = "Active";
43 constexpr auto IPS_ENABLED_PROP = "Enabled";
44 constexpr auto IPS_ENTER_UTIL = "EnterUtilizationPercent";
45 constexpr auto IPS_ENTER_TIME = "EnterDwellTime";
46 constexpr auto IPS_EXIT_UTIL = "ExitUtilizationPercent";
47 constexpr auto IPS_EXIT_TIME = "ExitDwellTime";
48 
49 const auto PMODE_DEFAULT_INTERFACE =
50     "xyz.openbmc_project.Configuration.PowerModeProperties"s;
51 
52 /** @brief Query the current Hypervisor target
53  * @return true if the current Hypervisor target is PowerVM
54  */
55 bool isPowerVM();
56 
57 /** @brief Convert power mode string to OCC SysPwrMode value
58  *
59  * @param[in] i_modeString - power mode string
60  *
61  * @return  SysPwrMode or SysPwrMode::NO_CHANGE if not found
62  */
63 SysPwrMode convertStringToMode(const std::string& i_modeString);
64 
65 struct PowerModeData
66 {
67     bool modeInitialized = false;
68     SysPwrMode mode = SysPwrMode::NO_CHANGE;
69     uint16_t oemModeData = 0x0000;
70     bool ipsInitialized = false;
71     bool ipsEnabled = true;
72     uint8_t ipsEnterUtil = 0;
73     uint16_t ipsEnterTime = 0;
74     uint8_t ipsExitUtil = 0;
75     uint16_t ipsExitTime = 0;
76     bool modeLocked = false;
77 
78     /** @brief Function specifying data to archive for cereal.
79      */
80     template <class Archive>
81     void serialize(Archive& archive)
82     {
83         archive(modeInitialized, mode, oemModeData, ipsInitialized, ipsEnabled,
84                 ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime,
85                 modeLocked);
86     }
87 };
88 
89 /** @class OccPersistData
90  *  @brief Provides persistent container to store data for OCC
91  *
92  * Data is stored via cereal
93  */
94 class OccPersistData
95 {
96   public:
97     ~OccPersistData() = default;
98     OccPersistData(const OccPersistData&) = default;
99     OccPersistData& operator=(const OccPersistData&) = default;
100     OccPersistData(OccPersistData&&) = default;
101     OccPersistData& operator=(OccPersistData&&) = default;
102 
103     /** @brief Loads any saved power mode data */
104     OccPersistData()
105     {
106         load();
107     }
108 
109     /** @brief Save Power Mode data to persistent file
110      *
111      *  @param[in] newMode - desired System Power Mode
112      *  @param[in] oemModeData - data required by some OEM Power Modes
113      */
114     void updateMode(const SysPwrMode newMode, const uint16_t oemModeData)
115     {
116         modeData.mode = newMode;
117         modeData.oemModeData = oemModeData;
118         modeData.modeInitialized = true;
119         save();
120     }
121 
122     /** @brief Save Power Mode Lock value to persistent file
123      *
124      *  @param[in] modeLock - desired System Power Mode Lock
125      */
126     void updateModeLock(const bool modeLock)
127     {
128         modeData.modeLocked = modeLock;
129         save();
130     }
131     /** @brief Write Idle Power Saver parameters to persistent file
132      *
133      *  @param[in] enabled - Idle Power Save status (true = enabled)
134      *  @param[in] enterUtil - IPS Enter Utilization (%)
135      *  @param[in] enterTime - IPS Enter Time (seconds)
136      *  @param[in] exitUtil - IPS Exit Utilization (%)
137      *  @param[in] exitTime - IPS Exit Time (seconds)
138      */
139     void updateIPS(const bool enabled, const uint8_t enterUtil,
140                    const uint16_t enterTime, const uint8_t exitUtil,
141                    const uint16_t exitTime)
142     {
143         modeData.ipsEnabled = enabled;
144         modeData.ipsEnterUtil = enterUtil;
145         modeData.ipsEnterTime = enterTime;
146         modeData.ipsExitUtil = exitUtil;
147         modeData.ipsExitTime = exitTime;
148         modeData.ipsInitialized = true;
149         save();
150     }
151 
152     /** @brief Return the Power Mode and mode data
153      *
154      *  @param[out] mode - current system power mode
155      *  @param[out] oemModeData - frequency data for some OEM mode
156      *
157      *  @returns true if mode was available
158      */
159     bool getMode(SysPwrMode& mode, uint16_t& oemModeData) const
160     {
161         if (!modeData.modeInitialized)
162         {
163             return false;
164         }
165 
166         mode = modeData.mode;
167         oemModeData = modeData.oemModeData;
168         return true;
169     }
170 
171     /** @brief Get the Idle Power Saver properties from DBus
172      *
173      *  @param[out] enabled - Idle Power Save status (true = enabled)
174      *  @param[out] enterUtil - IPS Enter Utilization (%)
175      *  @param[out] enterTime - IPS Enter Time (seconds)
176      *  @param[out] exitUtil - IPS Exit Utilization (%)
177      *  @param[out] exitTime - IPS Exit Time (seconds)
178      *
179      * @return true if parameters were read successfully
180      */
181     bool getIPS(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
182                 uint8_t& exitUtil, uint16_t& exitTime)
183     {
184         if (!modeData.ipsInitialized)
185         {
186             return false;
187         }
188 
189         enabled = modeData.ipsEnabled;
190         enterUtil = modeData.ipsEnterUtil;
191         enterTime = modeData.ipsEnterTime;
192         exitUtil = modeData.ipsExitUtil;
193         exitTime = modeData.ipsExitTime;
194         return true;
195     }
196 
197     /** @brief Return persisted mode lock */
198     bool getModeLock()
199     {
200         return modeData.modeLocked;
201     }
202 
203     /** @brief Return true if the power mode is available */
204     bool modeAvailable()
205     {
206         return (modeData.modeInitialized);
207     }
208 
209     /** @brief Return true if the IPS data is available */
210     bool ipsAvailable()
211     {
212         return (modeData.ipsInitialized);
213     }
214 
215     /** @brief Saves the Power Mode data in the filesystem using cereal. */
216     void save();
217 
218     /** @brief Trace the Power Mode and IPS parameters. */
219     void print();
220 
221   private:
222     /** @brief Power Mode data filename to store persistent data */
223     static constexpr auto powerModeFilename = "powerModeData";
224 
225     /** @brief Power Mode data object to be persisted */
226     PowerModeData modeData;
227 
228     /** @brief Loads the OEM mode data in the filesystem using cereal. */
229     void load();
230 };
231 
232 /** @class PowerMode
233  *  @brief Monitors for changes to the power mode and notifies occ
234  *
235  *  The customer power mode is provided to the OCC by host TMGT when the occ
236  *  first goes active or is reset.  This code is responsible for sending
237  *  the power mode to the OCC if the mode is changed while the occ is active.
238  */
239 
240 class PowerMode : public ModeInterface, public IpsInterface
241 {
242   public:
243     /** @brief PowerMode object to inform occ of changes to mode
244      *
245      * This object will monitor for changes to the power mode setting.
246      * If a change is detected, and the occ is active, then this object will
247      * notify the OCC of the change.
248      *
249      * @param[in] managerRef - manager object reference
250      * @param[in] modePath - Power Mode dbus path
251      * @param[in] ipsPath - Idle Power Saver dbus path
252      */
253     explicit PowerMode(const Manager& managerRef, const char* modePath,
254                        const char* ipsPath
255 #ifdef POWER10
256                        ,
257                        EventPtr& event
258 #endif
259                        ) :
260         ModeInterface(utils::getBus(), modePath,
261                       ModeInterface::action::emit_no_signals),
262         IpsInterface(utils::getBus(), ipsPath,
263                      IpsInterface::action::emit_no_signals),
264         manager(managerRef),
265         pmodeMatch(utils::getBus(),
266                    sdbusplus::bus::match::rules::propertiesChanged(
267                        PMODE_PATH, PMODE_INTERFACE),
268                    [this](auto& msg) { this->modeChanged(msg); }),
269         ipsMatch(utils::getBus(),
270                  sdbusplus::bus::match::rules::propertiesChanged(
271                      PIPS_PATH, PIPS_INTERFACE),
272                  [this](auto& msg) { this->ipsChanged(msg); }),
273         defaultsUpdateMatch(
274             utils::getBus(),
275             sdbusplus::bus::match::rules::propertiesChangedNamespace(
276                 "/xyz/openbmc_project/inventory", PMODE_DEFAULT_INTERFACE),
277             [this](auto& msg) { this->defaultsReady(msg); }),
278         masterOccSet(false), masterActive(false)
279 #ifdef POWER10
280         ,
281         event(event)
282 #endif
283     {
284         using Mode =
285             sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
286         ModeInterface::allowedPowerModes({Mode::PowerMode::Static,
287                                           Mode::PowerMode::MaximumPerformance,
288                                           Mode::PowerMode::PowerSaving});
289 
290         // restore Power Mode to DBus
291         SysPwrMode currentMode;
292         uint16_t oemModeData = 0;
293         if (getMode(currentMode, oemModeData))
294         {
295             updateDbusMode(currentMode);
296         }
297         // restore Idle Power Saver parameters to DBus
298         uint8_t enterUtil, exitUtil;
299         uint16_t enterTime, exitTime;
300         bool ipsEnabled;
301         if (getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime))
302         {
303             updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
304         }
305     };
306 
307     /** @brief Initialize the persistent data with default values
308      *
309      * @return true if initialization completed
310      */
311     bool initPersistentData();
312 
313     /** @brief Set the power mode lock (dbus method)
314      *
315      * @return true if successful
316      */
317     bool powerModeLock();
318 
319     /** @brief Get the power mode lock status (dbus method)
320      *
321      * @return true if locked
322      */
323     bool powerModeLockStatus();
324 
325     /** @brief Set the current power mode property
326      *
327      * @param[in] newMode     - desired system power mode
328      * @param[in] oemModeData - data required by some OEM Power Modes
329      *
330      * @return true if mode accepted
331      */
332     bool setMode(const SysPwrMode newMode, const uint16_t oemModeData);
333 
334     /** @brief Send mode change command to the master OCC
335      *  @return SUCCESS on success
336      */
337     CmdStatus sendModeChange();
338 
339     /** @brief Send Idle Power Saver config data to the master OCC
340      *  @return SUCCESS on success
341      */
342     CmdStatus sendIpsData();
343 
344     /** @brief Set the master OCC path
345      *
346      * @param[in]  occPath - hwmon path for master OCC
347      */
348     void setMasterOcc(const std::string& occPath);
349 
350     /** @brief Notify object of master OCC state.  If not acitve, no
351      * commands will be sent to the master OCC
352      *
353      * @param[in]  isActive - true when master OCC is active
354      */
355     void setMasterActive(const bool isActive = true)
356     {
357         masterActive = isActive;
358     };
359 
360 #ifdef POWER10
361     /** @brief Starts to monitor for IPS active state change conditions
362      *
363      *  @param[in] poll - Indicates whether or not the IPS state file should
364      *                    actually be read for changes.
365      */
366     void addIpsWatch(bool poll = true);
367 
368     /** @brief Removes IPS active watch */
369     void removeIpsWatch();
370 #endif
371 
372     /** @brief Set dbus property to SAFE Mode(true) or clear SAFE Mode(false)*/
373     void updateDbusSafeMode(const bool safeMode);
374 
375     /** @brief override the set/get MODE function
376      *
377      *  @param[in] value - Intended value
378      *
379      *  @return          - the value or Updated value of the property
380      */
381     Base::Mode::PowerMode powerMode(Base::Mode::PowerMode value) override;
382 
383   private:
384     /** @brief OCC manager object */
385     const Manager& manager;
386 
387     /** @brief Pass-through occ path on the bus */
388     std::string path;
389 
390     /** @brief OCC instance number */
391     int occInstance;
392 
393     /** @brief Object to send commands to the OCC */
394     std::unique_ptr<open_power::occ::OccCommand> occCmd;
395 
396     /** @brief Used to subscribe to dbus pmode property changes **/
397     sdbusplus::bus::match_t pmodeMatch;
398 
399     /** @brief Used to subscribe to dbus IPS property changes **/
400     sdbusplus::bus::match_t ipsMatch;
401 
402     /** @brief Used to subscribe to dbus defaults property changes **/
403     sdbusplus::bus::match_t defaultsUpdateMatch;
404 
405     OccPersistData persistedData;
406 
407     /** @brief True when the master OCC has been established */
408     bool masterOccSet;
409 
410     /** @brief True when the master OCC is active */
411     bool masterActive;
412 
413 #ifdef POWER10
414     /** @brief IPS status data filename to read */
415     const fs::path ipsStatusFile = std::filesystem::path{OCC_HWMON_PATH} /
416                                    std::filesystem::path{OCC_MASTER_NAME} /
417                                    "occ_ips_status";
418 
419     /** @brief Current state of error watching */
420     bool watching = false;
421 
422     /** @brief register for the callback from the POLL IPS changed event */
423     void registerIpsStatusCallBack();
424 #endif
425 
426     /** @brief Callback for pmode setting changes
427      *
428      * Process change and inform OCC
429      *
430      * @param[in]  msg       - Data associated with pmode change signal
431      *
432      */
433     void modeChanged(sdbusplus::message_t& msg);
434 
435     /** @brief Get the current power mode property
436      *
437      * @param[out] currentMode - current system power mode
438      * @param[out] oemModeData - frequency data for some OEM mode
439      *
440      * @return true if data read successfully
441      */
442     bool getMode(SysPwrMode& currentMode, uint16_t& oemModeData);
443 
444     /** @brief Update the power mode property on DBus
445      *
446      * @param[in]  newMode - desired power mode
447      *
448      * @return true on success
449      */
450     bool updateDbusMode(const SysPwrMode newMode);
451 
452     /** @brief Callback for IPS setting changes
453      *
454      * Process change and inform OCC
455      *
456      * @param[in]  msg - Data associated with IPS change signal
457      *
458      */
459     void ipsChanged(sdbusplus::message_t& msg);
460 
461     /** @brief Get the Idle Power Saver properties
462      *
463      *  @param[out] enabled - Idle Power Save status (true = enabled)
464      *  @param[out] enterUtil - IPS Enter Utilization (%)
465      *  @param[out] enterTime - IPS Enter Time (seconds)
466      *  @param[out] exitUtil - IPS Exit Utilization (%)
467      *  @param[out] exitTime - IPS Exit Time (seconds)
468      *
469      * @return true if data read successfully
470      */
471     bool getIPSParms(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
472                      uint8_t& exitUtil, uint16_t& exitTime);
473 
474     /** Update the Idle Power Saver data on DBus
475      *
476      *  @param[in] enabled - Idle Power Save status (true = enabled)
477      *  @param[in] enterUtil - IPS Enter Utilization (%)
478      *  @param[in] enterTime - IPS Enter Time (seconds)
479      *  @param[in] exitUtil - IPS Exit Utilization (%)
480      *  @param[in] exitTime - IPS Exit Time (seconds)
481      *
482      *  @return true if parameters were set successfully
483      */
484     bool updateDbusIPS(const bool enabled, const uint8_t enterUtil,
485                        const uint16_t enterTime, const uint8_t exitUtil,
486                        const uint16_t exitTime);
487 
488     /** @brief Callback for entity manager default changes
489      *
490      * Called when PowerModeProperties defaults are available
491      */
492     void defaultsReady(sdbusplus::message_t& msg);
493 
494     /** @brief Get the default power mode property for this system type
495      *
496      * @param[out] defaultMode - default system power mode
497      *
498      * @return true if data read successfully
499      */
500     bool getDefaultMode(SysPwrMode& defaultMode);
501 
502     /** @brief Get the default Idle Power Saver properties for this system type
503      *
504      *  @param[out] enabled - Idle Power Save status (true = enabled)
505      *  @param[out] enterUtil - IPS Enter Utilization (%)
506      *  @param[out] enterTime - IPS Enter Time (seconds)
507      *  @param[out] exitUtil - IPS Exit Utilization (%)
508      *  @param[out] exitTime - IPS Exit Time (seconds)
509      *
510      *  @return true if parameters were read successfully
511      */
512     bool getDefaultIPSParms(bool& enabled, uint8_t& enterUtil,
513                             uint16_t& enterTime, uint8_t& exitUtil,
514                             uint16_t& exitTime);
515 
516     /** @brief Read the default Idle Power Saver parameters and save them to the
517      * DBUS so they will get used
518      *
519      * @return true if restore was successful
520      */
521     bool useDefaultIPSParms();
522 
523 #ifdef POWER10
524     /** @brief callback for the POLL IPS changed event
525      *
526      *  @param[in] es       - Populated event source
527      *  @param[in] fd       - Associated File descriptor
528      *  @param[in] revents  - Type of event
529      *  @param[in] userData - User data that was passed during registration
530      */
531     static int ipsStatusCallBack(sd_event_source* es, int fd, uint32_t revents,
532                                  void* userData);
533 
534     /** @brief Opens the IPS file and populates fd */
535     bool openIpsFile();
536 
537     /** @brief sd_event wrapped in unique_ptr */
538     EventPtr& event;
539 
540     /** @brief event source wrapped in unique_ptr */
541     EventSourcePtr eventSource;
542 
543     /** @brief When the ips status event is received, analyzes it */
544     virtual void analyzeIpsEvent();
545 
546   protected:
547     /** @brief File descriptor to watch for errors */
548     int fd = -1;
549 #endif
550 };
551 
552 } // namespace powermode
553 
554 } // namespace occ
555 
556 } // namespace open_power
557 #endif
558