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